From fdd18f7a4b645b2df2f60004393965ff147ecd4b Mon Sep 17 00:00:00 2001 From: Louis Boudreau Date: Mon, 5 Dec 2022 09:18:43 -0500 Subject: [PATCH 1/5] Add CocoaPods source --- .github/workflows/test.yml | 21 + docs/sources/cocoapods.md | 15 + lib/licensed/sources.rb | 1 + lib/licensed/sources/cocoapods.rb | 75 + licensed.gemspec | 2 + script/source-setup/cocoapods | 17 + test/fixtures/cocoapods/Podfile | 19 + test/fixtures/cocoapods/Podfile.lock | 102 + .../fixtures/cocoapods/Pods/Alamofire/LICENSE | 19 + .../cocoapods/Pods/Alamofire/README.md | 206 + .../Pods/Alamofire/Source/AFError.swift | 854 ++++ .../Pods/Alamofire/Source/Alamofire.swift | 29 + .../Alamofire/Source/AlamofireExtended.swift | 61 + .../Source/AuthenticationInterceptor.swift | 404 ++ .../Source/CachedResponseHandler.swift | 91 + .../Pods/Alamofire/Source/Combine.swift | 622 +++ .../Source/DispatchQueue+Alamofire.swift | 37 + .../Pods/Alamofire/Source/EventMonitor.swift | 892 +++++ .../Pods/Alamofire/Source/HTTPHeaders.swift | 449 +++ .../Pods/Alamofire/Source/HTTPMethod.swift | 54 + .../Alamofire/Source/MultipartFormData.swift | 553 +++ .../Alamofire/Source/MultipartUpload.swift | 89 + .../Source/NetworkReachabilityManager.swift | 267 ++ .../Pods/Alamofire/Source/Notifications.swift | 115 + .../Source/OperationQueue+Alamofire.swift | 49 + .../Alamofire/Source/ParameterEncoder.swift | 184 + .../Alamofire/Source/ParameterEncoding.swift | 317 ++ .../Pods/Alamofire/Source/Protected.swift | 224 ++ .../Alamofire/Source/RedirectHandler.swift | 95 + .../Pods/Alamofire/Source/Request.swift | 1882 +++++++++ .../Alamofire/Source/RequestInterceptor.swift | 244 ++ .../Alamofire/Source/RequestTaskMap.swift | 149 + .../Pods/Alamofire/Source/Response.swift | 454 +++ .../Source/ResponseSerialization.swift | 1116 ++++++ .../Alamofire/Source/Result+Alamofire.swift | 120 + .../Pods/Alamofire/Source/RetryPolicy.swift | 370 ++ .../Source/ServerTrustEvaluation.swift | 619 +++ .../Pods/Alamofire/Source/Session.swift | 1258 ++++++ .../Alamofire/Source/SessionDelegate.swift | 330 ++ .../Source/StringEncoding+Alamofire.swift | 55 + ...URLConvertible+URLRequestConvertible.swift | 105 + .../Source/URLEncodedFormEncoder.swift | 976 +++++ .../Source/URLRequest+Alamofire.swift | 39 + .../URLSessionConfiguration+Alamofire.swift | 37 + .../Pods/Alamofire/Source/Validation.swift | 302 ++ .../Chat Items/BaseChatItemPresenter.swift | 123 + .../Source/Chat Items/ChatItemCompanion.swift | 52 + .../ChatItemProtocolDefinitions.swift | 84 + .../Chat Items/DummyChatItemPresenter.swift | 57 + ...ViewController+CellPanGestureHandler.swift | 36 + .../BaseChatViewController+Changes.swift | 373 ++ .../BaseChatViewController+Presenters.swift | 137 + .../BaseChatViewController+Scrolling.swift | 205 + .../BaseChatViewController.swift | 600 +++ .../ChatLayoutConfiguration.swift | 49 + .../BaseChatViewControllerView.swift | 45 + .../Collaborators/CellPanGestureHandler.swift | 200 + .../ChatCollectionViewLayout.swift | 170 + .../ChatDataSourceProtocol.swift | 49 + .../ChatItemPresenterFactory.swift | 54 + .../Collaborators/CollectionChanges.swift | 123 + .../Collaborators/KeyboardTracker.swift | 277 ++ .../ReplyFeedbackGenerator.swift | 38 + .../InputPositionControlling.swift | 39 + .../Source/ChatItemCompanionCollection.swift | 79 + .../Chatto/Source/SerialTaskQueue.swift | 80 + .../Pods/Chatto/Chatto/Source/Utils.swift | 69 + test/fixtures/cocoapods/Pods/Chatto/LICENSE | 22 + test/fixtures/cocoapods/Pods/Chatto/README.md | 92 + .../Pods/Local Podspecs/Chatto.podspec.json | 26 + .../Pods/MDFInternationalization/LICENSE | 202 + .../Pods/MDFInternationalization/README.md | 74 + .../Sources/MDFInternationalization.h | 29 + .../MDFInternationalization/Sources/MDFRTL.h | 105 + .../MDFInternationalization/Sources/MDFRTL.m | 73 + .../Sources/NSLocale+MaterialRTL.h | 36 + .../Sources/NSLocale+MaterialRTL.m | 38 + .../Sources/NSString+MaterialBidi.h | 84 + .../Sources/NSString+MaterialBidi.m | 165 + .../Sources/UIImage+MaterialRTL.h | 33 + .../Sources/UIImage+MaterialRTL.m | 168 + .../Sources/UIView+MaterialRTL.h | 91 + .../Sources/UIView+MaterialRTL.m | 167 + .../Pods/MDFTextAccessibility/LICENSE | 202 + .../Pods/MDFTextAccessibility/README.md | 101 + .../MDFTextAccessibility-Bridging-Header.h | 17 + .../src/MDFTextAccessibility.h | 199 + .../src/MDFTextAccessibility.m | 188 + .../src/private/MDFColorCalculations.h | 70 + .../src/private/MDFColorCalculations.m | 184 + .../src/private/MDFImageCalculations.h | 37 + .../src/private/MDFImageCalculations.m | 54 + .../src/private/NSArray+MDFUtils.h | 53 + .../src/private/NSArray+MDFUtils.m | 68 + test/fixtures/cocoapods/Pods/Manifest.lock | 102 + .../cocoapods/Pods/MaterialComponents/LICENSE | 202 + .../Pods/MaterialComponents/README.md | 71 + ...CAMediaTimingFunction+MDCAnimationTiming.h | 82 + ...CAMediaTimingFunction+MDCAnimationTiming.m | 37 + .../src/MaterialAnimationTiming.h | 16 + .../src/UIView+MDCTimingFunction.h | 37 + .../src/UIView+MDCTimingFunction.m | 35 + .../Availability/src/MDCAvailability.h | 39 + .../Availability/src/MaterialAvailability.h | 16 + .../components/Buttons/src/MDCButton.h | 453 +++ .../components/Buttons/src/MDCButton.m | 1452 +++++++ .../components/Buttons/src/MDCFlatButton.h | 49 + .../components/Buttons/src/MDCFlatButton.m | 47 + .../Buttons/src/MDCFloatingButton+Animation.h | 49 + .../Buttons/src/MDCFloatingButton+Animation.m | 285 ++ .../Buttons/src/MDCFloatingButton.h | 275 ++ .../Buttons/src/MDCFloatingButton.m | 573 +++ .../components/Buttons/src/MDCRaisedButton.h | 32 + .../components/Buttons/src/MDCRaisedButton.m | 28 + .../components/Buttons/src/MaterialButtons.h | 19 + .../src/private/MDCButton+Subclassing.h | 57 + .../private/MDCFloatingButtonModeAnimator.h | 49 + .../private/MDCFloatingButtonModeAnimator.m | 206 + .../MDCFloatingButtonModeAnimatorDelegate.h | 35 + .../components/Cards/src/MDCCard.h | 169 + .../components/Cards/src/MDCCard.m | 379 ++ .../Cards/src/MDCCardCollectionCell.h | 334 ++ .../Cards/src/MDCCardCollectionCell.m | 659 ++++ .../components/Cards/src/MaterialCards.h | 16 + ...llectionViewController+MDCCardReordering.h | 28 + ...llectionViewController+MDCCardReordering.m | 39 + .../components/Elevation/src/MDCElevatable.h | 42 + .../Elevation/src/MDCElevationOverriding.h | 34 + .../Elevation/src/MaterialElevation.h | 18 + .../Elevation/src/UIColor+MaterialElevation.h | 65 + .../Elevation/src/UIColor+MaterialElevation.m | 85 + .../src/UIView+MaterialElevationResponding.h | 53 + .../src/UIView+MaterialElevationResponding.m | 108 + .../Ink/src/MDCInkGestureRecognizer.h | 60 + .../Ink/src/MDCInkGestureRecognizer.m | 94 + .../Ink/src/MDCInkTouchController.h | 129 + .../Ink/src/MDCInkTouchController.m | 222 ++ .../Ink/src/MDCInkTouchControllerDelegate.h | 84 + .../components/Ink/src/MDCInkView.h | 180 + .../components/Ink/src/MDCInkView.m | 326 ++ .../components/Ink/src/MDCInkViewDelegate.h | 41 + .../components/Ink/src/MaterialInk.h | 19 + .../components/Ink/src/private/MDCInkLayer.h | 108 + .../components/Ink/src/private/MDCInkLayer.m | 246 ++ .../Ink/src/private/MDCInkLayerDelegate.h | 41 + .../src/private/MDCLegacyInkLayer+Private.h | 46 + .../Ink/src/private/MDCLegacyInkLayer.h | 112 + .../Ink/src/private/MDCLegacyInkLayer.m | 664 ++++ .../src/private/MDCLegacyInkLayerDelegate.h | 41 + .../private/MDCLegacyInkLayerRippleDelegate.h | 29 + .../Ripple/src/MDCRippleTouchController.h | 113 + .../Ripple/src/MDCRippleTouchController.m | 205 + .../src/MDCRippleTouchControllerDelegate.h | 87 + .../components/Ripple/src/MDCRippleView.h | 170 + .../components/Ripple/src/MDCRippleView.m | 349 ++ .../Ripple/src/MDCRippleViewDelegate.h | 55 + .../Ripple/src/MDCStatefulRippleView.h | 165 + .../Ripple/src/MDCStatefulRippleView.m | 273 ++ .../components/Ripple/src/MaterialRipple.h | 19 + .../Ripple/src/private/MDCRippleLayer.h | 96 + .../Ripple/src/private/MDCRippleLayer.m | 194 + .../src/private/MDCRippleLayerDelegate.h | 53 + .../components/Shadow/src/MDCShadow.h | 59 + .../components/Shadow/src/MDCShadow.m | 69 + .../Shadow/src/MDCShadowsCollection.h | 145 + .../Shadow/src/MDCShadowsCollection.m | 165 + .../components/Shadow/src/MaterialShadow.h | 17 + .../src/MDCShadowElevations.h | 106 + .../src/MaterialShadowElevations.h | 15 + .../src/MaterialShadowElevationsDummy.m | 17 + .../ShadowLayer/src/MDCShadowLayer.h | 106 + .../ShadowLayer/src/MDCShadowLayer.m | 471 +++ .../ShadowLayer/src/MaterialShadowLayer.h | 15 + .../MDCCornerTreatment+CornerTypeInitalizer.h | 80 + .../MDCCornerTreatment+CornerTypeInitalizer.m | 60 + .../src/MDCCurvedCornerTreatment.h | 40 + .../src/MDCCurvedCornerTreatment.m | 72 + .../src/MDCCurvedRectShapeGenerator.h | 35 + .../src/MDCCurvedRectShapeGenerator.m | 68 + .../ShapeLibrary/src/MDCCutCornerTreatment.h | 57 + .../ShapeLibrary/src/MDCCutCornerTreatment.m | 72 + .../ShapeLibrary/src/MDCPillShapeGenerator.h | 24 + .../ShapeLibrary/src/MDCPillShapeGenerator.m | 52 + .../src/MDCRoundedCornerTreatment.h | 38 + .../src/MDCRoundedCornerTreatment.m | 72 + .../src/MDCSlantedRectShapeGenerator.h | 31 + .../src/MDCSlantedRectShapeGenerator.m | 53 + .../src/MDCTriangleEdgeTreatment.h | 47 + .../src/MDCTriangleEdgeTreatment.m | 43 + .../ShapeLibrary/src/MaterialShapeLibrary.h | 22 + .../Shapes/src/MDCCornerTreatment.h | 73 + .../Shapes/src/MDCCornerTreatment.m | 56 + .../components/Shapes/src/MDCEdgeTreatment.h | 38 + .../components/Shapes/src/MDCEdgeTreatment.m | 35 + .../components/Shapes/src/MDCPathGenerator.h | 116 + .../components/Shapes/src/MDCPathGenerator.m | 190 + .../Shapes/src/MDCRectangleShapeGenerator.h | 64 + .../Shapes/src/MDCRectangleShapeGenerator.m | 251 ++ .../Shapes/src/MDCShapeGenerating.h | 30 + .../components/Shapes/src/MDCShapeMediator.h | 116 + .../components/Shapes/src/MDCShapeMediator.m | 197 + .../Shapes/src/MDCShapedShadowLayer.h | 81 + .../Shapes/src/MDCShapedShadowLayer.m | 224 ++ .../components/Shapes/src/MDCShapedView.h | 70 + .../components/Shapes/src/MDCShapedView.m | 97 + .../components/Shapes/src/MaterialShapes.h | 22 + .../components/Typography/src/MDCFontScaler.h | 80 + .../components/Typography/src/MDCFontScaler.m | 293 ++ .../Typography/src/MDCFontTextStyle.h | 38 + .../components/Typography/src/MDCTypography.h | 196 + .../components/Typography/src/MDCTypography.m | 324 ++ .../Typography/src/MaterialTypography.h | 30 + .../Typography/src/UIFont+MaterialScalable.h | 88 + .../Typography/src/UIFont+MaterialScalable.m | 86 + .../src/UIFont+MaterialSimpleEquality.h | 31 + .../src/UIFont+MaterialSimpleEquality.m | 28 + .../src/UIFont+MaterialTypography.h | 49 + .../src/UIFont+MaterialTypography.m | 80 + .../src/UIFontDescriptor+MaterialTypography.h | 41 + .../src/UIFontDescriptor+MaterialTypography.m | 83 + .../Typography/src/private/MDCFontTraits.h | 60 + .../Typography/src/private/MDCFontTraits.m | 511 +++ .../src/private/MDCTypographyUtilities.h | 17 + .../src/private/MDCTypographyUtilities.m | 25 + .../UIFont+MaterialTypographyPrivate.h | 42 + .../UIFont+MaterialTypographyPrivate.m | 113 + .../Application/src/MaterialApplication.h | 14 + .../src/UIApplication+MDCAppExtensions.h | 34 + .../src/UIApplication+MDCAppExtensions.m | 41 + .../private/Color/src/MaterialColor.h | 16 + .../Color/src/UIColor+MaterialBlending.h | 29 + .../Color/src/UIColor+MaterialBlending.m | 45 + .../Color/src/UIColor+MaterialDynamic.h | 56 + .../Color/src/UIColor+MaterialDynamic.m | 73 + .../src/MaterialIcons+ic_check_circle.h | 30 + .../src/MaterialIcons+ic_check_circle.m | 34 + .../Contents.json | 6 + .../ic_check_circle.imageset/Contents.json | 23 + .../ic_check_circle.png | Bin 0 -> 260 bytes .../ic_check_circle@2x.png | Bin 0 -> 493 bytes .../ic_check_circle@3x.png | Bin 0 -> 709 bytes .../private/Icons/src/MDCIcons+BundleLoader.h | 32 + .../components/private/Icons/src/MDCIcons.h | 29 + .../components/private/Icons/src/MDCIcons.m | 47 + .../private/Icons/src/MaterialIcons.h | 16 + .../components/private/Math/src/MDCMath.h | 256 ++ .../private/Math/src/MaterialMath.h | 15 + .../private/Math/src/MaterialMathDummy.m | 17 + .../Pods/Pods.xcodeproj/project.pbxproj | 3465 +++++++++++++++++ .../xcschemes/Alamofire.xcscheme | 58 + .../xcschemes/Chatto.xcscheme | 58 + .../MDFInternationalization.xcscheme | 58 + .../xcschemes/MDFTextAccessibility.xcscheme | 58 + ...nts-MaterialIcons_ic_check_circle.xcscheme | 58 + .../xcschemes/MaterialComponents.xcscheme | 58 + .../xcschemes/Pods-ios.xcscheme | 58 + .../xcschemes/Pods-iosTests.xcscheme | 58 + .../xcschemes/lottie-ios.xcscheme | 58 + .../xcschemes/xcschememanagement.plist | 56 + .../Alamofire/Alamofire-Info.plist | 26 + .../Alamofire/Alamofire-dummy.m | 5 + .../Alamofire/Alamofire-prefix.pch | 12 + .../Alamofire/Alamofire-umbrella.h | 16 + .../Alamofire/Alamofire.debug.xcconfig | 14 + .../Alamofire/Alamofire.modulemap | 6 + .../Alamofire/Alamofire.release.xcconfig | 14 + .../Chatto/Chatto-Info.plist | 26 + .../Chatto/Chatto-dummy.m | 5 + .../Chatto/Chatto-prefix.pch | 12 + .../Chatto/Chatto-umbrella.h | 16 + .../Chatto/Chatto.debug.xcconfig | 13 + .../Chatto/Chatto.modulemap | 6 + .../Chatto/Chatto.release.xcconfig | 13 + .../MDFInternationalization-Info.plist | 26 + .../MDFInternationalization-dummy.m | 5 + .../MDFInternationalization-prefix.pch | 12 + .../MDFInternationalization-umbrella.h | 22 + .../MDFInternationalization.debug.xcconfig | 11 + .../MDFInternationalization.modulemap | 6 + .../MDFInternationalization.release.xcconfig | 11 + .../MDFTextAccessibility-Info.plist | 26 + .../MDFTextAccessibility-dummy.m | 5 + .../MDFTextAccessibility-prefix.pch | 12 + .../MDFTextAccessibility-umbrella.h | 17 + .../MDFTextAccessibility.debug.xcconfig | 11 + .../MDFTextAccessibility.modulemap | 6 + .../MDFTextAccessibility.release.xcconfig | 11 + .../MaterialComponents-Info.plist | 26 + .../MaterialComponents-dummy.m | 5 + .../MaterialComponents-prefix.pch | 12 + .../MaterialComponents-umbrella.h | 92 + .../MaterialComponents.debug.xcconfig | 13 + .../MaterialComponents.modulemap | 6 + .../MaterialComponents.release.xcconfig | 13 + ...check_circle-MaterialComponents-Info.plist | 24 + .../Pods-ios/Pods-ios-Info.plist | 26 + .../Pods-ios-acknowledgements.markdown | 670 ++++ .../Pods-ios/Pods-ios-acknowledgements.plist | 726 ++++ .../Pods-ios/Pods-ios-dummy.m | 5 + ...os-frameworks-Debug-input-files.xcfilelist | 6 + ...s-frameworks-Debug-output-files.xcfilelist | 5 + ...-frameworks-Release-input-files.xcfilelist | 6 + ...frameworks-Release-output-files.xcfilelist | 5 + .../Pods-ios/Pods-ios-frameworks.sh | 194 + .../Pods-ios/Pods-ios-umbrella.h | 16 + .../Pods-ios/Pods-ios.debug.xcconfig | 15 + .../Pods-ios/Pods-ios.modulemap | 6 + .../Pods-ios/Pods-ios.release.xcconfig | 15 + .../Pods-iosTests/Pods-iosTests-Info.plist | 26 + .../Pods-iosTests-acknowledgements.markdown | 208 + .../Pods-iosTests-acknowledgements.plist | 240 ++ .../Pods-iosTests/Pods-iosTests-dummy.m | 5 + ...ts-frameworks-Debug-input-files.xcfilelist | 2 + ...s-frameworks-Debug-output-files.xcfilelist | 1 + ...-frameworks-Release-input-files.xcfilelist | 2 + ...frameworks-Release-output-files.xcfilelist | 1 + .../Pods-iosTests/Pods-iosTests-frameworks.sh | 186 + .../Pods-iosTests/Pods-iosTests-umbrella.h | 16 + .../Pods-iosTests.debug.xcconfig | 15 + .../Pods-iosTests/Pods-iosTests.modulemap | 6 + .../Pods-iosTests.release.xcconfig | 15 + .../lottie-ios/lottie-ios-Info.plist | 26 + .../lottie-ios/lottie-ios-dummy.m | 5 + .../lottie-ios/lottie-ios-prefix.pch | 12 + .../lottie-ios/lottie-ios-umbrella.h | 16 + .../lottie-ios/lottie-ios.debug.xcconfig | 14 + .../lottie-ios/lottie-ios.modulemap | 6 + .../lottie-ios/lottie-ios.release.xcconfig | 14 + .../cocoapods/Pods/lottie-ios/LICENSE | 201 + .../cocoapods/Pods/lottie-ios/README.md | 114 + .../LayerContainers/AnimationContainer.swift | 244 ++ .../CompLayers/CompositionLayer.swift | 160 + .../CompLayers/ImageCompositionLayer.swift | 50 + .../CompLayers/MaskContainerLayer.swift | 190 + .../CompLayers/NullCompositionLayer.swift | 28 + .../CompLayers/PreCompositionLayer.swift | 121 + .../CompLayers/ShapeCompositionLayer.swift | 60 + .../CompLayers/SolidCompositionLayer.swift | 57 + .../CompLayers/TextCompositionLayer.swift | 149 + .../CompositionLayersInitializer.swift | 90 + .../Utility/InvertedMatteLayer.swift | 61 + .../Utility/LayerFontProvider.swift | 41 + .../Utility/LayerImageProvider.swift | 53 + .../Utility/LayerTextProvider.swift | 40 + .../Utility/LayerTransformNode.swift | 144 + .../LayerContainers/Utility/TextLayer.swift | 321 ++ .../Sources/Private/Model/Animation.swift | 116 + .../Sources/Private/Model/Assets/Asset.swift | 33 + .../Private/Model/Assets/AssetLibrary.swift | 52 + .../Private/Model/Assets/ImageAsset.swift | 53 + .../Private/Model/Assets/PrecompAsset.swift | 34 + .../Private/Model/Extensions/Bundle.swift | 21 + .../KeyedDecodingContainerExtensions.swift | 44 + .../Private/Model/Keyframes/Keyframe.swift | 145 + .../Model/Keyframes/KeyframeGroup.swift | 120 + .../Model/Layers/ImageLayerModel.swift | 37 + .../Private/Model/Layers/LayerModel.swift | 166 + .../Model/Layers/PreCompLayerModel.swift | 55 + .../Model/Layers/ShapeLayerModel.swift | 37 + .../Model/Layers/SolidLayerModel.swift | 49 + .../Private/Model/Layers/TextLayerModel.swift | 49 + .../Private/Model/Objects/DashPattern.swift | 29 + .../Private/Model/Objects/Marker.swift | 23 + .../Sources/Private/Model/Objects/Mask.swift | 56 + .../Private/Model/Objects/Transform.swift | 111 + .../Private/Model/ShapeItems/Ellipse.swift | 59 + .../Private/Model/ShapeItems/FillI.swift | 58 + .../Model/ShapeItems/GradientFill.swift | 95 + .../Model/ShapeItems/GradientStroke.swift | 136 + .../Private/Model/ShapeItems/Group.swift | 37 + .../Private/Model/ShapeItems/Merge.swift | 50 + .../Private/Model/ShapeItems/Rectangle.swift | 55 + .../Private/Model/ShapeItems/Repeater.swift | 91 + .../Private/Model/ShapeItems/Shape.swift | 42 + .../Private/Model/ShapeItems/ShapeItem.swift | 106 + .../Model/ShapeItems/ShapeTransform.swift | 76 + .../Private/Model/ShapeItems/Star.swift | 95 + .../Private/Model/ShapeItems/Stroke.swift | 73 + .../Private/Model/ShapeItems/Trim.swift | 63 + .../Sources/Private/Model/Text/Font.swift | 42 + .../Sources/Private/Model/Text/Glyph.swift | 80 + .../Private/Model/Text/TextAnimator.swift | 105 + .../Private/Model/Text/TextDocument.swift | 78 + .../Extensions/ItemsExtension.swift | 97 + .../NodeProperties/NodeProperty.swift | 53 + .../Protocols/AnyNodeProperty.swift | 47 + .../Protocols/AnyValueContainer.swift | 26 + .../Protocols/KeypathSearchable.swift | 24 + .../Protocols/NodePropertyMap.swift | 44 + .../NodeProperties/ValueContainer.swift | 47 + .../ValueProviders/GroupInterpolator.swift | 38 + .../ValueProviders/KeyframeInterpolator.swift | 256 ++ .../ValueProviders/SingleValueProvider.swift | 44 + .../Nodes/ModifierNodes/TrimPathNode.swift | 281 ++ .../Nodes/OutputNodes/GroupOutputNode.swift | 76 + .../OutputNodes/PassThroughOutputNode.swift | 47 + .../Nodes/OutputNodes/PathOutputNode.swift | 90 + .../Renderables/FillRenderer.swift | 74 + .../Renderables/GradientFillRenderer.swift | 149 + .../Renderables/GradientStrokeRenderer.swift | 62 + .../Renderables/StrokeRenderer.swift | 162 + .../Nodes/PathNodes/EllipseNode.swift | 126 + .../Nodes/PathNodes/PolygonNode.swift | 152 + .../Nodes/PathNodes/RectNode.swift | 205 + .../Nodes/PathNodes/ShapeNode.swift | 74 + .../Nodes/PathNodes/StarNode.swift | 201 + .../Nodes/RenderContainers/GroupNode.swift | 155 + .../Nodes/RenderNodes/FillNode.swift | 90 + .../Nodes/RenderNodes/GradientFillNode.swift | 102 + .../RenderNodes/GradientStrokeNode.swift | 151 + .../Nodes/RenderNodes/StrokeNode.swift | 138 + .../Nodes/Text/TextAnimatorNode.swift | 270 ++ .../Protocols/AnimatorNode.swift | 204 + .../NodeRenderSystem/Protocols/PathNode.swift | 22 + .../Protocols/RenderNode.swift | 61 + .../RenderLayers/ShapeContainerLayer.swift | 77 + .../RenderLayers/ShapeRenderLayer.swift | 99 + .../Debugging/AnimatorNodeDebugging.swift | 25 + .../Utility/Debugging/LayerDebugging.swift | 226 ++ .../AnimationKeypathExtension.swift | 268 ++ .../Extensions/CGFloatExtensions.swift | 152 + .../Private/Utility/Extensions/MathKit.swift | 579 +++ .../Utility/Extensions/StringExtensions.swift | 33 + .../Utility/Helpers/AnimationContext.swift | 78 + .../Interpolatable/Interpolatable.swift | 19 + .../InterpolatableExtensions.swift | 217 ++ .../Interpolatable/KeyframeExtensions.swift | 47 + .../Utility/Primitives/BezierPath.swift | 423 ++ .../Utility/Primitives/ColorExtension.swift | 82 + .../Primitives/CompoundBezierPath.swift | 169 + .../Utility/Primitives/CurveVertex.swift | 192 + .../Utility/Primitives/PathElement.swift | 77 + .../Primitives/VectorsExtensions.swift | 283 ++ .../Public/Animation/AnimationPublic.swift | 206 + .../Public/Animation/AnimationView.swift | 1045 +++++ .../Animation/AnimationViewInitializers.swift | 96 + .../AnimationCacheProvider.swift | 24 + .../AnimationCache/LRUAnimationCache.swift | 63 + .../DynamicProperties/AnimationKeypath.swift | 46 + .../DynamicProperties/AnyValueProvider.swift | 29 + .../ValueProviders/ColorValueProvider.swift | 70 + .../ValueProviders/FloatValueProvider.swift | 69 + .../GradientValueProvider.swift | 122 + .../ValueProviders/PointValueProvider.swift | 68 + .../ValueProviders/SizeValueProvider.swift | 69 + .../FontProvider/AnimationFontProvider.swift | 37 + .../AnimationImageProvider.swift | 23 + .../Public/Primitives/AnimationTime.swift | 15 + .../Sources/Public/Primitives/Color.swift | 45 + .../Sources/Public/Primitives/Vectors.swift | 40 + .../TextProvider/AnimationTextProvider.swift | 55 + .../Sources/Public/iOS/AnimatedButton.swift | 77 + .../Sources/Public/iOS/AnimatedControl.swift | 173 + .../Sources/Public/iOS/AnimatedSwitch.swift | 216 + .../Sources/Public/iOS/AnimationSubview.swift | 20 + .../Public/iOS/BundleImageProvider.swift | 94 + .../CompatibleAnimationKeypath.swift | 33 + .../CompatibleAnimationView.swift | 325 ++ .../Public/iOS/FilepathImageProvider.swift | 63 + .../Sources/Public/iOS/LottieView.swift | 76 + .../Sources/Public/iOS/UIColorExtension.swift | 21 + .../cocoapods/ios.xcodeproj/project.pbxproj | 500 +++ .../contents.xcworkspacedata | 7 + .../ios.xcworkspace/contents.xcworkspacedata | 10 + test/fixtures/cocoapods/project.yml | 19 + test/sources/cocoapods_test.rb | 68 + 466 files changed, 58914 insertions(+) create mode 100644 docs/sources/cocoapods.md create mode 100644 lib/licensed/sources/cocoapods.rb create mode 100644 script/source-setup/cocoapods create mode 100644 test/fixtures/cocoapods/Podfile create mode 100644 test/fixtures/cocoapods/Podfile.lock create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/LICENSE create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/README.md create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift create mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift create mode 100644 test/fixtures/cocoapods/Pods/Chatto/LICENSE create mode 100644 test/fixtures/cocoapods/Pods/Chatto/README.md create mode 100644 test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/README.md create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h create mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h create mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m create mode 100644 test/fixtures/cocoapods/Pods/Manifest.lock create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/README.md create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons+BundleLoader.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h create mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme create mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist create mode 100755 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist create mode 100755 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap create mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/LICENSE create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/README.md create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/RectNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/KeyframeExtensions.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/PathElement.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift create mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift create mode 100644 test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj create mode 100644 test/fixtures/cocoapods/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 test/fixtures/cocoapods/ios.xcworkspace/contents.xcworkspacedata create mode 100644 test/fixtures/cocoapods/project.yml create mode 100644 test/sources/cocoapods_test.rb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d9650b35..ddab0157 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -612,3 +612,24 @@ jobs: run: script/source-setup/yarn/berry - name: Run tests run: script/test yarn/berry + + cocoapods: + runs-on: ubuntu-latest + needs: core + strategy: + matrix: + cocoapods: [ '1.11.3' ] + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - name: Set up Cocoapods + run: gem install cocoapods -v ${{ matrix.cocoapods }} + - name: Bootstrap + run: script/bootstrap + - name: Set up fixtures + run: script/source-setup/cocoapods + - name: Run tests + run: script/test cocoapods diff --git a/docs/sources/cocoapods.md b/docs/sources/cocoapods.md new file mode 100644 index 00000000..21a7ad15 --- /dev/null +++ b/docs/sources/cocoapods.md @@ -0,0 +1,15 @@ +# CocoaPods + +The cocoapods source will detect dependencies when `Podfile` and `Podfile.lock` are found at an app's `source_path`. + +It uses the `pod` CLI commands to enumerate dependencies and gather metadata on each package. + +### Evaluating dependencies from a specific target + +The `cocoapods.targets` property is used to specify which targets to analyze dependencies from. By default, dependencies from all targets will be analyzed. + +```yml +cocoapods: + targets: + - ios +``` diff --git a/lib/licensed/sources.rb b/lib/licensed/sources.rb index 15f6cdc8..74053dc3 100644 --- a/lib/licensed/sources.rb +++ b/lib/licensed/sources.rb @@ -19,5 +19,6 @@ module Sources require "licensed/sources/gradle" require "licensed/sources/mix" require "licensed/sources/yarn" + require "licensed/sources/cocoapods" end end diff --git a/lib/licensed/sources/cocoapods.rb b/lib/licensed/sources/cocoapods.rb new file mode 100644 index 00000000..3eaf67c3 --- /dev/null +++ b/lib/licensed/sources/cocoapods.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true +require "json" +require "pathname" +require "uri" +require "cocoapods-core" + +module Licensed + module Sources + class Cocoapods < Source + def enabled? + return unless Licensed::Shell.tool_available?("pod") + + config.pwd.join("Podfile").exist? && config.pwd.join("Podfile.lock").exist? + end + + def enumerate_dependencies + pods.map { |pod| + metadata = dependency_metadata(pod) + path = dependency_path(pod.root_name) + + Dependency.new( + path: path, + name: metadata["name"], + version: metadata["version"], + metadata: { + "type" => Cocoapods.type, + "homepage" => metadata["homepage"], + "summary" => metadata["summary"], + "platforms" => metadata["platform"] + } + ) + } + end + + private + + def pods + if targets.nil? + lockfile.dependencies + else + targets_to_validate = podfile.target_definition_list.filter { |t| targets.include?(t.label) } + if targets_to_validate.any? + targets_to_validate.map(&:dependencies).flatten + else + raise Licensed::Sources::Source::Error, "Unable to find any target in the Podfile matching the ones provided in the config." + end + end + end + + def targets + config.dig("cocoapods", "targets")&.map { |t| "Pods-#{t}" } + end + + def lockfile + @lockfile ||= Pod::Lockfile.from_file(config.pwd.join("Podfile.lock")) + end + + def podfile + @podfile ||= Pod::Podfile.from_file(config.pwd.join("Podfile")) + end + + def dependency_metadata(pod) + metadata = JSON.parse(Licensed::Shell.execute("pod", "spec", "cat", "--regex", "^#{pod.root_name}$")) + # The version returned by `pod spec cat` is the most recent version that exists which may not be the one installed. + metadata["version"] = lockfile.version(pod.name).version + metadata["name"] = pod.name + metadata + end + + def dependency_path(name) + config.pwd.join("Pods/#{name}") + end + end + end +end diff --git a/licensed.gemspec b/licensed.gemspec index 26296108..e90847e9 100644 --- a/licensed.gemspec +++ b/licensed.gemspec @@ -32,10 +32,12 @@ Gem::Specification.new do |spec| spec.add_dependency "parallel", ">= 0.18.0" spec.add_dependency "reverse_markdown", ">= 1", "< 3" spec.add_dependency "json", ">= 2.6.2" + spec.add_dependency "cocoapods-core", "~> 1.0.0" spec.add_development_dependency "rake", ">= 12.3.3" spec.add_development_dependency "minitest", "~> 5.8" spec.add_development_dependency "mocha", "~> 2.0" spec.add_development_dependency "rubocop-github", "~> 0.6" spec.add_development_dependency "byebug", "~> 11.1.3" + spec.add_development_dependency "cocoapods", "~> 1.0.0" end diff --git a/script/source-setup/cocoapods b/script/source-setup/cocoapods new file mode 100644 index 00000000..e03a4a09 --- /dev/null +++ b/script/source-setup/cocoapods @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +if [ -z "$(which pods)" ]; then + echo "A local pod installation is required for cocoapods development." >&2 + exit 127 +fi + +# setup test fixtures +BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +cd $BASE_PATH/test/fixtures/cocoapods + +if [ "$1" == "-f" ]; then + pod install --clean-install +fi + +pod install diff --git a/test/fixtures/cocoapods/Podfile b/test/fixtures/cocoapods/Podfile new file mode 100644 index 00000000..15761896 --- /dev/null +++ b/test/fixtures/cocoapods/Podfile @@ -0,0 +1,19 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '13.0' + +target 'ios' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + pod "Alamofire", "5.4.3" + pod "MaterialComponents/Buttons", "124.2.0" + pod "MaterialComponents/Cards", "124.2.0" + pod "Chatto", git: "https://github.com/badoo/Chatto", tag: "4.1.0" + + target 'iosTests' do + inherit! :search_paths + + pod "lottie-ios", "3.3.0" + end + +end diff --git a/test/fixtures/cocoapods/Podfile.lock b/test/fixtures/cocoapods/Podfile.lock new file mode 100644 index 00000000..dd84759f --- /dev/null +++ b/test/fixtures/cocoapods/Podfile.lock @@ -0,0 +1,102 @@ +PODS: + - Alamofire (5.4.3) + - Chatto (4.1.0) + - lottie-ios (3.3.0) + - MaterialComponents/AnimationTiming (124.2.0) + - MaterialComponents/Availability (124.2.0) + - MaterialComponents/Buttons (124.2.0): + - MaterialComponents/Elevation + - MaterialComponents/Ink + - MaterialComponents/private/Math + - MaterialComponents/Ripple + - MaterialComponents/Shadow + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/ShapeLibrary + - MaterialComponents/Shapes + - MaterialComponents/Typography + - MDFInternationalization + - MDFTextAccessibility + - MaterialComponents/Cards (124.2.0): + - MaterialComponents/Elevation + - MaterialComponents/Ink + - MaterialComponents/private/Icons/ic_check_circle + - MaterialComponents/private/Math + - MaterialComponents/Ripple + - MaterialComponents/ShadowLayer + - MaterialComponents/Shapes + - MaterialComponents/Elevation (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Ink (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/private/Application (124.2.0) + - MaterialComponents/private/Color (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Icons/Base (124.2.0) + - MaterialComponents/private/Icons/ic_check_circle (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Math (124.2.0) + - MaterialComponents/Ripple (124.2.0): + - MaterialComponents/AnimationTiming + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Shadow (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/ShadowElevations (124.2.0) + - MaterialComponents/ShadowLayer (124.2.0): + - MaterialComponents/ShadowElevations + - MaterialComponents/ShapeLibrary (124.2.0): + - MaterialComponents/private/Math + - MaterialComponents/Shapes + - MaterialComponents/Shapes (124.2.0): + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/ShadowLayer + - MaterialComponents/Typography (124.2.0): + - MaterialComponents/private/Application + - MaterialComponents/private/Math + - MDFTextAccessibility + - MDFInternationalization (3.0.0) + - MDFTextAccessibility (2.0.1) + +DEPENDENCIES: + - Alamofire (= 5.4.3) + - Chatto (from `https://github.com/badoo/Chatto`, tag `4.1.0`) + - lottie-ios (= 3.3.0) + - MaterialComponents/Buttons (= 124.2.0) + - MaterialComponents/Cards (= 124.2.0) + +SPEC REPOS: + trunk: + - Alamofire + - lottie-ios + - MaterialComponents + - MDFInternationalization + - MDFTextAccessibility + +EXTERNAL SOURCES: + Chatto: + :git: https://github.com/badoo/Chatto + :tag: 4.1.0 + +CHECKOUT OPTIONS: + Chatto: + :git: https://github.com/badoo/Chatto + :tag: 4.1.0 + +SPEC CHECKSUMS: + Alamofire: e447a2774a40c996748296fa2c55112fdbbc42f9 + Chatto: dae34c377e0eca8bdcfe7a05a4d79d55eaccec3e + lottie-ios: 6ac74dcc09904798f59b18cb3075c089d76be9ae + MaterialComponents: 1a9b2d9d45b1601ae544de85089adc4c464306d4 + MDFInternationalization: d697c55307816222a55685c4ccb1044ffb030c12 + MDFTextAccessibility: f4bb4cc2194286651b59a215fdeaa0e05dc90ba5 + +PODFILE CHECKSUM: 5288d14d2586d983b7a18fae6fc10abda0f306a9 + +COCOAPODS: 1.11.2 diff --git a/test/fixtures/cocoapods/Pods/Alamofire/LICENSE b/test/fixtures/cocoapods/Pods/Alamofire/LICENSE new file mode 100644 index 00000000..6b4d719a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2021 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. diff --git a/test/fixtures/cocoapods/Pods/Alamofire/README.md b/test/fixtures/cocoapods/Pods/Alamofire/README.md new file mode 100644 index 00000000..9a5a8063 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/README.md @@ -0,0 +1,206 @@ +![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png) + +[![Build Status](https://github.com/Alamofire/Alamofire/workflows/Alamofire%20CI/badge.svg?branch=master)](https://github.com/Alamofire/Alamofire/actions) +[![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)](https://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) +[![Open Source Helpers](https://www.codetriage.com/alamofire/alamofire/badges/users.svg)](https://www.codetriage.com/alamofire/alamofire) + +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](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#using-alamofire) + - [**Introduction -**](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#introduction) [Making Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-requests), [Response Handling](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-handling), [Response Validation](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-validation), [Response Caching](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-caching) + - **HTTP -** [HTTP Methods](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-methods), [Parameters and Parameter Encoder](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md##request-parameters-and-parameter-encoders), [HTTP Headers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-headers), [Authentication](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#authentication) + - **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server) + - **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output) +- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md) + - **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#sessiondelegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request) + - **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor) + - **Model Objects -** [Custom Response Handlers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#customizing-response-handlers) + - **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability) +- [Open Radars](#open-radars) +- [FAQ](#faq) +- [Credits](#credits) +- [Donations](#donations) +- [License](#license) + +## Features + +- [x] Chainable Request / Response Methods +- [x] Combine Support +- [x] URL / JSON 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 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ +- Xcode 11+ +- Swift 5.1+ + +## Migration Guides + +- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md) +- [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 with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`. +- If you need to **find or understand an API**, check [our documentation](http://alamofire.github.io/Alamofire/) or [Apple's documentation for `URLSession`](https://developer.apple.com/documentation/foundation/url_loading_system), on top of which Alamofire is built. +- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). +- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). +- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). +- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better! +- If you **want to contribute**, submit a pull request! + +## Installation + +### CocoaPods + +[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +pod 'Alamofire', '~> 5.2' +``` + +### Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "Alamofire/Alamofire" ~> 5.2 +``` + +### 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", .upToNextMajor(from: "5.2.0")) +] +``` + +### 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](https://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 macOS. + + > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as `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. + +## 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 the test case +- `rdar://26870455` - Background URL Session Configurations do not work in the simulator +- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` +- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS + +## 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): 9/1/17 in Xcode 9 beta 6. +- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+ + - (Resolved): Just add `CFNetwork` to your linked frameworks. + +## Workarounds + +- Collection of `URLSessionTaskMetrics` is currently disabled on watchOS due to `FB7624529`. + +## 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. + +## 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 stay registered as a federal non-profit organization. +Registering will allow Foundation 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 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. +Any amount you can donate today to help us reach our goal would be greatly appreciated. + +[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W34WPEE74APJQ) + +## License + +Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details. diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift new file mode 100644 index 00000000..e8e4fe83 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift @@ -0,0 +1,854 @@ +// +// AFError.swift +// +// Copyright (c) 2014-2018 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. +public enum AFError: Error { + /// The underlying reason the `.multipartEncodingFailed` error occurred. + public enum MultipartEncodingFailureReason { + /// The `fileURL` provided for reading an encodable body part isn't a file `URL`. + case bodyPartURLInvalid(url: URL) + /// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension. + case bodyPartFilenameInvalid(in: URL) + /// The file at the `fileURL` provided was not reachable. + case bodyPartFileNotReachable(at: URL) + /// Attempting to check the reachability of the `fileURL` provided threw an error. + case bodyPartFileNotReachableWithError(atURL: URL, error: Error) + /// The file at the `fileURL` provided is actually a directory. + case bodyPartFileIsDirectory(at: URL) + /// The size of the file at the `fileURL` provided was not returned by the system. + case bodyPartFileSizeNotAvailable(at: URL) + /// The attempt to find the size of the file at the `fileURL` provided threw an error. + case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) + /// An `InputStream` could not be created for the provided `fileURL`. + case bodyPartInputStreamCreationFailed(for: URL) + /// An `OutputStream` could not be created when attempting to write the encoded data to disk. + case outputStreamCreationFailed(for: URL) + /// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`. + case outputStreamFileAlreadyExists(at: URL) + /// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`. + case outputStreamURLInvalid(url: URL) + /// The attempt to write the encoded body data to disk failed with an underlying error. + case outputStreamWriteFailed(error: Error) + /// The attempt to read an encoded body part `InputStream` failed with underlying system error. + case inputStreamReadFailed(error: Error) + } + + /// Represents unexpected input stream length that occur when encoding the `MultipartFormData`. Instances will be + /// embedded within an `AFError.multipartEncodingFailed` `.inputStreamReadFailed` case. + public struct UnexpectedInputStreamLength: Error { + /// The expected byte count to read. + public var bytesExpected: UInt64 + /// The actual byte count read. + public var bytesRead: UInt64 + } + + /// The underlying reason the `.parameterEncodingFailed` error occurred. + public enum ParameterEncodingFailureReason { + /// The `URLRequest` did not have a `URL` to encode. + case missingURL + /// JSON serialization failed with an underlying system error during the encoding process. + case jsonEncodingFailed(error: Error) + /// Custom parameter encoding failed due to the associated `Error`. + case customEncodingFailed(error: Error) + } + + /// The underlying reason the `.parameterEncoderFailed` error occurred. + public enum ParameterEncoderFailureReason { + /// Possible missing components. + public enum RequiredComponent { + /// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding. + case url + /// The `HTTPMethod` could not be extracted from the passed `URLRequest`. + case httpMethod(rawValue: String) + } + + /// A `RequiredComponent` was missing during encoding. + case missingRequiredComponent(RequiredComponent) + /// The underlying encoder failed with the associated error. + case encoderFailed(error: Error) + } + + /// The underlying reason the `.responseValidationFailed` error occurred. + public enum ResponseValidationFailureReason { + /// The data file containing the server response did not exist. + case dataFileNil + /// The data file containing the server response at the associated `URL` could not be read. + case dataFileReadFailed(at: URL) + /// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a + /// wildcard type. + case missingContentType(acceptableContentTypes: [String]) + /// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`. + case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) + /// The response status code was not acceptable. + case unacceptableStatusCode(code: Int) + /// Custom response validation failed due to the associated `Error`. + case customValidationFailed(error: Error) + } + + /// The underlying reason the response serialization error occurred. + public enum ResponseSerializationFailureReason { + /// The server response contained no data or the data was zero length. + case inputDataNilOrZeroLength + /// The file containing the server response did not exist. + case inputFileNil + /// The file containing the server response could not be read from the associated `URL`. + case inputFileReadFailed(at: URL) + /// String serialization failed using the provided `String.Encoding`. + case stringSerializationFailed(encoding: String.Encoding) + /// JSON serialization failed with an underlying system error. + case jsonSerializationFailed(error: Error) + /// A `DataDecoder` failed to decode the response due to the associated `Error`. + case decodingFailed(error: Error) + /// A custom response serializer failed due to the associated `Error`. + case customSerializationFailed(error: Error) + /// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type. + case invalidEmptyResponse(type: String) + } + + /// Underlying reason a server trust evaluation error occurred. + public enum ServerTrustFailureReason { + /// The output of a server trust evaluation. + public struct Output { + /// The host for which the evaluation was performed. + public let host: String + /// The `SecTrust` value which was evaluated. + public let trust: SecTrust + /// The `OSStatus` of evaluation operation. + public let status: OSStatus + /// The result of the evaluation operation. + public let result: SecTrustResultType + + /// Creates an `Output` value from the provided values. + init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) { + self.host = host + self.trust = trust + self.status = status + self.result = result + } + } + + /// No `ServerTrustEvaluator` was found for the associated host. + case noRequiredEvaluator(host: String) + /// No certificates were found with which to perform the trust evaluation. + case noCertificatesFound + /// No public keys were found with which to perform the trust evaluation. + case noPublicKeysFound + /// During evaluation, application of the associated `SecPolicy` failed. + case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus) + /// During evaluation, setting the associated anchor certificates failed. + case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate]) + /// During evaluation, creation of the revocation policy failed. + case revocationPolicyCreationFailed + /// `SecTrust` evaluation failed with the associated `Error`, if one was produced. + case trustEvaluationFailed(error: Error?) + /// Default evaluation failed with the associated `Output`. + case defaultEvaluationFailed(output: Output) + /// Host validation failed with the associated `Output`. + case hostValidationFailed(output: Output) + /// Revocation check failed with the associated `Output` and options. + case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options) + /// Certificate pinning failed. + case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate]) + /// Public key pinning failed. + case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey]) + /// Custom server trust evaluation failed due to the associated `Error`. + case customEvaluationFailed(error: Error) + } + + /// The underlying reason the `.urlRequestValidationFailed` + public enum URLRequestValidationFailureReason { + /// URLRequest with GET method had body data. + case bodyDataInGETRequest(Data) + } + + /// `UploadableConvertible` threw an error in `createUploadable()`. + case createUploadableFailed(error: Error) + /// `URLRequestConvertible` threw an error in `asURLRequest()`. + case createURLRequestFailed(error: Error) + /// `SessionDelegate` threw an error while attempting to move downloaded file to destination URL. + case downloadedFileMoveFailed(error: Error, source: URL, destination: URL) + /// `Request` was explicitly cancelled. + case explicitlyCancelled + /// `URLConvertible` type failed to create a valid `URL`. + case invalidURL(url: URLConvertible) + /// Multipart form encoding failed. + case multipartEncodingFailed(reason: MultipartEncodingFailureReason) + /// `ParameterEncoding` threw an error during the encoding process. + case parameterEncodingFailed(reason: ParameterEncodingFailureReason) + /// `ParameterEncoder` threw an error while running the encoder. + case parameterEncoderFailed(reason: ParameterEncoderFailureReason) + /// `RequestAdapter` threw an error during adaptation. + case requestAdaptationFailed(error: Error) + /// `RequestRetrier` threw an error during the request retry process. + case requestRetryFailed(retryError: Error, originalError: Error) + /// Response validation failed. + case responseValidationFailed(reason: ResponseValidationFailureReason) + /// Response serialization failed. + case responseSerializationFailed(reason: ResponseSerializationFailureReason) + /// `ServerTrustEvaluating` instance threw an error during trust evaluation. + case serverTrustEvaluationFailed(reason: ServerTrustFailureReason) + /// `Session` which issued the `Request` was deinitialized, most likely because its reference went out of scope. + case sessionDeinitialized + /// `Session` was explicitly invalidated, possibly with the `Error` produced by the underlying `URLSession`. + case sessionInvalidated(error: Error?) + /// `URLSessionTask` completed with error. + case sessionTaskFailed(error: Error) + /// `URLRequest` failed validation. + case urlRequestValidationFailed(reason: URLRequestValidationFailureReason) +} + +extension Error { + /// Returns the instance cast as an `AFError`. + public var asAFError: AFError? { + self as? AFError + } + + /// Returns the instance cast as an `AFError`. If casting fails, a `fatalError` with the specified `message` is thrown. + public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError { + guard let afError = self as? AFError else { + fatalError(message(), file: file, line: line) + } + return afError + } + + /// Casts the instance as `AFError` or returns `defaultAFError` + func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError { + self as? AFError ?? defaultAFError() + } +} + +// MARK: - Error Booleans + +extension AFError { + /// Returns whether the instance is `.sessionDeinitialized`. + public var isSessionDeinitializedError: Bool { + if case .sessionDeinitialized = self { return true } + return false + } + + /// Returns whether the instance is `.sessionInvalidated`. + public var isSessionInvalidatedError: Bool { + if case .sessionInvalidated = self { return true } + return false + } + + /// Returns whether the instance is `.explicitlyCancelled`. + public var isExplicitlyCancelledError: Bool { + if case .explicitlyCancelled = self { return true } + return false + } + + /// Returns whether the instance is `.invalidURL`. + public var isInvalidURLError: Bool { + if case .invalidURL = self { return true } + return false + } + + /// Returns whether the instance is `.parameterEncodingFailed`. 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 instance is `.parameterEncoderFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isParameterEncoderError: Bool { + if case .parameterEncoderFailed = self { return true } + return false + } + + /// Returns whether the instance is `.multipartEncodingFailed`. 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 instance is `.requestAdaptationFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isRequestAdaptationError: Bool { + if case .requestAdaptationFailed = self { return true } + return false + } + + /// Returns whether the instance is `.responseValidationFailed`. When `true`, the `acceptableContentTypes`, + /// `responseContentType`, `responseCode`, and `underlyingError` properties will contain the associated values. + public var isResponseValidationError: Bool { + if case .responseValidationFailed = self { return true } + return false + } + + /// Returns whether the instance is `.responseSerializationFailed`. When `true`, the `failedStringEncoding` and + /// `underlyingError` properties will contain the associated values. + public var isResponseSerializationError: Bool { + if case .responseSerializationFailed = self { return true } + return false + } + + /// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isServerTrustEvaluationError: Bool { + if case .serverTrustEvaluationFailed = self { return true } + return false + } + + /// Returns whether the instance is `requestRetryFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isRequestRetryError: Bool { + if case .requestRetryFailed = self { return true } + return false + } + + /// Returns whether the instance is `createUploadableFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isCreateUploadableError: Bool { + if case .createUploadableFailed = self { return true } + return false + } + + /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isCreateURLRequestError: Bool { + if case .createURLRequestFailed = self { return true } + return false + } + + /// Returns whether the instance is `downloadedFileMoveFailed`. When `true`, the `destination` and `underlyingError` properties will + /// contain the associated values. + public var isDownloadedFileMoveError: Bool { + if case .downloadedFileMoveFailed = self { return true } + return false + } + + /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isSessionTaskError: Bool { + if case .sessionTaskFailed = self { return true } + return false + } +} + +// MARK: - Convenience Properties + +extension AFError { + /// The `URLConvertible` associated with the error. + public var urlConvertible: URLConvertible? { + guard case let .invalidURL(url) = self else { return nil } + return url + } + + /// The `URL` associated with the error. + public var url: URL? { + guard case let .multipartEncodingFailed(reason) = self else { return nil } + return reason.url + } + + /// The underlying `Error` responsible for generating the failure associated with `.sessionInvalidated`, + /// `.parameterEncodingFailed`, `.parameterEncoderFailed`, `.multipartEncodingFailed`, `.requestAdaptationFailed`, + /// `.responseSerializationFailed`, `.requestRetryFailed` errors. + public var underlyingError: Error? { + switch self { + case let .multipartEncodingFailed(reason): + return reason.underlyingError + case let .parameterEncodingFailed(reason): + return reason.underlyingError + case let .parameterEncoderFailed(reason): + return reason.underlyingError + case let .requestAdaptationFailed(error): + return error + case let .requestRetryFailed(retryError, _): + return retryError + case let .responseValidationFailed(reason): + return reason.underlyingError + case let .responseSerializationFailed(reason): + return reason.underlyingError + case let .serverTrustEvaluationFailed(reason): + return reason.underlyingError + case let .sessionInvalidated(error): + return error + case let .createUploadableFailed(error): + return error + case let .createURLRequestFailed(error): + return error + case let .downloadedFileMoveFailed(error, _, _): + return error + case let .sessionTaskFailed(error): + return error + case .explicitlyCancelled, + .invalidURL, + .sessionDeinitialized, + .urlRequestValidationFailed: + return nil + } + } + + /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. + public var acceptableContentTypes: [String]? { + guard case let .responseValidationFailed(reason) = self else { return nil } + return reason.acceptableContentTypes + } + + /// The response `Content-Type` of a `.responseValidationFailed` error. + public var responseContentType: String? { + guard case let .responseValidationFailed(reason) = self else { return nil } + return reason.responseContentType + } + + /// The response code of a `.responseValidationFailed` error. + public var responseCode: Int? { + guard case let .responseValidationFailed(reason) = self else { return nil } + return reason.responseCode + } + + /// The `String.Encoding` associated with a failed `.stringResponse()` call. + public var failedStringEncoding: String.Encoding? { + guard case let .responseSerializationFailed(reason) = self else { return nil } + return reason.failedStringEncoding + } + + /// The `source` URL of a `.downloadedFileMoveFailed` error. + public var sourceURL: URL? { + guard case let .downloadedFileMoveFailed(_, source, _) = self else { return nil } + return source + } + + /// The `destination` URL of a `.downloadedFileMoveFailed` error. + public var destinationURL: URL? { + guard case let .downloadedFileMoveFailed(_, _, destination) = self else { return nil } + return destination + } + + /// The download resume data of any underlying network error. Only produced by `DownloadRequest`s. + public var downloadResumeData: Data? { + (underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data + } +} + +extension AFError.ParameterEncodingFailureReason { + var underlyingError: Error? { + switch self { + case let .jsonEncodingFailed(error), + let .customEncodingFailed(error): + return error + case .missingURL: + return nil + } + } +} + +extension AFError.ParameterEncoderFailureReason { + var underlyingError: Error? { + switch self { + case let .encoderFailed(error): + return error + case .missingRequiredComponent: + return nil + } + } +} + +extension AFError.MultipartEncodingFailureReason { + var url: URL? { + switch self { + case let .bodyPartURLInvalid(url), + let .bodyPartFilenameInvalid(url), + let .bodyPartFileNotReachable(url), + let .bodyPartFileIsDirectory(url), + let .bodyPartFileSizeNotAvailable(url), + let .bodyPartInputStreamCreationFailed(url), + let .outputStreamCreationFailed(url), + let .outputStreamFileAlreadyExists(url), + let .outputStreamURLInvalid(url), + let .bodyPartFileNotReachableWithError(url, _), + let .bodyPartFileSizeQueryFailedWithError(url, _): + return url + case .outputStreamWriteFailed, + .inputStreamReadFailed: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .bodyPartFileNotReachableWithError(_, error), + let .bodyPartFileSizeQueryFailedWithError(_, error), + let .outputStreamWriteFailed(error), + let .inputStreamReadFailed(error): + return error + case .bodyPartURLInvalid, + .bodyPartFilenameInvalid, + .bodyPartFileNotReachable, + .bodyPartFileIsDirectory, + .bodyPartFileSizeNotAvailable, + .bodyPartInputStreamCreationFailed, + .outputStreamCreationFailed, + .outputStreamFileAlreadyExists, + .outputStreamURLInvalid: + return nil + } + } +} + +extension AFError.ResponseValidationFailureReason { + var acceptableContentTypes: [String]? { + switch self { + case let .missingContentType(types), + let .unacceptableContentType(types, _): + return types + case .dataFileNil, + .dataFileReadFailed, + .unacceptableStatusCode, + .customValidationFailed: + return nil + } + } + + var responseContentType: String? { + switch self { + case let .unacceptableContentType(_, responseType): + return responseType + case .dataFileNil, + .dataFileReadFailed, + .missingContentType, + .unacceptableStatusCode, + .customValidationFailed: + return nil + } + } + + var responseCode: Int? { + switch self { + case let .unacceptableStatusCode(code): + return code + case .dataFileNil, + .dataFileReadFailed, + .missingContentType, + .unacceptableContentType, + .customValidationFailed: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .customValidationFailed(error): + return error + case .dataFileNil, + .dataFileReadFailed, + .missingContentType, + .unacceptableContentType, + .unacceptableStatusCode: + return nil + } + } +} + +extension AFError.ResponseSerializationFailureReason { + var failedStringEncoding: String.Encoding? { + switch self { + case let .stringSerializationFailed(encoding): + return encoding + case .inputDataNilOrZeroLength, + .inputFileNil, + .inputFileReadFailed(_), + .jsonSerializationFailed(_), + .decodingFailed(_), + .customSerializationFailed(_), + .invalidEmptyResponse: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .jsonSerializationFailed(error), + let .decodingFailed(error), + let .customSerializationFailed(error): + return error + case .inputDataNilOrZeroLength, + .inputFileNil, + .inputFileReadFailed, + .stringSerializationFailed, + .invalidEmptyResponse: + return nil + } + } +} + +extension AFError.ServerTrustFailureReason { + var output: AFError.ServerTrustFailureReason.Output? { + switch self { + case let .defaultEvaluationFailed(output), + let .hostValidationFailed(output), + let .revocationCheckFailed(output, _): + return output + case .noRequiredEvaluator, + .noCertificatesFound, + .noPublicKeysFound, + .policyApplicationFailed, + .settingAnchorCertificatesFailed, + .revocationPolicyCreationFailed, + .trustEvaluationFailed, + .certificatePinningFailed, + .publicKeyPinningFailed, + .customEvaluationFailed: + return nil + } + } + + var underlyingError: Error? { + switch self { + case let .customEvaluationFailed(error): + return error + case let .trustEvaluationFailed(error): + return error + case .noRequiredEvaluator, + .noCertificatesFound, + .noPublicKeysFound, + .policyApplicationFailed, + .settingAnchorCertificatesFailed, + .revocationPolicyCreationFailed, + .defaultEvaluationFailed, + .hostValidationFailed, + .revocationCheckFailed, + .certificatePinningFailed, + .publicKeyPinningFailed: + return nil + } + } +} + +// MARK: - Error Descriptions + +extension AFError: LocalizedError { + public var errorDescription: String? { + switch self { + case .explicitlyCancelled: + return "Request explicitly cancelled." + case let .invalidURL(url): + return "URL is not valid: \(url)" + case let .parameterEncodingFailed(reason): + return reason.localizedDescription + case let .parameterEncoderFailed(reason): + return reason.localizedDescription + case let .multipartEncodingFailed(reason): + return reason.localizedDescription + case let .requestAdaptationFailed(error): + return "Request adaption failed with error: \(error.localizedDescription)" + case let .responseValidationFailed(reason): + return reason.localizedDescription + case let .responseSerializationFailed(reason): + return reason.localizedDescription + case let .requestRetryFailed(retryError, originalError): + return """ + Request retry failed with retry error: \(retryError.localizedDescription), \ + original error: \(originalError.localizedDescription) + """ + case .sessionDeinitialized: + return """ + Session was invalidated without error, so it was likely deinitialized unexpectedly. \ + Be sure to retain a reference to your Session for the duration of your requests. + """ + case let .sessionInvalidated(error): + return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")" + case let .serverTrustEvaluationFailed(reason): + return "Server trust evaluation failed due to reason: \(reason.localizedDescription)" + case let .urlRequestValidationFailed(reason): + return "URLRequest validation failed due to reason: \(reason.localizedDescription)" + case let .createUploadableFailed(error): + return "Uploadable creation failed with error: \(error.localizedDescription)" + case let .createURLRequestFailed(error): + return "URLRequest creation failed with error: \(error.localizedDescription)" + case let .downloadedFileMoveFailed(error, source, destination): + return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)" + case let .sessionTaskFailed(error): + return "URLSessionTask failed with error: \(error.localizedDescription)" + } + } +} + +extension AFError.ParameterEncodingFailureReason { + var localizedDescription: String { + switch self { + case .missingURL: + return "URL request to encode was missing a URL" + case let .jsonEncodingFailed(error): + return "JSON could not be encoded because of error:\n\(error.localizedDescription)" + case let .customEncodingFailed(error): + return "Custom parameter encoder failed with error: \(error.localizedDescription)" + } + } +} + +extension AFError.ParameterEncoderFailureReason { + var localizedDescription: String { + switch self { + case let .missingRequiredComponent(component): + return "Encoding failed due to a missing request component: \(component)" + case let .encoderFailed(error): + return "The underlying encoder failed with the error: \(error)" + } + } +} + +extension AFError.MultipartEncodingFailureReason { + var localizedDescription: String { + switch self { + case let .bodyPartURLInvalid(url): + return "The URL provided is not a file URL: \(url)" + case let .bodyPartFilenameInvalid(url): + return "The URL provided does not have a valid filename: \(url)" + case let .bodyPartFileNotReachable(url): + return "The URL provided is not reachable: \(url)" + case let .bodyPartFileNotReachableWithError(url, error): + return """ + The system returned an error while checking the provided URL for reachability. + URL: \(url) + Error: \(error) + """ + case let .bodyPartFileIsDirectory(url): + return "The URL provided is a directory: \(url)" + case let .bodyPartFileSizeNotAvailable(url): + return "Could not fetch the file size from the provided URL: \(url)" + case let .bodyPartFileSizeQueryFailedWithError(url, error): + return """ + The system returned an error while attempting to fetch the file size from the provided URL. + URL: \(url) + Error: \(error) + """ + case let .bodyPartInputStreamCreationFailed(url): + return "Failed to create an InputStream for the provided URL: \(url)" + case let .outputStreamCreationFailed(url): + return "Failed to create an OutputStream for URL: \(url)" + case let .outputStreamFileAlreadyExists(url): + return "A file already exists at the provided URL: \(url)" + case let .outputStreamURLInvalid(url): + return "The provided OutputStream URL is invalid: \(url)" + case let .outputStreamWriteFailed(error): + return "OutputStream write failed with error: \(error)" + case let .inputStreamReadFailed(error): + return "InputStream read failed with error: \(error)" + } + } +} + +extension AFError.ResponseSerializationFailureReason { + var localizedDescription: String { + switch self { + 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 let .inputFileReadFailed(url): + return "Response could not be serialized, input file could not be read: \(url)." + case let .stringSerializationFailed(encoding): + return "String could not be serialized with encoding: \(encoding)." + case let .jsonSerializationFailed(error): + return "JSON could not be serialized because of error:\n\(error.localizedDescription)" + case let .invalidEmptyResponse(type): + return """ + Empty response could not be serialized to type: \(type). \ + Use Empty as the expected type for such responses. + """ + case let .decodingFailed(error): + return "Response could not be decoded because of error:\n\(error.localizedDescription)" + case let .customSerializationFailed(error): + return "Custom response serializer failed with 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 let .dataFileReadFailed(url): + return "Response could not be validated, data file could not be read: \(url)." + case let .missingContentType(types): + return """ + Response Content-Type was missing and acceptable content types \ + (\(types.joined(separator: ","))) do not match "*/*". + """ + case let .unacceptableContentType(acceptableTypes, responseType): + return """ + Response Content-Type "\(responseType)" does not match any acceptable types: \ + \(acceptableTypes.joined(separator: ",")). + """ + case let .unacceptableStatusCode(code): + return "Response status code was unacceptable: \(code)." + case let .customValidationFailed(error): + return "Custom response validation failed with error: \(error.localizedDescription)" + } + } +} + +extension AFError.ServerTrustFailureReason { + var localizedDescription: String { + switch self { + case let .noRequiredEvaluator(host): + return "A ServerTrustEvaluating value is required for host \(host) but none was found." + case .noCertificatesFound: + return "No certificates were found or provided for evaluation." + case .noPublicKeysFound: + return "No public keys were found or provided for evaluation." + case .policyApplicationFailed: + return "Attempting to set a SecPolicy failed." + case .settingAnchorCertificatesFailed: + return "Attempting to set the provided certificates as anchor certificates failed." + case .revocationPolicyCreationFailed: + return "Attempting to create a revocation policy failed." + case let .trustEvaluationFailed(error): + return "SecTrust evaluation failed with error: \(error?.localizedDescription ?? "None")" + case let .defaultEvaluationFailed(output): + return "Default evaluation failed for host \(output.host)." + case let .hostValidationFailed(output): + return "Host validation failed for host \(output.host)." + case let .revocationCheckFailed(output, _): + return "Revocation check failed for host \(output.host)." + case let .certificatePinningFailed(host, _, _, _): + return "Certificate pinning failed for host \(host)." + case let .publicKeyPinningFailed(host, _, _, _): + return "Public key pinning failed for host \(host)." + case let .customEvaluationFailed(error): + return "Custom trust evaluation failed with error: \(error.localizedDescription)" + } + } +} + +extension AFError.URLRequestValidationFailureReason { + var localizedDescription: String { + switch self { + case let .bodyDataInGETRequest(data): + return """ + Invalid URLRequest: Requests with GET method cannot have body data: + \(String(decoding: data, as: UTF8.self)) + """ + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift new file mode 100644 index 00000000..bcf43d02 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift @@ -0,0 +1,29 @@ +// +// Alamofire.swift +// +// Copyright (c) 2014-2021 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. +// + +/// Reference to `Session.default` for quick bootstrapping and examples. +public let AF = Session.default + +/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. +let version = "5.4.3" diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift new file mode 100644 index 00000000..280c6de9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift @@ -0,0 +1,61 @@ +// +// AlamofireExtended.swift +// +// Copyright (c) 2019 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. +// + +/// Type that acts as a generic extension point for all `AlamofireExtended` types. +public struct AlamofireExtension { + /// Stores the type or meta-type of any extended type. + public private(set) var type: ExtendedType + + /// Create an instance from the provided value. + /// + /// - Parameter type: Instance being extended. + public init(_ type: ExtendedType) { + self.type = type + } +} + +/// Protocol describing the `af` extension points for Alamofire extended types. +public protocol AlamofireExtended { + /// Type being extended. + associatedtype ExtendedType + + /// Static Alamofire extension point. + static var af: AlamofireExtension.Type { get set } + /// Instance Alamofire extension point. + var af: AlamofireExtension { get set } +} + +extension AlamofireExtended { + /// Static Alamofire extension point. + public static var af: AlamofireExtension.Type { + get { AlamofireExtension.self } + set {} + } + + /// Instance Alamofire extension point. + public var af: AlamofireExtension { + get { AlamofireExtension(self) } + set {} + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift new file mode 100644 index 00000000..9d7f8360 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift @@ -0,0 +1,404 @@ +// +// AuthenticationInterceptor.swift +// +// Copyright (c) 2020 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 `AuthenticationCredential` protocol can be used to authenticate `URLRequest`s. +/// +/// One common example of an `AuthenticationCredential` is an OAuth2 credential containing an access token used to +/// authenticate all requests on behalf of a user. The access token generally has an expiration window of 60 minutes +/// which will then require a refresh of the credential using the refresh token to generate a new access token. +public protocol AuthenticationCredential { + /// Whether the credential requires a refresh. This property should always return `true` when the credential is + /// expired. It is also wise to consider returning `true` when the credential will expire in several seconds or + /// minutes depending on the expiration window of the credential. + /// + /// For example, if the credential is valid for 60 minutes, then it would be wise to return `true` when the + /// credential is only valid for 5 minutes or less. That ensures the credential will not expire as it is passed + /// around backend services. + var requiresRefresh: Bool { get } +} + +// MARK: - + +/// Types adopting the `Authenticator` protocol can be used to authenticate `URLRequest`s with an +/// `AuthenticationCredential` as well as refresh the `AuthenticationCredential` when required. +public protocol Authenticator: AnyObject { + /// The type of credential associated with the `Authenticator` instance. + associatedtype Credential: AuthenticationCredential + + /// Applies the `Credential` to the `URLRequest`. + /// + /// In the case of OAuth2, the access token of the `Credential` would be added to the `URLRequest` as a Bearer + /// token to the `Authorization` header. + /// + /// - Parameters: + /// - credential: The `Credential`. + /// - urlRequest: The `URLRequest`. + func apply(_ credential: Credential, to urlRequest: inout URLRequest) + + /// Refreshes the `Credential` and executes the `completion` closure with the `Result` once complete. + /// + /// Refresh can be called in one of two ways. It can be called before the `Request` is actually executed due to + /// a `requiresRefresh` returning `true` during the adapt portion of the `Request` creation process. It can also + /// be triggered by a failed `Request` where the authentication server denied access due to an expired or + /// invalidated access token. + /// + /// In the case of OAuth2, this method would use the refresh token of the `Credential` to generate a new + /// `Credential` using the authentication service. Once complete, the `completion` closure should be called with + /// the new `Credential`, or the error that occurred. + /// + /// In general, if the refresh call fails with certain status codes from the authentication server (commonly a 401), + /// the refresh token in the `Credential` can no longer be used to generate a valid `Credential`. In these cases, + /// you will need to reauthenticate the user with their username / password. + /// + /// Please note, these are just general examples of common use cases. They are not meant to solve your specific + /// authentication server challenges. Please work with your authentication server team to ensure your + /// `Authenticator` logic matches their expectations. + /// + /// - Parameters: + /// - credential: The `Credential` to refresh. + /// - session: The `Session` requiring the refresh. + /// - completion: The closure to be executed once the refresh is complete. + func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result) -> Void) + + /// Determines whether the `URLRequest` failed due to an authentication error based on the `HTTPURLResponse`. + /// + /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `false` + /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then you + /// will need to work with your authentication server team to understand how to identify when this occurs. + /// + /// In the case of OAuth2, where an authentication server can invalidate credentials, you will need to inspect the + /// `HTTPURLResponse` or possibly the `Error` for when this occurs. This is commonly handled by the authentication + /// server returning a 401 status code and some additional header to indicate an OAuth2 failure occurred. + /// + /// It is very important to understand how your authentication server works to be able to implement this correctly. + /// For example, if your authentication server returns a 401 when an OAuth2 error occurs, and your downstream + /// service also returns a 401 when you are not authorized to perform that operation, how do you know which layer + /// of the backend returned you a 401? You do not want to trigger a refresh unless you know your authentication + /// server is actually the layer rejecting the request. Again, work with your authentication server team to understand + /// how to identify an OAuth2 401 error vs. a downstream 401 error to avoid endless refresh loops. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest`. + /// - response: The `HTTPURLResponse`. + /// - error: The `Error`. + /// + /// - Returns: `true` if the `URLRequest` failed due to an authentication error, `false` otherwise. + func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool + + /// Determines whether the `URLRequest` is authenticated with the `Credential`. + /// + /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `true` + /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then + /// read on. + /// + /// When an authentication server can invalidate credentials, it means that you may have a non-expired credential + /// that appears to be valid, but will be rejected by the authentication server when used. Generally when this + /// happens, a number of requests are all sent when the application is foregrounded, and all of them will be + /// rejected by the authentication server in the order they are received. The first failed request will trigger a + /// refresh internally, which will update the credential, and then retry all the queued requests with the new + /// credential. However, it is possible that some of the original requests will not return from the authentication + /// server until the refresh has completed. This is where this method comes in. + /// + /// When the authentication server rejects a credential, we need to check to make sure we haven't refreshed the + /// credential while the request was in flight. If it has already refreshed, then we don't need to trigger an + /// additional refresh. If it hasn't refreshed, then we need to refresh. + /// + /// Now that it is understood how the result of this method is used in the refresh lifecyle, let's walk through how + /// to implement it. You should return `true` in this method if the `URLRequest` is authenticated in a way that + /// matches the values in the `Credential`. In the case of OAuth2, this would mean that the Bearer token in the + /// `Authorization` header of the `URLRequest` matches the access token in the `Credential`. If it matches, then we + /// know the `Credential` was used to authenticate the `URLRequest` and should return `true`. If the Bearer token + /// did not match the access token, then you should return `false`. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest`. + /// - credential: The `Credential`. + /// + /// - Returns: `true` if the `URLRequest` is authenticated with the `Credential`, `false` otherwise. + func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool +} + +// MARK: - + +/// Represents various authentication failures that occur when using the `AuthenticationInterceptor`. All errors are +/// still vended from Alamofire as `AFError` types. The `AuthenticationError` instances will be embedded within +/// `AFError` `.requestAdaptationFailed` or `.requestRetryFailed` cases. +public enum AuthenticationError: Error { + /// The credential was missing so the request could not be authenticated. + case missingCredential + /// The credential was refreshed too many times within the `RefreshWindow`. + case excessiveRefresh +} + +// MARK: - + +/// The `AuthenticationInterceptor` class manages the queuing and threading complexity of authenticating requests. +/// It relies on an `Authenticator` type to handle the actual `URLRequest` authentication and `Credential` refresh. +public class AuthenticationInterceptor: RequestInterceptor where AuthenticatorType: Authenticator { + // MARK: Typealiases + + /// Type of credential used to authenticate requests. + public typealias Credential = AuthenticatorType.Credential + + // MARK: Helper Types + + /// Type that defines a time window used to identify excessive refresh calls. When enabled, prior to executing a + /// refresh, the `AuthenticationInterceptor` compares the timestamp history of previous refresh calls against the + /// `RefreshWindow`. If more refreshes have occurred within the refresh window than allowed, the refresh is + /// cancelled and an `AuthorizationError.excessiveRefresh` error is thrown. + public struct RefreshWindow { + /// `TimeInterval` defining the duration of the time window before the current time in which the number of + /// refresh attempts is compared against `maximumAttempts`. For example, if `interval` is 30 seconds, then the + /// `RefreshWindow` represents the past 30 seconds. If more attempts occurred in the past 30 seconds than + /// `maximumAttempts`, an `.excessiveRefresh` error will be thrown. + public let interval: TimeInterval + + /// Total refresh attempts allowed within `interval` before throwing an `.excessiveRefresh` error. + public let maximumAttempts: Int + + /// Creates a `RefreshWindow` instance from the specified `interval` and `maximumAttempts`. + /// + /// - Parameters: + /// - interval: `TimeInterval` defining the duration of the time window before the current time. + /// - maximumAttempts: The maximum attempts allowed within the `TimeInterval`. + public init(interval: TimeInterval = 30.0, maximumAttempts: Int = 5) { + self.interval = interval + self.maximumAttempts = maximumAttempts + } + } + + private struct AdaptOperation { + let urlRequest: URLRequest + let session: Session + let completion: (Result) -> Void + } + + private enum AdaptResult { + case adapt(Credential) + case doNotAdapt(AuthenticationError) + case adaptDeferred + } + + private struct MutableState { + var credential: Credential? + + var isRefreshing = false + var refreshTimestamps: [TimeInterval] = [] + var refreshWindow: RefreshWindow? + + var adaptOperations: [AdaptOperation] = [] + var requestsToRetry: [(RetryResult) -> Void] = [] + } + + // MARK: Properties + + /// The `Credential` used to authenticate requests. + public var credential: Credential? { + get { mutableState.credential } + set { mutableState.credential = newValue } + } + + let authenticator: AuthenticatorType + let queue = DispatchQueue(label: "org.alamofire.authentication.inspector") + + @Protected + private var mutableState = MutableState() + + // MARK: Initialization + + /// Creates an `AuthenticationInterceptor` instance from the specified parameters. + /// + /// A `nil` `RefreshWindow` will result in the `AuthenticationInterceptor` not checking for excessive refresh calls. + /// It is recommended to always use a `RefreshWindow` to avoid endless refresh cycles. + /// + /// - Parameters: + /// - authenticator: The `Authenticator` type. + /// - credential: The `Credential` if it exists. `nil` by default. + /// - refreshWindow: The `RefreshWindow` used to identify excessive refresh calls. `RefreshWindow()` by default. + public init(authenticator: AuthenticatorType, + credential: Credential? = nil, + refreshWindow: RefreshWindow? = RefreshWindow()) { + self.authenticator = authenticator + mutableState.credential = credential + mutableState.refreshWindow = refreshWindow + } + + // MARK: Adapt + + public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + let adaptResult: AdaptResult = $mutableState.write { mutableState in + // Queue the adapt operation if a refresh is already in place. + guard !mutableState.isRefreshing else { + let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) + mutableState.adaptOperations.append(operation) + return .adaptDeferred + } + + // Throw missing credential error is the credential is missing. + guard let credential = mutableState.credential else { + let error = AuthenticationError.missingCredential + return .doNotAdapt(error) + } + + // Queue the adapt operation and trigger refresh operation if credential requires refresh. + guard !credential.requiresRefresh else { + let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) + mutableState.adaptOperations.append(operation) + refresh(credential, for: session, insideLock: &mutableState) + return .adaptDeferred + } + + return .adapt(credential) + } + + switch adaptResult { + case let .adapt(credential): + var authenticatedRequest = urlRequest + authenticator.apply(credential, to: &authenticatedRequest) + completion(.success(authenticatedRequest)) + + case let .doNotAdapt(adaptError): + completion(.failure(adaptError)) + + case .adaptDeferred: + // No-op: adapt operation captured during refresh. + break + } + } + + // MARK: Retry + + public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { + // Do not attempt retry if there was not an original request and response from the server. + guard let urlRequest = request.request, let response = request.response else { + completion(.doNotRetry) + return + } + + // Do not attempt retry unless the `Authenticator` verifies failure was due to authentication error (i.e. 401 status code). + guard authenticator.didRequest(urlRequest, with: response, failDueToAuthenticationError: error) else { + completion(.doNotRetry) + return + } + + // Do not attempt retry if there is no credential. + guard let credential = credential else { + let error = AuthenticationError.missingCredential + completion(.doNotRetryWithError(error)) + return + } + + // Retry the request if the `Authenticator` verifies it was authenticated with a previous credential. + guard authenticator.isRequest(urlRequest, authenticatedWith: credential) else { + completion(.retry) + return + } + + $mutableState.write { mutableState in + mutableState.requestsToRetry.append(completion) + + guard !mutableState.isRefreshing else { return } + + refresh(credential, for: session, insideLock: &mutableState) + } + } + + // MARK: Refresh + + private func refresh(_ credential: Credential, for session: Session, insideLock mutableState: inout MutableState) { + guard !isRefreshExcessive(insideLock: &mutableState) else { + let error = AuthenticationError.excessiveRefresh + handleRefreshFailure(error, insideLock: &mutableState) + return + } + + mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime) + mutableState.isRefreshing = true + + // Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously. + queue.async { + self.authenticator.refresh(credential, for: session) { result in + self.$mutableState.write { mutableState in + switch result { + case let .success(credential): + self.handleRefreshSuccess(credential, insideLock: &mutableState) + case let .failure(error): + self.handleRefreshFailure(error, insideLock: &mutableState) + } + } + } + } + } + + private func isRefreshExcessive(insideLock mutableState: inout MutableState) -> Bool { + guard let refreshWindow = mutableState.refreshWindow else { return false } + + let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval + + let refreshAttemptsWithinWindow = mutableState.refreshTimestamps.reduce(into: 0) { attempts, refreshTimestamp in + guard refreshWindowMin <= refreshTimestamp else { return } + attempts += 1 + } + + let isRefreshExcessive = refreshAttemptsWithinWindow >= refreshWindow.maximumAttempts + + return isRefreshExcessive + } + + private func handleRefreshSuccess(_ credential: Credential, insideLock mutableState: inout MutableState) { + mutableState.credential = credential + + let adaptOperations = mutableState.adaptOperations + let requestsToRetry = mutableState.requestsToRetry + + mutableState.adaptOperations.removeAll() + mutableState.requestsToRetry.removeAll() + + mutableState.isRefreshing = false + + // Dispatch to queue to hop out of the mutable state lock + queue.async { + adaptOperations.forEach { self.adapt($0.urlRequest, for: $0.session, completion: $0.completion) } + requestsToRetry.forEach { $0(.retry) } + } + } + + private func handleRefreshFailure(_ error: Error, insideLock mutableState: inout MutableState) { + let adaptOperations = mutableState.adaptOperations + let requestsToRetry = mutableState.requestsToRetry + + mutableState.adaptOperations.removeAll() + mutableState.requestsToRetry.removeAll() + + mutableState.isRefreshing = false + + // Dispatch to queue to hop out of the mutable state lock + queue.async { + adaptOperations.forEach { $0.completion(.failure(error)) } + requestsToRetry.forEach { $0(.doNotRetryWithError(error)) } + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift new file mode 100644 index 00000000..b6e0d4b2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift @@ -0,0 +1,91 @@ +// +// CachedResponseHandler.swift +// +// Copyright (c) 2019 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 handles whether the data task should store the HTTP response in the cache. +public protocol CachedResponseHandler { + /// Determines whether the HTTP response should be stored in the cache. + /// + /// The `completion` closure should be passed one of three possible options: + /// + /// 1. The cached response provided by the server (this is the most common use case). + /// 2. A modified version of the cached response (you may want to modify it in some way before caching). + /// 3. A `nil` value to prevent the cached response from being stored in the cache. + /// + /// - Parameters: + /// - task: The data task whose request resulted in the cached response. + /// - response: The cached response to potentially store in the cache. + /// - completion: The closure to execute containing cached response, a modified response, or `nil`. + func dataTask(_ task: URLSessionDataTask, + willCacheResponse response: CachedURLResponse, + completion: @escaping (CachedURLResponse?) -> Void) +} + +// MARK: - + +/// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached +/// response. +public struct ResponseCacher { + /// Defines the behavior of the `ResponseCacher` type. + public enum Behavior { + /// Stores the cached response in the cache. + case cache + /// Prevents the cached response from being stored in the cache. + case doNotCache + /// Modifies the cached response before storing it in the cache. + case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) + } + + /// Returns a `ResponseCacher` with a follow `Behavior`. + public static let cache = ResponseCacher(behavior: .cache) + /// Returns a `ResponseCacher` with a do not follow `Behavior`. + public static let doNotCache = ResponseCacher(behavior: .doNotCache) + + /// The `Behavior` of the `ResponseCacher`. + public let behavior: Behavior + + /// Creates a `ResponseCacher` instance from the `Behavior`. + /// + /// - Parameter behavior: The `Behavior`. + public init(behavior: Behavior) { + self.behavior = behavior + } +} + +extension ResponseCacher: CachedResponseHandler { + public func dataTask(_ task: URLSessionDataTask, + willCacheResponse response: CachedURLResponse, + completion: @escaping (CachedURLResponse?) -> Void) { + switch behavior { + case .cache: + completion(response) + case .doNotCache: + completion(nil) + case let .modify(closure): + let response = closure(task, response) + completion(response) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift new file mode 100644 index 00000000..a1382494 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift @@ -0,0 +1,622 @@ +// +// Combine.swift +// +// Copyright (c) 2020 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 canImport(Combine) + +import Combine +import Dispatch +import Foundation + +// MARK: - DataRequest / UploadRequest + +/// A Combine `Publisher` that publishes the `DataResponse` of the provided `DataRequest`. +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public struct DataResponsePublisher: Publisher { + public typealias Output = DataResponse + public typealias Failure = Never + + private typealias Handler = (@escaping (_ response: DataResponse) -> Void) -> DataRequest + + private let request: DataRequest + private let responseHandler: Handler + + /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. + /// + /// - Parameters: + /// - request: `DataRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. + /// - serializer: `ResponseSerializer` used to produce the published `DataResponse`. + public init(_ request: DataRequest, queue: DispatchQueue, serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Creates an instance which will serialize responses using the provided `DataResponseSerializerProtocol`. + /// + /// - Parameters: + /// - request: `DataRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. + /// - serializer: `DataResponseSerializerProtocol` used to produce the published `DataResponse`. + public init(_ request: DataRequest, + queue: DispatchQueue, + serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Publishes only the `Result` of the `DataResponse` value. + /// + /// - Returns: The `AnyPublisher` publishing the `Result` value. + public func result() -> AnyPublisher, Never> { + map { $0.result }.eraseToAnyPublisher() + } + + /// Publishes the `Result` of the `DataResponse` as a single `Value` or fail with the `AFError` instance. + /// + /// - Returns: The `AnyPublisher` publishing the stream. + public func value() -> AnyPublisher { + setFailureType(to: AFError.self).flatMap { $0.result.publisher }.eraseToAnyPublisher() + } + + public func receive(subscriber: S) where S: Subscriber, DataResponsePublisher.Failure == S.Failure, DataResponsePublisher.Output == S.Input { + subscriber.receive(subscription: Inner(request: request, + responseHandler: responseHandler, + downstream: subscriber)) + } + + private final class Inner: Subscription, Cancellable + where Downstream.Input == Output { + typealias Failure = Downstream.Failure + + @Protected + private var downstream: Downstream? + private let request: DataRequest + private let responseHandler: Handler + + init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) { + self.request = request + self.responseHandler = responseHandler + self.downstream = downstream + } + + func request(_ demand: Subscribers.Demand) { + assert(demand > 0) + + guard let downstream = downstream else { return } + + self.downstream = nil + responseHandler { response in + _ = downstream.receive(response) + downstream.receive(completion: .finished) + }.resume() + } + + func cancel() { + request.cancel() + downstream = nil + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +extension DataResponsePublisher where Value == Data? { + /// Creates an instance which publishes a `DataResponse` value without serialization. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public init(_ request: DataRequest, queue: DispatchQueue) { + self.request = request + responseHandler = { request.response(queue: queue, completionHandler: $0) } + } +} + +extension DataRequest { + /// Creates a `DataResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `ResponseSerializer` used to serialize response `Data`. + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DataResponsePublisher + where Serializer.SerializedObject == T { + DataResponsePublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishData(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding + /// will be determined by the server response, falling back to the default HTTP character + /// set, `ISO-8859-1`. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishString(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default. + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyResponseMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyResponseMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance which does not serialize the response before publishing. + /// + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// + /// - Returns: The `DataResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishUnserialized(queue: DispatchQueue = .main) -> DataResponsePublisher { + DataResponsePublisher(self, queue: queue) + } +} + +// A Combine `Publisher` that publishes a sequence of `Stream` values received by the provided `DataStreamRequest`. +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public struct DataStreamPublisher: Publisher { + public typealias Output = DataStreamRequest.Stream + public typealias Failure = Never + + private typealias Handler = (@escaping DataStreamRequest.Handler) -> DataStreamRequest + + private let request: DataStreamRequest + private let streamHandler: Handler + + /// Creates an instance which will serialize responses using the provided `DataStreamSerializer`. + /// + /// - Parameters: + /// - request: `DataStreamRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `Stream` values will be published. `.main` by + /// default. + /// - serializer: `DataStreamSerializer` used to produce the published `Stream` values. + public init(_ request: DataStreamRequest, queue: DispatchQueue, serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + streamHandler = { request.responseStream(using: serializer, on: queue, stream: $0) } + } + + /// Publishes only the `Result` of the `DataStreamRequest.Stream`'s `Event`s. + /// + /// - Returns: The `AnyPublisher` publishing the `Result` value. + public func result() -> AnyPublisher, Never> { + compactMap { stream in + switch stream.event { + case let .stream(result): + return result + // If the stream has completed with an error, send the error value downstream as a `.failure`. + case let .complete(completion): + return completion.error.map(Result.failure) + } + } + .eraseToAnyPublisher() + } + + /// Publishes the streamed values of the `DataStreamRequest.Stream` as a sequence of `Value` or fail with the + /// `AFError` instance. + /// + /// - Returns: The `AnyPublisher` publishing the stream. + public func value() -> AnyPublisher { + result().setFailureType(to: AFError.self).flatMap { $0.publisher }.eraseToAnyPublisher() + } + + public func receive(subscriber: S) where S: Subscriber, DataStreamPublisher.Failure == S.Failure, DataStreamPublisher.Output == S.Input { + subscriber.receive(subscription: Inner(request: request, + streamHandler: streamHandler, + downstream: subscriber)) + } + + private final class Inner: Subscription, Cancellable + where Downstream.Input == Output { + typealias Failure = Downstream.Failure + + @Protected + private var downstream: Downstream? + private let request: DataStreamRequest + private let streamHandler: Handler + + init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) { + self.request = request + self.streamHandler = streamHandler + self.downstream = downstream + } + + func request(_ demand: Subscribers.Demand) { + assert(demand > 0) + + guard let downstream = downstream else { return } + + self.downstream = nil + streamHandler { stream in + _ = downstream.receive(stream) + if case .complete = stream.event { + downstream.receive(completion: .finished) + } + }.resume() + } + + func cancel() { + request.cancel() + downstream = nil + } + } +} + +extension DataStreamRequest { + /// Creates a `DataStreamPublisher` for this instance using the given `DataStreamSerializer` and `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `DataStreamSerializer` used to serialize the streamed `Data`. + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishStream(using serializer: Serializer, + on queue: DispatchQueue = .main) -> DataStreamPublisher { + DataStreamPublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DataStreamPublisher` for this instance which uses a `PassthroughStreamSerializer` to stream `Data` + /// unserialized. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishData(queue: DispatchQueue = .main) -> DataStreamPublisher { + publishStream(using: PassthroughStreamSerializer(), on: queue) + } + + /// Creates a `DataStreamPublisher` for this instance which uses a `StringStreamSerializer` to serialize stream + /// `Data` values into `String` values. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishString(queue: DispatchQueue = .main) -> DataStreamPublisher { + publishStream(using: StringStreamSerializer(), on: queue) + } + + /// Creates a `DataStreamPublisher` for this instance which uses a `DecodableStreamSerializer` with the provided + /// parameters to serialize stream `Data` values into the provided type. + /// + /// - Parameters: + /// - type: `Decodable` type to which to decode stream `Data`. Inferred from the context by default. + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. + /// - decoder: `DataDecoder` instance used to decode stream `Data`. `JSONDecoder()` by default. + /// - preprocessor: `DataPreprocessor` which filters incoming stream `Data` before serialization. + /// `PassthroughPreprocessor()` by default. + /// - Returns: The `DataStreamPublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + decoder: DataDecoder = JSONDecoder(), + preprocessor: DataPreprocessor = PassthroughPreprocessor()) -> DataStreamPublisher { + publishStream(using: DecodableStreamSerializer(decoder: decoder, + dataPreprocessor: preprocessor), + on: queue) + } +} + +/// A Combine `Publisher` that publishes the `DownloadResponse` of the provided `DownloadRequest`. +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public struct DownloadResponsePublisher: Publisher { + public typealias Output = DownloadResponse + public typealias Failure = Never + + private typealias Handler = (@escaping (_ response: DownloadResponse) -> Void) -> DownloadRequest + + private let request: DownloadRequest + private let responseHandler: Handler + + /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. + /// + /// - Parameters: + /// - request: `DownloadRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DownloadResponse` value will be published. `.main` by default. + /// - serializer: `ResponseSerializer` used to produce the published `DownloadResponse`. + public init(_ request: DownloadRequest, queue: DispatchQueue, serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Creates an instance which will serialize responses using the provided `DownloadResponseSerializerProtocol` value. + /// + /// - Parameters: + /// - request: `DownloadRequest` for which to publish the response. + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. + /// - serializer: `DownloadResponseSerializerProtocol` used to produce the published `DownloadResponse`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public init(_ request: DownloadRequest, + queue: DispatchQueue, + serializer: Serializer) + where Value == Serializer.SerializedObject { + self.request = request + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } + } + + /// Publishes only the `Result` of the `DownloadResponse` value. + /// + /// - Returns: The `AnyPublisher` publishing the `Result` value. + public func result() -> AnyPublisher, Never> { + map { $0.result }.eraseToAnyPublisher() + } + + /// Publishes the `Result` of the `DownloadResponse` as a single `Value` or fail with the `AFError` instance. + /// + /// - Returns: The `AnyPublisher` publishing the stream. + public func value() -> AnyPublisher { + setFailureType(to: AFError.self).flatMap { $0.result.publisher }.eraseToAnyPublisher() + } + + public func receive(subscriber: S) where S: Subscriber, DownloadResponsePublisher.Failure == S.Failure, DownloadResponsePublisher.Output == S.Input { + subscriber.receive(subscription: Inner(request: request, + responseHandler: responseHandler, + downstream: subscriber)) + } + + private final class Inner: Subscription, Cancellable + where Downstream.Input == Output { + typealias Failure = Downstream.Failure + + @Protected + private var downstream: Downstream? + private let request: DownloadRequest + private let responseHandler: Handler + + init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) { + self.request = request + self.responseHandler = responseHandler + self.downstream = downstream + } + + func request(_ demand: Subscribers.Demand) { + assert(demand > 0) + + guard let downstream = downstream else { return } + + self.downstream = nil + responseHandler { response in + _ = downstream.receive(response) + downstream.receive(completion: .finished) + }.resume() + } + + func cancel() { + request.cancel() + downstream = nil + } + } +} + +extension DownloadRequest { + /// Creates a `DownloadResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `ResponseSerializer` used to serialize the response `Data` from disk. + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher + where Serializer.SerializedObject == T { + DownloadResponsePublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DownloadResponsePublisher` for this instance using the given `DownloadResponseSerializerProtocol` and + /// `DispatchQueue`. + /// + /// - Parameters: + /// - serializer: `DownloadResponseSerializer` used to serialize the response `Data` from disk. + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher + where Serializer.SerializedObject == T { + DownloadResponsePublisher(self, queue: queue, serializer: serializer) + } + + /// Creates a `DownloadResponsePublisher` for this instance and uses a `URLResponseSerializer` to serialize the + /// response. + /// + /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishURL(queue: DispatchQueue = .main) -> DownloadResponsePublisher { + publishResponse(using: URLResponseSerializer(), on: queue) + } + + /// Creates a `DownloadResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishData(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding + /// will be determined by the server response, falling back to the default HTTP character + /// set, `ISO-8859-1`. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishString(queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + on: queue) + } + + /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the + /// response. + /// + /// - Parameters: + /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default. + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` + /// by default. + /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. + /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by + /// default. + /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of + /// status code. `[.head]` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishDecodable(type: T.Type = T.self, + queue: DispatchQueue = .main, + preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyResponseMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyResponseMethods), + on: queue) + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +extension DownloadResponsePublisher where Value == URL? { + /// Creates an instance which publishes a `DownloadResponse` value without serialization. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public init(_ request: DownloadRequest, queue: DispatchQueue) { + self.request = request + responseHandler = { request.response(queue: queue, completionHandler: $0) } + } +} + +extension DownloadRequest { + /// Creates a `DownloadResponsePublisher` for this instance which does not serialize the response before publishing. + /// + /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. + /// + /// - Returns: The `DownloadResponsePublisher`. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + public func publishUnserialized(on queue: DispatchQueue = .main) -> DownloadResponsePublisher { + DownloadResponsePublisher(self, queue: queue) + } +} + +#endif diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift new file mode 100644 index 00000000..10cd273e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift @@ -0,0 +1,37 @@ +// +// DispatchQueue+Alamofire.swift +// +// Copyright (c) 2014-2018 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 { + /// Execute the provided closure after a `TimeInterval`. + /// + /// - Parameters: + /// - delay: `TimeInterval` to delay execution. + /// - closure: Closure to execute. + func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { + asyncAfter(deadline: .now() + delay, execute: closure) + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift new file mode 100644 index 00000000..3b096712 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift @@ -0,0 +1,892 @@ +// +// EventMonitor.swift +// +// Copyright (c) 2014-2018 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 + +/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various +/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses. +public protocol EventMonitor { + /// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default. + var queue: DispatchQueue { get } + + // MARK: - URLSession Events + + // MARK: URLSessionDelegate Events + + /// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method. + func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) + + // MARK: URLSessionTaskDelegate Events + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method. + func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method. + func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method. + func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method. + func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method. + func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method. + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) + + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method. + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) + + // MARK: URLSessionDataDelegate Events + + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method. + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) + + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method. + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) + + // MARK: URLSessionDownloadDelegate Events + + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method. + func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) + + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method. + func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) + + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method. + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) + + // MARK: - Request Events + + /// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the + /// `URLRequest` will be adapted before being issued. + func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) + + /// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails. + func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) + + /// Event called when a `RequestAdapter` adapts the `Request`'s initial `URLRequest`. + func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) + + /// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`. + func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) + + /// Event called when a final `URLRequest` is created for a `Request`. + func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) + + /// Event called when a `URLSessionTask` subclass instance is created for a `Request`. + func request(_ request: Request, didCreateTask task: URLSessionTask) + + /// Event called when a `Request` receives a `URLSessionTaskMetrics` value. + func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) + + /// Event called when a `Request` fails due to an error created by Alamofire. e.g. When certificate pinning fails. + func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) + + /// Event called when a `Request`'s task completes, possibly with an error. A `Request` may receive this event + /// multiple times if it is retried. + func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) + + /// Event called when a `Request` is about to be retried. + func requestIsRetrying(_ request: Request) + + /// Event called when a `Request` finishes and response serializers are being called. + func requestDidFinish(_ request: Request) + + /// Event called when a `Request` receives a `resume` call. + func requestDidResume(_ request: Request) + + /// Event called when a `Request`'s associated `URLSessionTask` is resumed. + func request(_ request: Request, didResumeTask task: URLSessionTask) + + /// Event called when a `Request` receives a `suspend` call. + func requestDidSuspend(_ request: Request) + + /// Event called when a `Request`'s associated `URLSessionTask` is suspended. + func request(_ request: Request, didSuspendTask task: URLSessionTask) + + /// Event called when a `Request` receives a `cancel` call. + func requestDidCancel(_ request: Request) + + /// Event called when a `Request`'s associated `URLSessionTask` is cancelled. + func request(_ request: Request, didCancelTask task: URLSessionTask) + + // MARK: DataRequest Events + + /// Event called when a `DataRequest` calls a `Validation`. + func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) + + /// Event called when a `DataRequest` creates a `DataResponse` value without calling a `ResponseSerializer`. + func request(_ request: DataRequest, didParseResponse response: DataResponse) + + /// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse`. + func request(_ request: DataRequest, didParseResponse response: DataResponse) + + // MARK: DataStreamRequest Events + + /// Event called when a `DataStreamRequest` calls a `Validation` closure. + /// + /// - Parameters: + /// - request: `DataStreamRequest` which is calling the `Validation`. + /// - urlRequest: `URLRequest` of the request being validated. + /// - response: `HTTPURLResponse` of the request being validated. + /// - result: Produced `ValidationResult`. + func request(_ request: DataStreamRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + withResult result: Request.ValidationResult) + + /// Event called when a `DataStreamSerializer` produces a value from streamed `Data`. + /// + /// - Parameters: + /// - request: `DataStreamRequest` for which the value was serialized. + /// - result: `Result` of the serialization attempt. + func request(_ request: DataStreamRequest, didParseStream result: Result) + + // MARK: UploadRequest Events + + /// Event called when an `UploadRequest` creates its `Uploadable` value, indicating the type of upload it represents. + func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) + + /// Event called when an `UploadRequest` failed to create its `Uploadable` value due to an error. + func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) + + /// Event called when an `UploadRequest` provides the `InputStream` from its `Uploadable` value. This only occurs if + /// the `InputStream` does not wrap a `Data` value or file `URL`. + func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) + + // MARK: DownloadRequest Events + + /// Event called when a `DownloadRequest`'s `URLSessionDownloadTask` finishes and the temporary file has been moved. + func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) + + /// Event called when a `DownloadRequest`'s `Destination` closure is called and creates the destination URL the + /// downloaded file will be moved to. + func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) + + /// Event called when a `DownloadRequest` calls a `Validation`. + func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) + + /// Event called when a `DownloadRequest` creates a `DownloadResponse` without calling a `ResponseSerializer`. + func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) + + /// Event called when a `DownloadRequest` calls a `DownloadResponseSerializer` and creates a generic `DownloadResponse` + func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) +} + +extension EventMonitor { + /// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default. + public var queue: DispatchQueue { .main } + + // MARK: Default Implementations + + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) {} + public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) {} + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didFinishCollecting metrics: URLSessionTaskMetrics) {} + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {} + public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {} + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {} + public func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse) {} + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) {} + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) {} + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) {} + public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {} + public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {} + public func request(_ request: Request, + didAdaptInitialRequest initialRequest: URLRequest, + to adaptedRequest: URLRequest) {} + public func request(_ request: Request, + didFailToAdaptURLRequest initialRequest: URLRequest, + withError error: AFError) {} + public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {} + public func request(_ request: Request, didCreateTask task: URLSessionTask) {} + public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {} + public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {} + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {} + public func requestIsRetrying(_ request: Request) {} + public func requestDidFinish(_ request: Request) {} + public func requestDidResume(_ request: Request) {} + public func request(_ request: Request, didResumeTask task: URLSessionTask) {} + public func requestDidSuspend(_ request: Request) {} + public func request(_ request: Request, didSuspendTask task: URLSessionTask) {} + public func requestDidCancel(_ request: Request) {} + public func request(_ request: Request, didCancelTask task: URLSessionTask) {} + public func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) {} + public func request(_ request: DataRequest, didParseResponse response: DataResponse) {} + public func request(_ request: DataRequest, didParseResponse response: DataResponse) {} + public func request(_ request: DataStreamRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + withResult result: Request.ValidationResult) {} + public func request(_ request: DataStreamRequest, didParseStream result: Result) {} + public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {} + public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {} + public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {} + public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) {} + public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {} + public func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) {} + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) {} + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) {} +} + +/// An `EventMonitor` which can contain multiple `EventMonitor`s and calls their methods on their queues. +public final class CompositeEventMonitor: EventMonitor { + public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor", qos: .utility) + + let monitors: [EventMonitor] + + init(monitors: [EventMonitor]) { + self.monitors = monitors + } + + func performEvent(_ event: @escaping (EventMonitor) -> Void) { + queue.async { + for monitor in self.monitors { + monitor.queue.async { event(monitor) } + } + } + } + + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) } + } + + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge) { + performEvent { $0.urlSession(session, task: task, didReceive: challenge) } + } + + public func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) { + performEvent { + $0.urlSession(session, + task: task, + didSendBodyData: bytesSent, + totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend) + } + } + + public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { + performEvent { + $0.urlSession(session, taskNeedsNewBodyStream: task) + } + } + + public func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) { + performEvent { + $0.urlSession(session, + task: task, + willPerformHTTPRedirection: response, + newRequest: request) + } + } + + public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + performEvent { $0.urlSession(session, task: task, didFinishCollecting: metrics) } + } + + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + performEvent { $0.urlSession(session, task: task, didCompleteWithError: error) } + } + + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { + performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) } + } + + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) } + } + + public func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse) { + performEvent { $0.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) } + } + + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) { + performEvent { + $0.urlSession(session, + downloadTask: downloadTask, + didResumeAtOffset: fileOffset, + expectedTotalBytes: expectedTotalBytes) + } + } + + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) { + performEvent { + $0.urlSession(session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite) + } + } + + public func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) { + performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) } + } + + public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { + performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) } + } + + public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { + performEvent { $0.request(request, didFailToCreateURLRequestWithError: error) } + } + + public func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { + performEvent { $0.request(request, didAdaptInitialRequest: initialRequest, to: adaptedRequest) } + } + + public func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { + performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) } + } + + public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { + performEvent { $0.request(request, didCreateURLRequest: urlRequest) } + } + + public func request(_ request: Request, didCreateTask task: URLSessionTask) { + performEvent { $0.request(request, didCreateTask: task) } + } + + public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { + performEvent { $0.request(request, didGatherMetrics: metrics) } + } + + public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { + performEvent { $0.request(request, didFailTask: task, earlyWithError: error) } + } + + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { + performEvent { $0.request(request, didCompleteTask: task, with: error) } + } + + public func requestIsRetrying(_ request: Request) { + performEvent { $0.requestIsRetrying(request) } + } + + public func requestDidFinish(_ request: Request) { + performEvent { $0.requestDidFinish(request) } + } + + public func requestDidResume(_ request: Request) { + performEvent { $0.requestDidResume(request) } + } + + public func request(_ request: Request, didResumeTask task: URLSessionTask) { + performEvent { $0.request(request, didResumeTask: task) } + } + + public func requestDidSuspend(_ request: Request) { + performEvent { $0.requestDidSuspend(request) } + } + + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { + performEvent { $0.request(request, didSuspendTask: task) } + } + + public func requestDidCancel(_ request: Request) { + performEvent { $0.requestDidCancel(request) } + } + + public func request(_ request: Request, didCancelTask task: URLSessionTask) { + performEvent { $0.request(request, didCancelTask: task) } + } + + public func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) { + performEvent { $0.request(request, + didValidateRequest: urlRequest, + response: response, + data: data, + withResult: result) + } + } + + public func request(_ request: DataRequest, didParseResponse response: DataResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } + + public func request(_ request: DataRequest, didParseResponse response: DataResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } + + public func request(_ request: DataStreamRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + withResult result: Request.ValidationResult) { + performEvent { $0.request(request, + didValidateRequest: urlRequest, + response: response, + withResult: result) + } + } + + public func request(_ request: DataStreamRequest, didParseStream result: Result) { + performEvent { $0.request(request, didParseStream: result) } + } + + public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { + performEvent { $0.request(request, didCreateUploadable: uploadable) } + } + + public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { + performEvent { $0.request(request, didFailToCreateUploadableWithError: error) } + } + + public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { + performEvent { $0.request(request, didProvideInputStream: stream) } + } + + public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) { + performEvent { $0.request(request, didFinishDownloadingUsing: task, with: result) } + } + + public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { + performEvent { $0.request(request, didCreateDestinationURL: url) } + } + + public func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) { + performEvent { $0.request(request, + didValidateRequest: urlRequest, + response: response, + fileURL: fileURL, + withResult: result) } + } + + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } + + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { + performEvent { $0.request(request, didParseResponse: response) } + } +} + +/// `EventMonitor` that allows optional closures to be set to receive events. +open class ClosureEventMonitor: EventMonitor { + /// Closure called on the `urlSession(_:didBecomeInvalidWithError:)` event. + open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? + + /// Closure called on the `urlSession(_:task:didReceive:completionHandler:)`. + open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> Void)? + + /// Closure that receives `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` event. + open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? + + /// Closure called on the `urlSession(_:task:needNewBodyStream:)` event. + open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> Void)? + + /// Closure called on the `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` event. + open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> Void)? + + /// Closure called on the `urlSession(_:task:didFinishCollecting:)` event. + open var taskDidFinishCollectingMetrics: ((URLSession, URLSessionTask, URLSessionTaskMetrics) -> Void)? + + /// Closure called on the `urlSession(_:task:didCompleteWithError:)` event. + open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)? + + /// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event. + open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)? + + /// Closure that receives the `urlSession(_:dataTask:didReceive:)` event. + open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? + + /// Closure called on the `urlSession(_:dataTask:willCacheResponse:completionHandler:)` event. + open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> Void)? + + /// Closure called on the `urlSession(_:downloadTask:didFinishDownloadingTo:)` event. + open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)? + + /// Closure called on the `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` + /// event. + open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? + + /// Closure called on the `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` event. + open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? + + // MARK: - Request Events + + /// Closure called on the `request(_:didCreateInitialURLRequest:)` event. + open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)? + + /// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event. + open var requestDidFailToCreateURLRequestWithError: ((Request, AFError) -> Void)? + + /// Closure called on the `request(_:didAdaptInitialRequest:to:)` event. + open var requestDidAdaptInitialRequestToAdaptedRequest: ((Request, URLRequest, URLRequest) -> Void)? + + /// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event. + open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, AFError) -> Void)? + + /// Closure called on the `request(_:didCreateURLRequest:)` event. + open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)? + + /// Closure called on the `request(_:didCreateTask:)` event. + open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `request(_:didGatherMetrics:)` event. + open var requestDidGatherMetrics: ((Request, URLSessionTaskMetrics) -> Void)? + + /// Closure called on the `request(_:didFailTask:earlyWithError:)` event. + open var requestDidFailTaskEarlyWithError: ((Request, URLSessionTask, AFError) -> Void)? + + /// Closure called on the `request(_:didCompleteTask:with:)` event. + open var requestDidCompleteTaskWithError: ((Request, URLSessionTask, AFError?) -> Void)? + + /// Closure called on the `requestIsRetrying(_:)` event. + open var requestIsRetrying: ((Request) -> Void)? + + /// Closure called on the `requestDidFinish(_:)` event. + open var requestDidFinish: ((Request) -> Void)? + + /// Closure called on the `requestDidResume(_:)` event. + open var requestDidResume: ((Request) -> Void)? + + /// Closure called on the `request(_:didResumeTask:)` event. + open var requestDidResumeTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `requestDidSuspend(_:)` event. + open var requestDidSuspend: ((Request) -> Void)? + + /// Closure called on the `request(_:didSuspendTask:)` event. + open var requestDidSuspendTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `requestDidCancel(_:)` event. + open var requestDidCancel: ((Request) -> Void)? + + /// Closure called on the `request(_:didCancelTask:)` event. + open var requestDidCancelTask: ((Request, URLSessionTask) -> Void)? + + /// Closure called on the `request(_:didValidateRequest:response:data:withResult:)` event. + open var requestDidValidateRequestResponseDataWithResult: ((DataRequest, URLRequest?, HTTPURLResponse, Data?, Request.ValidationResult) -> Void)? + + /// Closure called on the `request(_:didParseResponse:)` event. + open var requestDidParseResponse: ((DataRequest, DataResponse) -> Void)? + + /// Closure called on the `request(_:didValidateRequest:response:withResult:)` event. + open var requestDidValidateRequestResponseWithResult: ((DataStreamRequest, URLRequest?, HTTPURLResponse, Request.ValidationResult) -> Void)? + + /// Closure called on the `request(_:didCreateUploadable:)` event. + open var requestDidCreateUploadable: ((UploadRequest, UploadRequest.Uploadable) -> Void)? + + /// Closure called on the `request(_:didFailToCreateUploadableWithError:)` event. + open var requestDidFailToCreateUploadableWithError: ((UploadRequest, AFError) -> Void)? + + /// Closure called on the `request(_:didProvideInputStream:)` event. + open var requestDidProvideInputStream: ((UploadRequest, InputStream) -> Void)? + + /// Closure called on the `request(_:didFinishDownloadingUsing:with:)` event. + open var requestDidFinishDownloadingUsingTaskWithResult: ((DownloadRequest, URLSessionTask, Result) -> Void)? + + /// Closure called on the `request(_:didCreateDestinationURL:)` event. + open var requestDidCreateDestinationURL: ((DownloadRequest, URL) -> Void)? + + /// Closure called on the `request(_:didValidateRequest:response:temporaryURL:destinationURL:withResult:)` event. + open var requestDidValidateRequestResponseFileURLWithResult: ((DownloadRequest, URLRequest?, HTTPURLResponse, URL?, Request.ValidationResult) -> Void)? + + /// Closure called on the `request(_:didParseResponse:)` event. + open var requestDidParseDownloadResponse: ((DownloadRequest, DownloadResponse) -> Void)? + + public let queue: DispatchQueue + + /// Creates an instance using the provided queue. + /// + /// - Parameter queue: `DispatchQueue` on which events will fired. `.main` by default. + public init(queue: DispatchQueue = .main) { + self.queue = queue + } + + open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + sessionDidBecomeInvalidWithError?(session, error) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) { + taskDidReceiveChallenge?(session, task, challenge) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) { + taskDidSendBodyData?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) + } + + open func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { + taskNeedNewBodyStream?(session, task) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest) { + taskWillPerformHTTPRedirection?(session, task, response, request) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + taskDidFinishCollectingMetrics?(session, task, metrics) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + taskDidComplete?(session, task, error) + } + + open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { + taskIsWaitingForConnectivity?(session, task) + } + + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + dataTaskDidReceiveData?(session, dataTask, data) + } + + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) { + dataTaskWillCacheResponse?(session, dataTask, proposedResponse) + } + + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) { + downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes) + } + + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) { + downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) + } + + open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { + downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location) + } + + // MARK: Request Events + + open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { + requestDidCreateInitialURLRequest?(request, urlRequest) + } + + open func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { + requestDidFailToCreateURLRequestWithError?(request, error) + } + + open func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { + requestDidAdaptInitialRequestToAdaptedRequest?(request, initialRequest, adaptedRequest) + } + + open func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { + requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error) + } + + open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { + requestDidCreateURLRequest?(request, urlRequest) + } + + open func request(_ request: Request, didCreateTask task: URLSessionTask) { + requestDidCreateTask?(request, task) + } + + open func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { + requestDidGatherMetrics?(request, metrics) + } + + open func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { + requestDidFailTaskEarlyWithError?(request, task, error) + } + + open func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { + requestDidCompleteTaskWithError?(request, task, error) + } + + open func requestIsRetrying(_ request: Request) { + requestIsRetrying?(request) + } + + open func requestDidFinish(_ request: Request) { + requestDidFinish?(request) + } + + open func requestDidResume(_ request: Request) { + requestDidResume?(request) + } + + public func request(_ request: Request, didResumeTask task: URLSessionTask) { + requestDidResumeTask?(request, task) + } + + open func requestDidSuspend(_ request: Request) { + requestDidSuspend?(request) + } + + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { + requestDidSuspendTask?(request, task) + } + + open func requestDidCancel(_ request: Request) { + requestDidCancel?(request) + } + + public func request(_ request: Request, didCancelTask task: URLSessionTask) { + requestDidCancelTask?(request, task) + } + + open func request(_ request: DataRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + data: Data?, + withResult result: Request.ValidationResult) { + requestDidValidateRequestResponseDataWithResult?(request, urlRequest, response, data, result) + } + + open func request(_ request: DataRequest, didParseResponse response: DataResponse) { + requestDidParseResponse?(request, response) + } + + public func request(_ request: DataStreamRequest, didValidateRequest urlRequest: URLRequest?, response: HTTPURLResponse, withResult result: Request.ValidationResult) { + requestDidValidateRequestResponseWithResult?(request, urlRequest, response, result) + } + + open func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { + requestDidCreateUploadable?(request, uploadable) + } + + open func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { + requestDidFailToCreateUploadableWithError?(request, error) + } + + open func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { + requestDidProvideInputStream?(request, stream) + } + + open func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) { + requestDidFinishDownloadingUsingTaskWithResult?(request, task, result) + } + + open func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { + requestDidCreateDestinationURL?(request, url) + } + + open func request(_ request: DownloadRequest, + didValidateRequest urlRequest: URLRequest?, + response: HTTPURLResponse, + fileURL: URL?, + withResult result: Request.ValidationResult) { + requestDidValidateRequestResponseFileURLWithResult?(request, + urlRequest, + response, + fileURL, + result) + } + + open func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { + requestDidParseDownloadResponse?(request, response) + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift new file mode 100644 index 00000000..7829fc69 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift @@ -0,0 +1,449 @@ +// +// HTTPHeaders.swift +// +// Copyright (c) 2014-2018 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 + +/// An order-preserving and case-insensitive representation of HTTP headers. +public struct HTTPHeaders { + private var headers: [HTTPHeader] = [] + + /// Creates an empty instance. + public init() {} + + /// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last + /// name and value encountered. + public init(_ headers: [HTTPHeader]) { + self.init() + + headers.forEach { update($0) } + } + + /// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name + /// and value encountered. + public init(_ dictionary: [String: String]) { + self.init() + + dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) } + } + + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. + /// + /// - Parameters: + /// - name: The `HTTPHeader` name. + /// - value: The `HTTPHeader value. + public mutating func add(name: String, value: String) { + update(HTTPHeader(name: name, value: value)) + } + + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. + /// + /// - Parameter header: The `HTTPHeader` to update or append. + public mutating func add(_ header: HTTPHeader) { + update(header) + } + + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. + /// + /// - Parameters: + /// - name: The `HTTPHeader` name. + /// - value: The `HTTPHeader value. + public mutating func update(name: String, value: String) { + update(HTTPHeader(name: name, value: value)) + } + + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. + /// + /// - Parameter header: The `HTTPHeader` to update or append. + public mutating func update(_ header: HTTPHeader) { + guard let index = headers.index(of: header.name) else { + headers.append(header) + return + } + + headers.replaceSubrange(index...index, with: [header]) + } + + /// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance. + /// + /// - Parameter name: The name of the `HTTPHeader` to remove. + public mutating func remove(name: String) { + guard let index = headers.index(of: name) else { return } + + headers.remove(at: index) + } + + /// Sort the current instance by header name, case insensitively. + public mutating func sort() { + headers.sort { $0.name.lowercased() < $1.name.lowercased() } + } + + /// Returns an instance sorted by header name. + /// + /// - Returns: A copy of the current instance sorted by name. + public func sorted() -> HTTPHeaders { + var headers = self + headers.sort() + + return headers + } + + /// Case-insensitively find a header's value by name. + /// + /// - Parameter name: The name of the header to search for, case-insensitively. + /// + /// - Returns: The value of header, if it exists. + public func value(for name: String) -> String? { + guard let index = headers.index(of: name) else { return nil } + + return headers[index].value + } + + /// Case-insensitively access the header with the given name. + /// + /// - Parameter name: The name of the header. + public subscript(_ name: String) -> String? { + get { value(for: name) } + set { + if let value = newValue { + update(name: name, value: value) + } else { + remove(name: name) + } + } + } + + /// The dictionary representation of all headers. + /// + /// This representation does not preserve the current order of the instance. + public var dictionary: [String: String] { + let namesAndValues = headers.map { ($0.name, $0.value) } + + return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last }) + } +} + +extension HTTPHeaders: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, String)...) { + self.init() + + elements.forEach { update(name: $0.0, value: $0.1) } + } +} + +extension HTTPHeaders: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: HTTPHeader...) { + self.init(elements) + } +} + +extension HTTPHeaders: Sequence { + public func makeIterator() -> IndexingIterator<[HTTPHeader]> { + headers.makeIterator() + } +} + +extension HTTPHeaders: Collection { + public var startIndex: Int { + headers.startIndex + } + + public var endIndex: Int { + headers.endIndex + } + + public subscript(position: Int) -> HTTPHeader { + headers[position] + } + + public func index(after i: Int) -> Int { + headers.index(after: i) + } +} + +extension HTTPHeaders: CustomStringConvertible { + public var description: String { + headers.map { $0.description } + .joined(separator: "\n") + } +} + +// MARK: - HTTPHeader + +/// A representation of a single HTTP header's name / value pair. +public struct HTTPHeader: Hashable { + /// Name of the header. + public let name: String + + /// Value of the header. + public let value: String + + /// Creates an instance from the given `name` and `value`. + /// + /// - Parameters: + /// - name: The name of the header. + /// - value: The value of the header. + public init(name: String, value: String) { + self.name = name + self.value = value + } +} + +extension HTTPHeader: CustomStringConvertible { + public var description: String { + "\(name): \(value)" + } +} + +extension HTTPHeader { + /// Returns an `Accept` header. + /// + /// - Parameter value: The `Accept` value. + /// - Returns: The header. + public static func accept(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept", value: value) + } + + /// Returns an `Accept-Charset` header. + /// + /// - Parameter value: The `Accept-Charset` value. + /// - Returns: The header. + public static func acceptCharset(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept-Charset", value: value) + } + + /// Returns an `Accept-Language` header. + /// + /// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages. + /// Use `HTTPHeader.defaultAcceptLanguage`. + /// + /// - Parameter value: The `Accept-Language` value. + /// + /// - Returns: The header. + public static func acceptLanguage(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept-Language", value: value) + } + + /// Returns an `Accept-Encoding` header. + /// + /// Alamofire offers a default accept encoding value that provides the most common values. Use + /// `HTTPHeader.defaultAcceptEncoding`. + /// + /// - Parameter value: The `Accept-Encoding` value. + /// + /// - Returns: The header + public static func acceptEncoding(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Accept-Encoding", value: value) + } + + /// Returns a `Basic` `Authorization` header using the `username` and `password` provided. + /// + /// - Parameters: + /// - username: The username of the header. + /// - password: The password of the header. + /// + /// - Returns: The header. + public static func authorization(username: String, password: String) -> HTTPHeader { + let credential = Data("\(username):\(password)".utf8).base64EncodedString() + + return authorization("Basic \(credential)") + } + + /// Returns a `Bearer` `Authorization` header using the `bearerToken` provided + /// + /// - Parameter bearerToken: The bearer token. + /// + /// - Returns: The header. + public static func authorization(bearerToken: String) -> HTTPHeader { + authorization("Bearer \(bearerToken)") + } + + /// Returns an `Authorization` header. + /// + /// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use + /// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use + /// `HTTPHeader.authorization(bearerToken:)`. + /// + /// - Parameter value: The `Authorization` value. + /// + /// - Returns: The header. + public static func authorization(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Authorization", value: value) + } + + /// Returns a `Content-Disposition` header. + /// + /// - Parameter value: The `Content-Disposition` value. + /// + /// - Returns: The header. + public static func contentDisposition(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Content-Disposition", value: value) + } + + /// Returns a `Content-Type` header. + /// + /// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not be necessary to manually + /// set this value. + /// + /// - Parameter value: The `Content-Type` value. + /// + /// - Returns: The header. + public static func contentType(_ value: String) -> HTTPHeader { + HTTPHeader(name: "Content-Type", value: value) + } + + /// Returns a `User-Agent` header. + /// + /// - Parameter value: The `User-Agent` value. + /// + /// - Returns: The header. + public static func userAgent(_ value: String) -> HTTPHeader { + HTTPHeader(name: "User-Agent", value: value) + } +} + +extension Array where Element == HTTPHeader { + /// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists. + func index(of name: String) -> Int? { + let lowercasedName = name.lowercased() + return firstIndex { $0.name.lowercased() == lowercasedName } + } +} + +// MARK: - Defaults + +extension HTTPHeaders { + /// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and + /// `User-Agent`. + public static let `default`: HTTPHeaders = [.defaultAcceptEncoding, + .defaultAcceptLanguage, + .defaultUserAgent] +} + +extension HTTPHeader { + /// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS + /// versions. + /// + /// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) . + public static let defaultAcceptEncoding: HTTPHeader = { + let encodings: [String] + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) { + encodings = ["br", "gzip", "deflate"] + } else { + encodings = ["gzip", "deflate"] + } + + return .acceptEncoding(encodings.qualityEncoded()) + }() + + /// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's + /// `preferredLanguages`. + /// + /// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5). + public static let defaultAcceptLanguage: HTTPHeader = { + .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded()) + }() + + /// Returns Alamofire's default `User-Agent` header. + /// + /// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3). + /// + /// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0` + public static let defaultUserAgent: HTTPHeader = { + let info = Bundle.main.infoDictionary + let executable = (info?[kCFBundleExecutableKey as String] as? String) ?? + (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ?? + "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) + #if targetEnvironment(macCatalyst) + return "macOS(Catalyst)" + #else + return "iOS" + #endif + #elseif os(watchOS) + return "watchOS" + #elseif os(tvOS) + return "tvOS" + #elseif os(macOS) + return "macOS" + #elseif os(Linux) + return "Linux" + #elseif os(Windows) + return "Windows" + #else + return "Unknown" + #endif + }() + + return "\(osName) \(versionString)" + }() + + let alamofireVersion = "Alamofire/\(version)" + + let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)" + + return .userAgent(userAgent) + }() +} + +extension Collection where Element == String { + func qualityEncoded() -> String { + enumerated().map { index, encoding in + let quality = 1.0 - (Double(index) * 0.1) + return "\(encoding);q=\(quality)" + }.joined(separator: ", ") + } +} + +// MARK: - System Type Extensions + +extension URLRequest { + /// Returns `allHTTPHeaderFields` as `HTTPHeaders`. + public var headers: HTTPHeaders { + get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() } + set { allHTTPHeaderFields = newValue.dictionary } + } +} + +extension HTTPURLResponse { + /// Returns `allHeaderFields` as `HTTPHeaders`. + public var headers: HTTPHeaders { + (allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() + } +} + +extension URLSessionConfiguration { + /// Returns `httpAdditionalHeaders` as `HTTPHeaders`. + public var headers: HTTPHeaders { + get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() } + set { httpAdditionalHeaders = newValue.dictionary } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift new file mode 100644 index 00000000..4867c1e9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift @@ -0,0 +1,54 @@ +// +// HTTPMethod.swift +// +// Copyright (c) 2014-2018 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. +// + +/// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so +/// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. +/// +/// See https://tools.ietf.org/html/rfc7231#section-4.3 +public struct HTTPMethod: RawRepresentable, Equatable, Hashable { + /// `CONNECT` method. + public static let connect = HTTPMethod(rawValue: "CONNECT") + /// `DELETE` method. + public static let delete = HTTPMethod(rawValue: "DELETE") + /// `GET` method. + public static let get = HTTPMethod(rawValue: "GET") + /// `HEAD` method. + public static let head = HTTPMethod(rawValue: "HEAD") + /// `OPTIONS` method. + public static let options = HTTPMethod(rawValue: "OPTIONS") + /// `PATCH` method. + public static let patch = HTTPMethod(rawValue: "PATCH") + /// `POST` method. + public static let post = HTTPMethod(rawValue: "POST") + /// `PUT` method. + public static let put = HTTPMethod(rawValue: "PUT") + /// `TRACE` method. + public static let trace = HTTPMethod(rawValue: "TRACE") + + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift new file mode 100644 index 00000000..d9cecef5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift @@ -0,0 +1,553 @@ +// +// MultipartFormData.swift +// +// Copyright (c) 2014-2018 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 + + enum EncodingCharacters { + static let crlf = "\r\n" + } + + enum BoundaryGenerator { + enum BoundaryType { + case initial, encapsulated, final + } + + static func randomBoundary() -> String { + let first = UInt32.random(in: UInt32.min...UInt32.max) + let second = UInt32.random(in: UInt32.min...UInt32.max) + + return String(format: "alamofire.boundary.%08x%08x", first, second) + } + + 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 Data(boundaryText.utf8) + } + } + + 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 + + /// Default memory threshold used when encoding `MultipartFormData`, in bytes. + public static let encodingMemoryThreshold: UInt64 = 10_000_000 + + /// 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 { bodyParts.reduce(0) { $0 + $1.bodyContentLength } } + + /// The boundary used to separate the body parts in the encoded form data. + public let boundary: String + + let fileManager: FileManager + + private var bodyParts: [BodyPart] + private var bodyPartError: AFError? + private let streamBufferSize: Int + + // MARK: - Lifecycle + + /// Creates an instance. + /// + /// - Parameters: + /// - fileManager: `FileManager` to use for file operations, if needed. + /// - boundary: Boundary `String` used to separate body parts. + public init(fileManager: FileManager = .default, boundary: String? = nil) { + self.fileManager = fileManager + self.boundary = boundary ?? BoundaryGenerator.randomBoundary() + 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 + // + streamBufferSize = 1024 + } + + // MARK: - Body Parts + + /// Creates a body part from the data and appends it to the instance. + /// + /// 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 + /// + /// - Parameters: + /// - data: `Data` to encoding into the instance. + /// - name: Name to associate with the `Data` in the `Content-Disposition` HTTP header. + /// - fileName: Filename to associate with the `Data` in the `Content-Disposition` HTTP header. + /// - mimeType: MIME type to associate with the data in the `Content-Type` HTTP header. + public func append(_ data: Data, withName name: String, fileName: String? = nil, mimeType: String? = nil) { + 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 instance. + /// + /// 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. + /// + /// - Parameters: + /// - fileURL: `URL` of the file whose content will be encoded into the instance. + /// - name: 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 instance. + /// + /// 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 + /// + /// - Parameters: + /// - fileURL: `URL` of the file whose content will be encoded into the instance. + /// - name: Name to associate with the file content in the `Content-Disposition` HTTP header. + /// - fileName: Filename to associate with the file content in the `Content-Disposition` HTTP header. + /// - mimeType: 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.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.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 instance. + /// + /// 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 + /// + /// - Parameters: + /// - stream: `InputStream` to encode into the instance. + /// - length: Length, in bytes, of the stream. + /// - name: Name to associate with the stream content in the `Content-Disposition` HTTP header. + /// - fileName: Filename to associate with the stream content in the `Content-Disposition` HTTP header. + /// - mimeType: 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 stream, length, and headers and appends it to the instance. + /// + /// The body part data will be encoded using the following format: + /// + /// - HTTP headers + /// - Encoded stream data + /// - Multipart form boundary + /// + /// - Parameters: + /// - stream: `InputStream` to encode into the instance. + /// - length: Length, in bytes, of the stream. + /// - headers: `HTTPHeaders` 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 appended body parts into a single `Data` value. + /// + /// - Note: 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 `writeEncodedData(to:))` method. + /// + /// - Returns: The encoded `Data`, if encoding is successful. + /// - Throws: An `AFError` if encoding encounters an error. + 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 all appended body parts to 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: File `URL` to which to write the form data. + /// - Throws: An `AFError` if encoding encounters an error. + public func writeEncodedData(to fileURL: URL) throws { + if let bodyPartError = bodyPartError { + throw bodyPartError + } + + if fileManager.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() } + + bodyParts.first?.hasInitialBoundary = true + bodyParts.last?.hasFinalBoundary = true + + for bodyPart in 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 { + let headerText = bodyPart.headers.map { "\($0.name): \($0.value)\(EncodingCharacters.crlf)" } + .joined() + + EncodingCharacters.crlf + + return Data(headerText.utf8) + } + + 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 + } + } + + guard UInt64(encoded.count) == bodyPart.bodyContentLength else { + let error = AFError.UnexpectedInputStreamLength(bytesExpected: bodyPart.bodyContentLength, + bytesRead: UInt64(encoded.count)) + throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) + } + + 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) -> HTTPHeaders { + var disposition = "form-data; name=\"\(name)\"" + if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" } + + var headers: HTTPHeaders = [.contentDisposition(disposition)] + if let mimeType = mimeType { headers.add(.contentType(mimeType)) } + + return headers + } + + // MARK: - Private - Boundary Encoding + + private func initialBoundaryData() -> Data { + BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary) + } + + private func encapsulatedBoundaryData() -> Data { + BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary) + } + + private func finalBoundaryData() -> Data { + 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) + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift new file mode 100644 index 00000000..606bd33e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift @@ -0,0 +1,89 @@ +// +// MultipartUpload.swift +// +// Copyright (c) 2014-2018 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 + +/// Internal type which encapsulates a `MultipartFormData` upload. +final class MultipartUpload { + lazy var result = Result { try build() } + + @Protected + private(set) var multipartFormData: MultipartFormData + let encodingMemoryThreshold: UInt64 + let request: URLRequestConvertible + let fileManager: FileManager + + init(encodingMemoryThreshold: UInt64, + request: URLRequestConvertible, + multipartFormData: MultipartFormData) { + self.encodingMemoryThreshold = encodingMemoryThreshold + self.request = request + fileManager = multipartFormData.fileManager + self.multipartFormData = multipartFormData + } + + func build() throws -> UploadRequest.Uploadable { + let uploadable: UploadRequest.Uploadable + if multipartFormData.contentLength < encodingMemoryThreshold { + let data = try multipartFormData.encode() + + uploadable = .data(data) + } else { + let tempDirectoryURL = fileManager.temporaryDirectory + let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") + let fileName = UUID().uuidString + let fileURL = directoryURL.appendingPathComponent(fileName) + + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + + do { + try multipartFormData.writeEncodedData(to: fileURL) + } catch { + // Cleanup after attempted write if it fails. + try? fileManager.removeItem(at: fileURL) + throw error + } + + uploadable = .file(fileURL, shouldRemove: true) + } + + return uploadable + } +} + +extension MultipartUpload: UploadConvertible { + func asURLRequest() throws -> URLRequest { + var urlRequest = try request.asURLRequest() + + $multipartFormData.read { multipartFormData in + urlRequest.headers.add(.contentType(multipartFormData.contentType)) + } + + return urlRequest + } + + func createUploadable() throws -> UploadRequest.Uploadable { + try result.get() + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift new file mode 100644 index 00000000..b97b62fa --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift @@ -0,0 +1,267 @@ +// +// NetworkReachabilityManager.swift +// +// Copyright (c) 2014-2018 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) || os(Linux)) + +import Foundation +import SystemConfiguration + +/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both cellular 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. +open class NetworkReachabilityManager { + /// Defines the various states of network reachability. + public enum NetworkReachabilityStatus { + /// It is unknown whether the network is reachable. + case unknown + /// The network is not reachable. + case notReachable + /// The network is reachable on the associated `ConnectionType`. + case reachable(ConnectionType) + + init(_ flags: SCNetworkReachabilityFlags) { + guard flags.isActuallyReachable else { self = .notReachable; return } + + var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) + + if flags.isCellular { networkStatus = .reachable(.cellular) } + + self = networkStatus + } + + /// Defines the various connection types detected by reachability flags. + public enum ConnectionType { + /// The connection type is either over Ethernet or WiFi. + case ethernetOrWiFi + /// The connection type is a cellular connection. + case cellular + } + } + + /// A closure executed when the network reachability status changes. The closure takes a single argument: the + /// network reachability status. + public typealias Listener = (NetworkReachabilityStatus) -> Void + + /// Default `NetworkReachabilityManager` for the zero address and a `listenerQueue` of `.main`. + public static let `default` = NetworkReachabilityManager() + + // MARK: - Properties + + /// Whether the network is currently reachable. + open var isReachable: Bool { isReachableOnCellular || isReachableOnEthernetOrWiFi } + + /// Whether the network is currently reachable over the cellular interface. + /// + /// - Note: Using this property to decide whether to make a high or low bandwidth request is not recommended. + /// Instead, set the `allowsCellularAccess` on any `URLRequest`s being issued. + /// + open var isReachableOnCellular: Bool { status == .reachable(.cellular) } + + /// Whether the network is currently reachable over Ethernet or WiFi interface. + open var isReachableOnEthernetOrWiFi: Bool { status == .reachable(.ethernetOrWiFi) } + + /// `DispatchQueue` on which reachability will update. + public let reachabilityQueue = DispatchQueue(label: "org.alamofire.reachabilityQueue") + + /// Flags of the current reachability type, if any. + open var flags: SCNetworkReachabilityFlags? { + var flags = SCNetworkReachabilityFlags() + + return (SCNetworkReachabilityGetFlags(reachability, &flags)) ? flags : nil + } + + /// The current network reachability status. + open var status: NetworkReachabilityStatus { + flags.map(NetworkReachabilityStatus.init) ?? .unknown + } + + /// Mutable state storage. + struct MutableState { + /// A closure executed when the network reachability status changes. + var listener: Listener? + /// `DispatchQueue` on which listeners will be called. + var listenerQueue: DispatchQueue? + /// Previously calculated status. + var previousStatus: NetworkReachabilityStatus? + } + + /// `SCNetworkReachability` instance providing notifications. + private let reachability: SCNetworkReachability + + /// Protected storage for mutable state. + @Protected + private var mutableState = MutableState() + + // MARK: - Initialization + + /// Creates an instance with the specified host. + /// + /// - Note: The `host` value must *not* contain a scheme, just the hostname. + /// + /// - Parameters: + /// - host: Host used to evaluate network reachability. Must *not* include the scheme (e.g. `https`). + public convenience init?(host: String) { + guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } + + self.init(reachability: reachability) + } + + /// Creates an 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. + public convenience init?() { + var zero = sockaddr() + zero.sa_len = UInt8(MemoryLayout.size) + zero.sa_family = sa_family_t(AF_INET) + + guard let reachability = SCNetworkReachabilityCreateWithAddress(nil, &zero) else { return nil } + + self.init(reachability: reachability) + } + + private init(reachability: SCNetworkReachability) { + self.reachability = reachability + } + + deinit { + stopListening() + } + + // MARK: - Listening + + /// Starts listening for changes in network reachability status. + /// + /// - Note: Stops and removes any existing listener. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which to call the `listener` closure. `.main` by default. + /// - listener: `Listener` closure called when reachability changes. + /// + /// - Returns: `true` if listening was started successfully, `false` otherwise. + @discardableResult + open func startListening(onQueue queue: DispatchQueue = .main, + onUpdatePerforming listener: @escaping Listener) -> Bool { + stopListening() + + $mutableState.write { state in + state.listenerQueue = queue + state.listener = listener + } + + var context = SCNetworkReachabilityContext(version: 0, + info: Unmanaged.passUnretained(self).toOpaque(), + retain: nil, + release: nil, + copyDescription: nil) + let callback: SCNetworkReachabilityCallBack = { _, flags, info in + guard let info = info else { return } + + let instance = Unmanaged.fromOpaque(info).takeUnretainedValue() + instance.notifyListener(flags) + } + + let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue) + let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context) + + // Manually call listener to give initial state, since the framework may not. + if let currentFlags = flags { + reachabilityQueue.async { + self.notifyListener(currentFlags) + } + } + + return callbackAdded && queueAdded + } + + /// Stops listening for changes in network reachability status. + open func stopListening() { + SCNetworkReachabilitySetCallback(reachability, nil, nil) + SCNetworkReachabilitySetDispatchQueue(reachability, nil) + $mutableState.write { state in + state.listener = nil + state.listenerQueue = nil + state.previousStatus = nil + } + } + + // MARK: - Internal - Listener Notification + + /// Calls the `listener` closure of the `listenerQueue` if the computed status hasn't changed. + /// + /// - Note: Should only be called from the `reachabilityQueue`. + /// + /// - Parameter flags: `SCNetworkReachabilityFlags` to use to calculate the status. + func notifyListener(_ flags: SCNetworkReachabilityFlags) { + let newStatus = NetworkReachabilityStatus(flags) + + $mutableState.write { state in + guard state.previousStatus != newStatus else { return } + + state.previousStatus = newStatus + + let listener = state.listener + state.listenerQueue?.async { listener?(newStatus) } + } + } +} + +// MARK: - + +extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} + +extension SCNetworkReachabilityFlags { + var isReachable: Bool { contains(.reachable) } + var isConnectionRequired: Bool { contains(.connectionRequired) } + var canConnectAutomatically: Bool { contains(.connectionOnDemand) || contains(.connectionOnTraffic) } + var canConnectWithoutUserInteraction: Bool { canConnectAutomatically && !contains(.interventionRequired) } + var isActuallyReachable: Bool { isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) } + var isCellular: Bool { + #if os(iOS) || os(tvOS) + return contains(.isWWAN) + #else + return false + #endif + } + + /// Human readable `String` for all states, to help with debugging. + var readableDescription: String { + let W = isCellular ? "W" : "-" + let R = isReachable ? "R" : "-" + let c = isConnectionRequired ? "c" : "-" + let t = contains(.transientConnection) ? "t" : "-" + let i = contains(.interventionRequired) ? "i" : "-" + let C = contains(.connectionOnTraffic) ? "C" : "-" + let D = contains(.connectionOnDemand) ? "D" : "-" + let l = contains(.isLocalAddress) ? "l" : "-" + let d = contains(.isDirect) ? "d" : "-" + let a = contains(.connectionAutomatic) ? "a" : "-" + + return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)\(a)" + } +} +#endif diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift new file mode 100644 index 00000000..66434b6e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift @@ -0,0 +1,115 @@ +// +// Notifications.swift +// +// Copyright (c) 2014-2018 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 { + /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`. + public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume") + /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`. + public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend") + /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`. + public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel") + /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`. + public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish") + + /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask") + /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask") + /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask") + /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`. + public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask") +} + +// MARK: - + +extension Notification { + /// The `Request` contained by the instance's `userInfo`, `nil` otherwise. + public var request: Request? { + userInfo?[String.requestKey] as? Request + } + + /// Convenience initializer for a `Notification` containing a `Request` payload. + /// + /// - Parameters: + /// - name: The name of the notification. + /// - request: The `Request` payload. + init(name: Notification.Name, request: Request) { + self.init(name: name, object: nil, userInfo: [String.requestKey: request]) + } +} + +extension NotificationCenter { + /// Convenience function for posting notifications with `Request` payloads. + /// + /// - Parameters: + /// - name: The name of the notification. + /// - request: The `Request` payload. + func postNotification(named name: Notification.Name, with request: Request) { + let notification = Notification(name: name, request: request) + post(notification) + } +} + +extension String { + /// User info dictionary key representing the `Request` associated with the notification. + fileprivate static let requestKey = "org.alamofire.notification.key.request" +} + +/// `EventMonitor` that provides Alamofire's notifications. +public final class AlamofireNotifications: EventMonitor { + public func requestDidResume(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request) + } + + public func requestDidSuspend(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request) + } + + public func requestDidCancel(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request) + } + + public func requestDidFinish(_ request: Request) { + NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request) + } + + public func request(_ request: Request, didResumeTask task: URLSessionTask) { + NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request) + } + + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { + NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request) + } + + public func request(_ request: Request, didCancelTask task: URLSessionTask) { + NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request) + } + + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { + NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request) + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift new file mode 100644 index 00000000..b06a0ccc --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift @@ -0,0 +1,49 @@ +// +// OperationQueue+Alamofire.swift +// +// Copyright (c) 2014-2018 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 OperationQueue { + /// Creates an instance using the provided parameters. + /// + /// - Parameters: + /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. + /// - maxConcurrentOperationCount: Maximum concurrent operations. + /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. + /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. + /// - name: Name for the queue. `nil` by default. + /// - startSuspended: Whether the queue starts suspended. `false` by default. + convenience init(qualityOfService: QualityOfService = .default, + maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, + underlyingQueue: DispatchQueue? = nil, + name: String? = nil, + startSuspended: Bool = false) { + self.init() + self.qualityOfService = qualityOfService + self.maxConcurrentOperationCount = maxConcurrentOperationCount + self.underlyingQueue = underlyingQueue + self.name = name + isSuspended = startSuspended + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift new file mode 100644 index 00000000..f4facbee --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift @@ -0,0 +1,184 @@ +// +// ParameterEncoder.swift +// +// Copyright (c) 2014-2018 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 encode any `Encodable` type into a `URLRequest`. +public protocol ParameterEncoder { + /// Encode the provided `Encodable` parameters into `request`. + /// + /// - Parameters: + /// - parameters: The `Encodable` parameter value. + /// - request: The `URLRequest` into which to encode the parameters. + /// + /// - Returns: A `URLRequest` with the result of the encoding. + /// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of + /// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`. + func encode(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest +} + +/// A `ParameterEncoder` that encodes types as JSON body data. +/// +/// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`. +open class JSONParameterEncoder: ParameterEncoder { + /// Returns an encoder with default parameters. + public static var `default`: JSONParameterEncoder { JSONParameterEncoder() } + + /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`. + public static var prettyPrinted: JSONParameterEncoder { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + return JSONParameterEncoder(encoder: encoder) + } + + /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`. + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + public static var sortedKeys: JSONParameterEncoder { + let encoder = JSONEncoder() + encoder.outputFormatting = .sortedKeys + + return JSONParameterEncoder(encoder: encoder) + } + + /// `JSONEncoder` used to encode parameters. + public let encoder: JSONEncoder + + /// Creates an instance with the provided `JSONEncoder`. + /// + /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default. + public init(encoder: JSONEncoder = JSONEncoder()) { + self.encoder = encoder + } + + open func encode(_ parameters: Parameters?, + into request: URLRequest) throws -> URLRequest { + guard let parameters = parameters else { return request } + + var request = request + + do { + let data = try encoder.encode(parameters) + request.httpBody = data + if request.headers["Content-Type"] == nil { + request.headers.update(.contentType("application/json")) + } + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return request + } +} + +/// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending +/// on the `Destination` set. +/// +/// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to +/// `application/x-www-form-urlencoded; charset=utf-8`. +/// +/// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer. +open class URLEncodedFormParameterEncoder: ParameterEncoder { + /// Defines where the URL-encoded string should be set for each `URLRequest`. + public enum Destination { + /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request. + /// Sets it to the `httpBody` for all other methods. + case methodDependent + /// Applies the encoded query string to any existing query string from the `URLRequest`. + case queryString + /// Applies the encoded query string to the `httpBody` of the `URLRequest`. + case httpBody + + /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`. + /// + /// - Parameter method: The `HTTPMethod`. + /// + /// - Returns: Whether the URL-encoded string should be applied to a `URL`. + func encodesParametersInURL(for method: HTTPMethod) -> Bool { + switch self { + case .methodDependent: return [.get, .head, .delete].contains(method) + case .queryString: return true + case .httpBody: return false + } + } + } + + /// Returns an encoder with default parameters. + public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() } + + /// The `URLEncodedFormEncoder` to use. + public let encoder: URLEncodedFormEncoder + + /// The `Destination` for the URL-encoded string. + public let destination: Destination + + /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value. + /// + /// - Parameters: + /// - encoder: The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default. + /// - destination: The `Destination`. `.methodDependent` by default. + public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) { + self.encoder = encoder + self.destination = destination + } + + open func encode(_ parameters: Parameters?, + into request: URLRequest) throws -> URLRequest { + guard let parameters = parameters else { return request } + + var request = request + + guard let url = request.url else { + throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) + } + + guard let method = request.method else { + let rawValue = request.method?.rawValue ?? "nil" + throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue))) + } + + if destination.encodesParametersInURL(for: method), + var components = URLComponents(url: url, resolvingAgainstBaseURL: false) { + let query: String = try Result { try encoder.encode(parameters) } + .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() + let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands() + components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString + + guard let newURL = components.url else { + throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) + } + + request.url = newURL + } else { + if request.headers["Content-Type"] == nil { + request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) + } + + request.httpBody = try Result { try encoder.encode(parameters) } + .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() + } + + return request + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift new file mode 100644 index 00000000..6e72604c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift @@ -0,0 +1,317 @@ +// +// ParameterEncoding.swift +// +// Copyright (c) 2014-2018 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 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 `URLRequest` by encoding parameters and applying them on the passed request. + /// + /// - Parameters: + /// - urlRequest: `URLRequestConvertible` value onto which parameters will be encoded. + /// - parameters: `Parameters` to encode onto the request. + /// + /// - Returns: The encoded `URLRequest`. + /// - Throws: Any `Error` produced during parameter encoding. + 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`. +/// +/// There is no published specification for how to encode collection types. By default 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`) is used. Optionally, `ArrayEncoding` can be used to omit the +/// square brackets appended to array keys. +/// +/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode +/// `true` as 1 and `false` as 0. +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. + public enum Destination { + /// 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. + case methodDependent + /// Sets or appends encoded query string result to existing query string. + case queryString + /// Sets encoded query string result as the HTTP body of the URL request. + case httpBody + + func encodesParametersInURL(for method: HTTPMethod) -> Bool { + switch self { + case .methodDependent: return [.get, .head, .delete].contains(method) + case .queryString: return true + case .httpBody: return false + } + } + } + + /// Configures how `Array` parameters are encoded. + public enum ArrayEncoding { + /// An empty set of square brackets is appended to the key for every value. This is the default behavior. + case brackets + /// No brackets are appended. The key is encoded as is. + case noBrackets + + func encode(key: String) -> String { + switch self { + case .brackets: + return "\(key)[]" + case .noBrackets: + return key + } + } + } + + /// Configures how `Bool` parameters are encoded. + public enum BoolEncoding { + /// Encode `true` as `1` and `false` as `0`. This is the default behavior. + case numeric + /// Encode `true` and `false` as string literals. + case literal + + func encode(value: Bool) -> String { + switch self { + case .numeric: + return value ? "1" : "0" + case .literal: + return value ? "true" : "false" + } + } + } + + // MARK: Properties + + /// Returns a default `URLEncoding` instance with a `.methodDependent` destination. + public static var `default`: URLEncoding { URLEncoding() } + + /// Returns a `URLEncoding` instance with a `.queryString` destination. + public static var queryString: URLEncoding { URLEncoding(destination: .queryString) } + + /// Returns a `URLEncoding` instance with an `.httpBody` destination. + public static var httpBody: URLEncoding { URLEncoding(destination: .httpBody) } + + /// The destination defining where the encoded query string is to be applied to the URL request. + public let destination: Destination + + /// The encoding to use for `Array` parameters. + public let arrayEncoding: ArrayEncoding + + /// The encoding to use for `Bool` parameters. + public let boolEncoding: BoolEncoding + + // MARK: Initialization + + /// Creates an instance using the specified parameters. + /// + /// - Parameters: + /// - destination: `Destination` defining where the encoded query string will be applied. `.methodDependent` by + /// default. + /// - arrayEncoding: `ArrayEncoding` to use. `.brackets` by default. + /// - boolEncoding: `BoolEncoding` to use. `.numeric` by default. + public init(destination: Destination = .methodDependent, + arrayEncoding: ArrayEncoding = .brackets, + boolEncoding: BoolEncoding = .numeric) { + self.destination = destination + self.arrayEncoding = arrayEncoding + self.boolEncoding = boolEncoding + } + + // MARK: Encoding + + 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 = urlRequest.method, destination.encodesParametersInURL(for: 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.headers["Content-Type"] == nil { + urlRequest.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) + } + + urlRequest.httpBody = Data(query(parameters).utf8) + } + + return urlRequest + } + + /// Creates a percent-escaped, URL encoded query string components from the given key-value pair recursively. + /// + /// - Parameters: + /// - key: Key of the query component. + /// - value: 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)] = [] + switch value { + case let dictionary as [String: Any]: + for (nestedKey, value) in dictionary { + components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) + } + case let array as [Any]: + for value in array { + components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value) + } + case let number as NSNumber: + if number.isBool { + components.append((escape(key), escape(boolEncoding.encode(value: number.boolValue)))) + } else { + components.append((escape(key), escape("\(number)"))) + } + case let bool as Bool: + components.append((escape(key), escape(boolEncoding.encode(value: bool)))) + default: + components.append((escape(key), escape("\(value)"))) + } + return components + } + + /// Creates a percent-escaped string following RFC 3986 for a query string key or value. + /// + /// - Parameter string: `String` to be percent-escaped. + /// + /// - Returns: The percent-escaped `String`. + public func escape(_ string: String) -> String { + string.addingPercentEncoding(withAllowedCharacters: .afURLQueryAllowed) ?? string + } + + private func query(_ parameters: [String: Any]) -> 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: "&") + } +} + +// 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 { JSONEncoding() } + + /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. + public static var prettyPrinted: JSONEncoding { JSONEncoding(options: .prettyPrinted) } + + /// The options for writing the parameters as JSON data. + public let options: JSONSerialization.WritingOptions + + // MARK: Initialization + + /// Creates an instance using the specified `WritingOptions`. + /// + /// - Parameter options: `JSONSerialization.WritingOptions` to use. + public init(options: JSONSerialization.WritingOptions = []) { + self.options = options + } + + // MARK: Encoding + + 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.headers["Content-Type"] == nil { + urlRequest.headers.update(.contentType("application/json")) + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return urlRequest + } + + /// Encodes any JSON compatible object into a `URLRequest`. + /// + /// - Parameters: + /// - urlRequest: `URLRequestConvertible` value into which the object will be encoded. + /// - jsonObject: `Any` value (must be JSON compatible` to be encoded into the `URLRequest`. `nil` by default. + /// + /// - Returns: The encoded `URLRequest`. + /// - Throws: Any `Error` produced during encoding. + 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.headers["Content-Type"] == nil { + urlRequest.headers.update(.contentType("application/json")) + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return urlRequest + } +} + +// MARK: - + +extension NSNumber { + fileprivate var isBool: Bool { + // Use Obj-C type encoding to check whether the underlying type is a `Bool`, as it's guaranteed as part of + // swift-corelibs-foundation, per [this discussion on the Swift forums](https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553/22). + String(cString: objCType) == "c" + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift new file mode 100644 index 00000000..6288205e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift @@ -0,0 +1,224 @@ +// +// Protected.swift +// +// Copyright (c) 2014-2020 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 + +private protocol Lock { + func lock() + func unlock() +} + +extension Lock { + /// Executes a closure returning a value while acquiring the lock. + /// + /// - Parameter closure: The closure to run. + /// + /// - Returns: The value the closure generated. + func around(_ closure: () -> T) -> T { + lock(); defer { unlock() } + return closure() + } + + /// Execute a closure while acquiring the lock. + /// + /// - Parameter closure: The closure to run. + func around(_ closure: () -> Void) { + lock(); defer { unlock() } + closure() + } +} + +#if os(Linux) +/// A `pthread_mutex_t` wrapper. +final class MutexLock: Lock { + private var mutex: UnsafeMutablePointer + + init() { + mutex = .allocate(capacity: 1) + + var attr = pthread_mutexattr_t() + pthread_mutexattr_init(&attr) + pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK)) + + let error = pthread_mutex_init(mutex, &attr) + precondition(error == 0, "Failed to create pthread_mutex") + } + + deinit { + let error = pthread_mutex_destroy(mutex) + precondition(error == 0, "Failed to destroy pthread_mutex") + } + + fileprivate func lock() { + let error = pthread_mutex_lock(mutex) + precondition(error == 0, "Failed to lock pthread_mutex") + } + + fileprivate func unlock() { + let error = pthread_mutex_unlock(mutex) + precondition(error == 0, "Failed to unlock pthread_mutex") + } +} +#endif + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +/// An `os_unfair_lock` wrapper. +final class UnfairLock: Lock { + private let unfairLock: os_unfair_lock_t + + init() { + unfairLock = .allocate(capacity: 1) + unfairLock.initialize(to: os_unfair_lock()) + } + + deinit { + unfairLock.deinitialize(count: 1) + unfairLock.deallocate() + } + + fileprivate func lock() { + os_unfair_lock_lock(unfairLock) + } + + fileprivate func unlock() { + os_unfair_lock_unlock(unfairLock) + } +} +#endif + +/// A thread-safe wrapper around a value. +@propertyWrapper +@dynamicMemberLookup +final class Protected { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + private let lock = UnfairLock() + #elseif os(Linux) + private let lock = MutexLock() + #endif + private var value: T + + init(_ value: T) { + self.value = value + } + + /// The contained value. Unsafe for anything more than direct read or write. + var wrappedValue: T { + get { lock.around { value } } + set { lock.around { value = newValue } } + } + + var projectedValue: Protected { self } + + init(wrappedValue: T) { + value = wrappedValue + } + + /// Synchronously read or transform the contained value. + /// + /// - Parameter closure: The closure to execute. + /// + /// - Returns: The return value of the closure passed. + func read(_ closure: (T) -> U) -> U { + lock.around { closure(self.value) } + } + + /// Synchronously modify the protected value. + /// + /// - Parameter closure: The closure to execute. + /// + /// - Returns: The modified value. + @discardableResult + func write(_ closure: (inout T) -> U) -> U { + lock.around { closure(&self.value) } + } + + subscript(dynamicMember keyPath: WritableKeyPath) -> Property { + get { lock.around { value[keyPath: keyPath] } } + set { lock.around { value[keyPath: keyPath] = newValue } } + } +} + +extension Protected where T: RangeReplaceableCollection { + /// Adds a new element to the end of this protected collection. + /// + /// - Parameter newElement: The `Element` to append. + func append(_ newElement: T.Element) { + write { (ward: inout T) in + ward.append(newElement) + } + } + + /// Adds the elements of a sequence to the end of this protected collection. + /// + /// - Parameter newElements: The `Sequence` to append. + func append(contentsOf newElements: S) where S.Element == T.Element { + write { (ward: inout T) in + ward.append(contentsOf: newElements) + } + } + + /// Add the elements of a collection to the end of the protected collection. + /// + /// - Parameter newElements: The `Collection` to append. + func append(contentsOf newElements: C) where C.Element == T.Element { + write { (ward: inout T) in + ward.append(contentsOf: newElements) + } + } +} + +extension Protected where T == Data? { + /// Adds the contents of a `Data` value to the end of the protected `Data`. + /// + /// - Parameter data: The `Data` to be appended. + func append(_ data: Data) { + write { (ward: inout T) in + ward?.append(data) + } + } +} + +extension Protected where T == Request.MutableState { + /// Attempts to transition to the passed `State`. + /// + /// - Parameter state: The `State` to attempt transition to. + /// + /// - Returns: Whether the transition occurred. + func attemptToTransitionTo(_ state: Request.State) -> Bool { + lock.around { + guard value.state.canTransitionTo(state) else { return false } + + value.state = state + + return true + } + } + + /// Perform a closure while locked with the provided `Request.State`. + /// + /// - Parameter perform: The closure to perform while locked. + func withState(perform: (Request.State) -> Void) { + lock.around { perform(value.state) } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift new file mode 100644 index 00000000..b6c069ca --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift @@ -0,0 +1,95 @@ +// +// RedirectHandler.swift +// +// Copyright (c) 2014-2018 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 handles how an HTTP redirect response from a remote server should be redirected to the new request. +public protocol RedirectHandler { + /// Determines how the HTTP redirect response should be redirected to the new request. + /// + /// The `completion` closure should be passed one of three possible options: + /// + /// 1. The new request specified by the redirect (this is the most common use case). + /// 2. A modified version of the new request (you may want to route it somewhere else). + /// 3. A `nil` value to deny the redirect request and return the body of the redirect response. + /// + /// - Parameters: + /// - task: The `URLSessionTask` whose request resulted in a redirect. + /// - request: The `URLRequest` to the new location specified by the redirect response. + /// - response: The `HTTPURLResponse` containing the server's response to the original request. + /// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`. + func task(_ task: URLSessionTask, + willBeRedirectedTo request: URLRequest, + for response: HTTPURLResponse, + completion: @escaping (URLRequest?) -> Void) +} + +// MARK: - + +/// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect. +public struct Redirector { + /// Defines the behavior of the `Redirector` type. + public enum Behavior { + /// Follow the redirect as defined in the response. + case follow + /// Do not follow the redirect defined in the response. + case doNotFollow + /// Modify the redirect request defined in the response. + case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) + } + + /// Returns a `Redirector` with a `.follow` `Behavior`. + public static let follow = Redirector(behavior: .follow) + /// Returns a `Redirector` with a `.doNotFollow` `Behavior`. + public static let doNotFollow = Redirector(behavior: .doNotFollow) + + /// The `Behavior` of the `Redirector`. + public let behavior: Behavior + + /// Creates a `Redirector` instance from the `Behavior`. + /// + /// - Parameter behavior: The `Behavior`. + public init(behavior: Behavior) { + self.behavior = behavior + } +} + +// MARK: - + +extension Redirector: RedirectHandler { + public func task(_ task: URLSessionTask, + willBeRedirectedTo request: URLRequest, + for response: HTTPURLResponse, + completion: @escaping (URLRequest?) -> Void) { + switch behavior { + case .follow: + completion(request) + case .doNotFollow: + completion(nil) + case let .modify(closure): + let request = closure(task, request, response) + completion(request) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift new file mode 100644 index 00000000..849f8784 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift @@ -0,0 +1,1882 @@ +// +// Request.swift +// +// Copyright (c) 2014-2020 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 + +/// `Request` is the common superclass of all Alamofire request types and provides common state, delegate, and callback +/// handling. +public class Request { + /// State of the `Request`, with managed transitions between states set when calling `resume()`, `suspend()`, or + /// `cancel()` on the `Request`. + public enum State { + /// Initial state of the `Request`. + case initialized + /// `State` set when `resume()` is called. Any tasks created for the `Request` will have `resume()` called on + /// them in this state. + case resumed + /// `State` set when `suspend()` is called. Any tasks created for the `Request` will have `suspend()` called on + /// them in this state. + case suspended + /// `State` set when `cancel()` is called. Any tasks created for the `Request` will have `cancel()` called on + /// them. Unlike `resumed` or `suspended`, once in the `cancelled` state, the `Request` can no longer transition + /// to any other state. + case cancelled + /// `State` set when all response serialization completion closures have been cleared on the `Request` and + /// enqueued on their respective queues. + case finished + + /// Determines whether `self` can be transitioned to the provided `State`. + func canTransitionTo(_ state: State) -> Bool { + switch (self, state) { + case (.initialized, _): + return true + case (_, .initialized), (.cancelled, _), (.finished, _): + return false + case (.resumed, .cancelled), (.suspended, .cancelled), (.resumed, .suspended), (.suspended, .resumed): + return true + case (.suspended, .suspended), (.resumed, .resumed): + return false + case (_, .finished): + return true + } + } + } + + // MARK: - Initial State + + /// `UUID` providing a unique identifier for the `Request`, used in the `Hashable` and `Equatable` conformances. + public let id: UUID + /// The serial queue for all internal async actions. + public let underlyingQueue: DispatchQueue + /// The queue used for all serialization actions. By default it's a serial queue that targets `underlyingQueue`. + public let serializationQueue: DispatchQueue + /// `EventMonitor` used for event callbacks. + public let eventMonitor: EventMonitor? + /// The `Request`'s interceptor. + public let interceptor: RequestInterceptor? + /// The `Request`'s delegate. + public private(set) weak var delegate: RequestDelegate? + + // MARK: - Mutable State + + /// Type encapsulating all mutable state that may need to be accessed from anything other than the `underlyingQueue`. + struct MutableState { + /// State of the `Request`. + var state: State = .initialized + /// `ProgressHandler` and `DispatchQueue` provided for upload progress callbacks. + var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? + /// `ProgressHandler` and `DispatchQueue` provided for download progress callbacks. + var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? + /// `RedirectHandler` provided for to handle request redirection. + var redirectHandler: RedirectHandler? + /// `CachedResponseHandler` provided to handle response caching. + var cachedResponseHandler: CachedResponseHandler? + /// Queue and closure called when the `Request` is able to create a cURL description of itself. + var cURLHandler: (queue: DispatchQueue, handler: (String) -> Void)? + /// Queue and closure called when the `Request` creates a `URLRequest`. + var urlRequestHandler: (queue: DispatchQueue, handler: (URLRequest) -> Void)? + /// Queue and closure called when the `Request` creates a `URLSessionTask`. + var urlSessionTaskHandler: (queue: DispatchQueue, handler: (URLSessionTask) -> Void)? + /// Response serialization closures that handle response parsing. + var responseSerializers: [() -> Void] = [] + /// Response serialization completion closures executed once all response serializers are complete. + var responseSerializerCompletions: [() -> Void] = [] + /// Whether response serializer processing is finished. + var responseSerializerProcessingFinished = false + /// `URLCredential` used for authentication challenges. + var credential: URLCredential? + /// All `URLRequest`s created by Alamofire on behalf of the `Request`. + var requests: [URLRequest] = [] + /// All `URLSessionTask`s created by Alamofire on behalf of the `Request`. + var tasks: [URLSessionTask] = [] + /// All `URLSessionTaskMetrics` values gathered by Alamofire on behalf of the `Request`. Should correspond + /// exactly the the `tasks` created. + var metrics: [URLSessionTaskMetrics] = [] + /// Number of times any retriers provided retried the `Request`. + var retryCount = 0 + /// Final `AFError` for the `Request`, whether from various internal Alamofire calls or as a result of a `task`. + var error: AFError? + /// Whether the instance has had `finish()` called and is running the serializers. Should be replaced with a + /// representation in the state machine in the future. + var isFinishing = false + } + + /// Protected `MutableState` value that provides thread-safe access to state values. + @Protected + fileprivate var mutableState = MutableState() + + /// `State` of the `Request`. + public var state: State { mutableState.state } + /// Returns whether `state` is `.initialized`. + public var isInitialized: Bool { state == .initialized } + /// Returns whether `state is `.resumed`. + public var isResumed: Bool { state == .resumed } + /// Returns whether `state` is `.suspended`. + public var isSuspended: Bool { state == .suspended } + /// Returns whether `state` is `.cancelled`. + public var isCancelled: Bool { state == .cancelled } + /// Returns whether `state` is `.finished`. + public var isFinished: Bool { state == .finished } + + // MARK: Progress + + /// Closure type executed when monitoring the upload or download progress of a request. + public typealias ProgressHandler = (Progress) -> Void + + /// `Progress` of the upload of the body of the executed `URLRequest`. Reset to `0` if the `Request` is retried. + public let uploadProgress = Progress(totalUnitCount: 0) + /// `Progress` of the download of any response data. Reset to `0` if the `Request` is retried. + public let downloadProgress = Progress(totalUnitCount: 0) + /// `ProgressHandler` called when `uploadProgress` is updated, on the provided `DispatchQueue`. + private var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? { + get { mutableState.uploadProgressHandler } + set { mutableState.uploadProgressHandler = newValue } + } + + /// `ProgressHandler` called when `downloadProgress` is updated, on the provided `DispatchQueue`. + fileprivate var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? { + get { mutableState.downloadProgressHandler } + set { mutableState.downloadProgressHandler = newValue } + } + + // MARK: Redirect Handling + + /// `RedirectHandler` set on the instance. + public private(set) var redirectHandler: RedirectHandler? { + get { mutableState.redirectHandler } + set { mutableState.redirectHandler = newValue } + } + + // MARK: Cached Response Handling + + /// `CachedResponseHandler` set on the instance. + public private(set) var cachedResponseHandler: CachedResponseHandler? { + get { mutableState.cachedResponseHandler } + set { mutableState.cachedResponseHandler = newValue } + } + + // MARK: URLCredential + + /// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods. + public private(set) var credential: URLCredential? { + get { mutableState.credential } + set { mutableState.credential = newValue } + } + + // MARK: Validators + + /// `Validator` callback closures that store the validation calls enqueued. + @Protected + fileprivate var validators: [() -> Void] = [] + + // MARK: URLRequests + + /// All `URLRequests` created on behalf of the `Request`, including original and adapted requests. + public var requests: [URLRequest] { mutableState.requests } + /// First `URLRequest` created on behalf of the `Request`. May not be the first one actually executed. + public var firstRequest: URLRequest? { requests.first } + /// Last `URLRequest` created on behalf of the `Request`. + public var lastRequest: URLRequest? { requests.last } + /// Current `URLRequest` created on behalf of the `Request`. + public var request: URLRequest? { lastRequest } + + /// `URLRequest`s from all of the `URLSessionTask`s executed on behalf of the `Request`. May be different from + /// `requests` due to `URLSession` manipulation. + public var performedRequests: [URLRequest] { $mutableState.read { $0.tasks.compactMap { $0.currentRequest } } } + + // MARK: HTTPURLResponse + + /// `HTTPURLResponse` received from the server, if any. If the `Request` was retried, this is the response of the + /// last `URLSessionTask`. + public var response: HTTPURLResponse? { lastTask?.response as? HTTPURLResponse } + + // MARK: Tasks + + /// All `URLSessionTask`s created on behalf of the `Request`. + public var tasks: [URLSessionTask] { mutableState.tasks } + /// First `URLSessionTask` created on behalf of the `Request`. + public var firstTask: URLSessionTask? { tasks.first } + /// Last `URLSessionTask` crated on behalf of the `Request`. + public var lastTask: URLSessionTask? { tasks.last } + /// Current `URLSessionTask` created on behalf of the `Request`. + public var task: URLSessionTask? { lastTask } + + // MARK: Metrics + + /// All `URLSessionTaskMetrics` gathered on behalf of the `Request`. Should correspond to the `tasks` created. + public var allMetrics: [URLSessionTaskMetrics] { mutableState.metrics } + /// First `URLSessionTaskMetrics` gathered on behalf of the `Request`. + public var firstMetrics: URLSessionTaskMetrics? { allMetrics.first } + /// Last `URLSessionTaskMetrics` gathered on behalf of the `Request`. + public var lastMetrics: URLSessionTaskMetrics? { allMetrics.last } + /// Current `URLSessionTaskMetrics` gathered on behalf of the `Request`. + public var metrics: URLSessionTaskMetrics? { lastMetrics } + + // MARK: Retry Count + + /// Number of times the `Request` has been retried. + public var retryCount: Int { mutableState.retryCount } + + // MARK: Error + + /// `Error` returned from Alamofire internally, from the network request directly, or any validators executed. + public fileprivate(set) var error: AFError? { + get { mutableState.error } + set { mutableState.error = newValue } + } + + /// Default initializer for the `Request` superclass. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. + init(id: UUID = UUID(), + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.id = id + self.underlyingQueue = underlyingQueue + self.serializationQueue = serializationQueue + self.eventMonitor = eventMonitor + self.interceptor = interceptor + self.delegate = delegate + } + + // MARK: - Internal Event API + + // All API must be called from underlyingQueue. + + /// Called when an initial `URLRequest` has been created on behalf of the instance. If a `RequestAdapter` is active, + /// the `URLRequest` will be adapted before being issued. + /// + /// - Parameter request: The `URLRequest` created. + func didCreateInitialURLRequest(_ request: URLRequest) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + $mutableState.write { $0.requests.append(request) } + + eventMonitor?.request(self, didCreateInitialURLRequest: request) + } + + /// Called when initial `URLRequest` creation has failed, typically through a `URLRequestConvertible`. + /// + /// - Note: Triggers retry. + /// + /// - Parameter error: `AFError` thrown from the failed creation. + func didFailToCreateURLRequest(with error: AFError) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = error + + eventMonitor?.request(self, didFailToCreateURLRequestWithError: error) + + callCURLHandlerIfNecessary() + + retryOrFinish(error: error) + } + + /// Called when a `RequestAdapter` has successfully adapted a `URLRequest`. + /// + /// - Parameters: + /// - initialRequest: The `URLRequest` that was adapted. + /// - adaptedRequest: The `URLRequest` returned by the `RequestAdapter`. + func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + $mutableState.write { $0.requests.append(adaptedRequest) } + + eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest) + } + + /// Called when a `RequestAdapter` fails to adapt a `URLRequest`. + /// + /// - Note: Triggers retry. + /// + /// - Parameters: + /// - request: The `URLRequest` the adapter was called with. + /// - error: The `AFError` returned by the `RequestAdapter`. + func didFailToAdaptURLRequest(_ request: URLRequest, withError error: AFError) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = error + + eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error) + + callCURLHandlerIfNecessary() + + retryOrFinish(error: error) + } + + /// Final `URLRequest` has been created for the instance. + /// + /// - Parameter request: The `URLRequest` created. + func didCreateURLRequest(_ request: URLRequest) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + $mutableState.read { state in + state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) } + } + + eventMonitor?.request(self, didCreateURLRequest: request) + + callCURLHandlerIfNecessary() + } + + /// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`. + private func callCURLHandlerIfNecessary() { + $mutableState.write { mutableState in + guard let cURLHandler = mutableState.cURLHandler else { return } + + cURLHandler.queue.async { cURLHandler.handler(self.cURLDescription()) } + + mutableState.cURLHandler = nil + } + } + + /// Called when a `URLSessionTask` is created on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` created. + func didCreateTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + $mutableState.write { state in + state.tasks.append(task) + + guard let urlSessionTaskHandler = state.urlSessionTaskHandler else { return } + + urlSessionTaskHandler.queue.async { urlSessionTaskHandler.handler(task) } + } + + eventMonitor?.request(self, didCreateTask: task) + } + + /// Called when resumption is completed. + func didResume() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.requestDidResume(self) + } + + /// Called when a `URLSessionTask` is resumed on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` resumed. + func didResumeTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.request(self, didResumeTask: task) + } + + /// Called when suspension is completed. + func didSuspend() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.requestDidSuspend(self) + } + + /// Called when a `URLSessionTask` is suspended on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` suspended. + func didSuspendTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.request(self, didSuspendTask: task) + } + + /// Called when cancellation is completed, sets `error` to `AFError.explicitlyCancelled`. + func didCancel() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + error = error ?? AFError.explicitlyCancelled + + eventMonitor?.requestDidCancel(self) + } + + /// Called when a `URLSessionTask` is cancelled on behalf of the instance. + /// + /// - Parameter task: The `URLSessionTask` cancelled. + func didCancelTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + eventMonitor?.request(self, didCancelTask: task) + } + + /// Called when a `URLSessionTaskMetrics` value is gathered on behalf of the instance. + /// + /// - Parameter metrics: The `URLSessionTaskMetrics` gathered. + func didGatherMetrics(_ metrics: URLSessionTaskMetrics) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + $mutableState.write { $0.metrics.append(metrics) } + + eventMonitor?.request(self, didGatherMetrics: metrics) + } + + /// Called when a `URLSessionTask` fails before it is finished, typically during certificate pinning. + /// + /// - Parameters: + /// - task: The `URLSessionTask` which failed. + /// - error: The early failure `AFError`. + func didFailTask(_ task: URLSessionTask, earlyWithError error: AFError) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = error + + // Task will still complete, so didCompleteTask(_:with:) will handle retry. + eventMonitor?.request(self, didFailTask: task, earlyWithError: error) + } + + /// Called when a `URLSessionTask` completes. All tasks will eventually call this method. + /// + /// - Note: Response validation is synchronously triggered in this step. + /// + /// - Parameters: + /// - task: The `URLSessionTask` which completed. + /// - error: The `AFError` `task` may have completed with. If `error` has already been set on the instance, this + /// value is ignored. + func didCompleteTask(_ task: URLSessionTask, with error: AFError?) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + self.error = self.error ?? error + + validators.forEach { $0() } + + eventMonitor?.request(self, didCompleteTask: task, with: error) + + retryOrFinish(error: self.error) + } + + /// Called when the `RequestDelegate` is going to retry this `Request`. Calls `reset()`. + func prepareForRetry() { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + $mutableState.write { $0.retryCount += 1 } + + reset() + + eventMonitor?.requestIsRetrying(self) + } + + /// Called to determine whether retry will be triggered for the particular error, or whether the instance should + /// call `finish()`. + /// + /// - Parameter error: The possible `AFError` which may trigger retry. + func retryOrFinish(error: AFError?) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + guard let error = error, let delegate = delegate else { finish(); return } + + delegate.retryResult(for: self, dueTo: error) { retryResult in + switch retryResult { + case .doNotRetry: + self.finish() + case let .doNotRetryWithError(retryError): + self.finish(error: retryError.asAFError(orFailWith: "Received retryError was not already AFError")) + case .retry, .retryWithDelay: + delegate.retryRequest(self, withDelay: retryResult.delay) + } + } + } + + /// Finishes this `Request` and starts the response serializers. + /// + /// - Parameter error: The possible `Error` with which the instance will finish. + func finish(error: AFError? = nil) { + dispatchPrecondition(condition: .onQueue(underlyingQueue)) + + guard !mutableState.isFinishing else { return } + + mutableState.isFinishing = true + + if let error = error { self.error = error } + + // Start response handlers + processNextResponseSerializer() + + eventMonitor?.requestDidFinish(self) + } + + /// Appends the response serialization closure to the instance. + /// + /// - Note: This method will also `resume` the instance if `delegate.startImmediately` returns `true`. + /// + /// - Parameter closure: The closure containing the response serialization call. + func appendResponseSerializer(_ closure: @escaping () -> Void) { + $mutableState.write { mutableState in + mutableState.responseSerializers.append(closure) + + if mutableState.state == .finished { + mutableState.state = .resumed + } + + if mutableState.responseSerializerProcessingFinished { + underlyingQueue.async { self.processNextResponseSerializer() } + } + + if mutableState.state.canTransitionTo(.resumed) { + underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } } + } + } + } + + /// Returns the next response serializer closure to execute if there's one left. + /// + /// - Returns: The next response serialization closure, if there is one. + func nextResponseSerializer() -> (() -> Void)? { + var responseSerializer: (() -> Void)? + + $mutableState.write { mutableState in + let responseSerializerIndex = mutableState.responseSerializerCompletions.count + + if responseSerializerIndex < mutableState.responseSerializers.count { + responseSerializer = mutableState.responseSerializers[responseSerializerIndex] + } + } + + return responseSerializer + } + + /// Processes the next response serializer and calls all completions if response serialization is complete. + func processNextResponseSerializer() { + guard let responseSerializer = nextResponseSerializer() else { + // Execute all response serializer completions and clear them + var completions: [() -> Void] = [] + + $mutableState.write { mutableState in + completions = mutableState.responseSerializerCompletions + + // Clear out all response serializers and response serializer completions in mutable state since the + // request is complete. It's important to do this prior to calling the completion closures in case + // the completions call back into the request triggering a re-processing of the response serializers. + // An example of how this can happen is by calling cancel inside a response completion closure. + mutableState.responseSerializers.removeAll() + mutableState.responseSerializerCompletions.removeAll() + + if mutableState.state.canTransitionTo(.finished) { + mutableState.state = .finished + } + + mutableState.responseSerializerProcessingFinished = true + mutableState.isFinishing = false + } + + completions.forEach { $0() } + + // Cleanup the request + cleanup() + + return + } + + serializationQueue.async { responseSerializer() } + } + + /// Notifies the `Request` that the response serializer is complete. + /// + /// - Parameter completion: The completion handler provided with the response serializer, called when all serializers + /// are complete. + func responseSerializerDidComplete(completion: @escaping () -> Void) { + $mutableState.write { $0.responseSerializerCompletions.append(completion) } + processNextResponseSerializer() + } + + /// Resets all task and response serializer related state for retry. + func reset() { + error = nil + + uploadProgress.totalUnitCount = 0 + uploadProgress.completedUnitCount = 0 + downloadProgress.totalUnitCount = 0 + downloadProgress.completedUnitCount = 0 + + $mutableState.write { state in + state.isFinishing = false + state.responseSerializerCompletions = [] + } + } + + /// Called when updating the upload progress. + /// + /// - Parameters: + /// - totalBytesSent: Total bytes sent so far. + /// - totalBytesExpectedToSend: Total bytes expected to send. + func updateUploadProgress(totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + uploadProgress.totalUnitCount = totalBytesExpectedToSend + uploadProgress.completedUnitCount = totalBytesSent + + uploadProgressHandler?.queue.async { self.uploadProgressHandler?.handler(self.uploadProgress) } + } + + /// Perform a closure on the current `state` while locked. + /// + /// - Parameter perform: The closure to perform. + func withState(perform: (State) -> Void) { + $mutableState.withState(perform: perform) + } + + // MARK: Task Creation + + /// Called when creating a `URLSessionTask` for this `Request`. Subclasses must override. + /// + /// - Parameters: + /// - request: `URLRequest` to use to create the `URLSessionTask`. + /// - session: `URLSession` which creates the `URLSessionTask`. + /// + /// - Returns: The `URLSessionTask` created. + func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + fatalError("Subclasses must override.") + } + + // MARK: - Public API + + // These APIs are callable from any queue. + + // MARK: State + + /// Cancels the instance. Once cancelled, a `Request` can no longer be resumed or suspended. + /// + /// - Returns: The instance. + @discardableResult + public func cancel() -> Self { + $mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.cancelled) else { return } + + mutableState.state = .cancelled + + underlyingQueue.async { self.didCancel() } + + guard let task = mutableState.tasks.last, task.state != .completed else { + underlyingQueue.async { self.finish() } + return + } + + // Resume to ensure metrics are gathered. + task.resume() + task.cancel() + underlyingQueue.async { self.didCancelTask(task) } + } + + return self + } + + /// Suspends the instance. + /// + /// - Returns: The instance. + @discardableResult + public func suspend() -> Self { + $mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.suspended) else { return } + + mutableState.state = .suspended + + underlyingQueue.async { self.didSuspend() } + + guard let task = mutableState.tasks.last, task.state != .completed else { return } + + task.suspend() + underlyingQueue.async { self.didSuspendTask(task) } + } + + return self + } + + /// Resumes the instance. + /// + /// - Returns: The instance. + @discardableResult + public func resume() -> Self { + $mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.resumed) else { return } + + mutableState.state = .resumed + + underlyingQueue.async { self.didResume() } + + guard let task = mutableState.tasks.last, task.state != .completed else { return } + + task.resume() + underlyingQueue.async { self.didResumeTask(task) } + } + + return self + } + + // MARK: - Closure API + + /// Associates a credential using the provided values with the instance. + /// + /// - Parameters: + /// - username: The username. + /// - password: The password. + /// - persistence: The `URLCredential.Persistence` for the created `URLCredential`. `.forSession` by default. + /// + /// - Returns: The instance. + @discardableResult + public func authenticate(username: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self { + let credential = URLCredential(user: username, password: password, persistence: persistence) + + return authenticate(with: credential) + } + + /// Associates the provided credential with the instance. + /// + /// - Parameter credential: The `URLCredential`. + /// + /// - Returns: The instance. + @discardableResult + public func authenticate(with credential: URLCredential) -> Self { + mutableState.credential = credential + + return self + } + + /// Sets a closure to be called periodically during the lifecycle of the instance as data is read from the server. + /// + /// - Note: Only the last closure provided is used. + /// + /// - Parameters: + /// - queue: The `DispatchQueue` to execute the closure on. `.main` by default. + /// - closure: The closure to be executed periodically as data is read from the server. + /// + /// - Returns: The instance. + @discardableResult + public func downloadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { + mutableState.downloadProgressHandler = (handler: closure, queue: queue) + + return self + } + + /// Sets a closure to be called periodically during the lifecycle of the instance as data is sent to the server. + /// + /// - Note: Only the last closure provided is used. + /// + /// - Parameters: + /// - queue: The `DispatchQueue` to execute the closure on. `.main` by default. + /// - closure: The closure to be executed periodically as data is sent to the server. + /// + /// - Returns: The instance. + @discardableResult + public func uploadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { + mutableState.uploadProgressHandler = (handler: closure, queue: queue) + + return self + } + + // MARK: Redirects + + /// Sets the redirect handler for the instance which will be used if a redirect response is encountered. + /// + /// - Note: Attempting to set the redirect handler more than once is a logic error and will crash. + /// + /// - Parameter handler: The `RedirectHandler`. + /// + /// - Returns: The instance. + @discardableResult + public func redirect(using handler: RedirectHandler) -> Self { + $mutableState.write { mutableState in + precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.") + mutableState.redirectHandler = handler + } + + return self + } + + // MARK: Cached Responses + + /// Sets the cached response handler for the `Request` which will be used when attempting to cache a response. + /// + /// - Note: Attempting to set the cache handler more than once is a logic error and will crash. + /// + /// - Parameter handler: The `CachedResponseHandler`. + /// + /// - Returns: The instance. + @discardableResult + public func cacheResponse(using handler: CachedResponseHandler) -> Self { + $mutableState.write { mutableState in + precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.") + mutableState.cachedResponseHandler = handler + } + + return self + } + + // MARK: - Lifetime APIs + + /// Sets a handler to be called when the cURL description of the request is available. + /// + /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which `handler` will be called. + /// - handler: Closure to be called when the cURL description is available. + /// + /// - Returns: The instance. + @discardableResult + public func cURLDescription(on queue: DispatchQueue, calling handler: @escaping (String) -> Void) -> Self { + $mutableState.write { mutableState in + if mutableState.requests.last != nil { + queue.async { handler(self.cURLDescription()) } + } else { + mutableState.cURLHandler = (queue, handler) + } + } + + return self + } + + /// Sets a handler to be called when the cURL description of the request is available. + /// + /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called. + /// + /// - Parameter handler: Closure to be called when the cURL description is available. Called on the instance's + /// `underlyingQueue` by default. + /// + /// - Returns: The instance. + @discardableResult + public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self { + $mutableState.write { mutableState in + if mutableState.requests.last != nil { + underlyingQueue.async { handler(self.cURLDescription()) } + } else { + mutableState.cURLHandler = (underlyingQueue, handler) + } + } + + return self + } + + /// Sets a closure to called whenever Alamofire creates a `URLRequest` for this instance. + /// + /// - Note: This closure will be called multiple times if the instance adapts incoming `URLRequest`s or is retried. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which `handler` will be called. `.main` by default. + /// - handler: Closure to be called when a `URLRequest` is available. + /// + /// - Returns: The instance. + @discardableResult + public func onURLRequestCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLRequest) -> Void) -> Self { + $mutableState.write { state in + if let request = state.requests.last { + queue.async { handler(request) } + } + + state.urlRequestHandler = (queue, handler) + } + + return self + } + + /// Sets a closure to be called whenever the instance creates a `URLSessionTask`. + /// + /// - Note: This API should only be used to provide `URLSessionTask`s to existing API, like `NSFileProvider`. It + /// **SHOULD NOT** be used to interact with tasks directly, as that may be break Alamofire features. + /// Additionally, this closure may be called multiple times if the instance is retried. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which `handler` will be called. `.main` by default. + /// - handler: Closure to be called when the `URLSessionTask` is available. + /// + /// - Returns: The instance. + @discardableResult + public func onURLSessionTaskCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLSessionTask) -> Void) -> Self { + $mutableState.write { state in + if let task = state.tasks.last { + queue.async { handler(task) } + } + + state.urlSessionTaskHandler = (queue, handler) + } + + return self + } + + // MARK: Cleanup + + /// Final cleanup step executed when the instance finishes response serialization. + func cleanup() { + delegate?.cleanup(after: self) + // No-op: override in subclass + } +} + +// MARK: - Protocol Conformances + +extension Request: Equatable { + public static func ==(lhs: Request, rhs: Request) -> Bool { + lhs.id == rhs.id + } +} + +extension Request: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} + +extension Request: CustomStringConvertible { + /// A textual representation of this instance, including the `HTTPMethod` and `URL` if the `URLRequest` has been + /// created, as well as the response status code, if a response has been received. + public var description: String { + guard let request = performedRequests.last ?? lastRequest, + let url = request.url, + let method = request.httpMethod else { return "No request created yet." } + + let requestDescription = "\(method) \(url.absoluteString)" + + return response.map { "\(requestDescription) (\($0.statusCode))" } ?? requestDescription + } +} + +extension Request { + /// cURL representation of the instance. + /// + /// - Returns: The cURL equivalent of the instance. + public func cURLDescription() -> String { + guard + let request = lastRequest, + let url = request.url, + let host = url.host, + let method = request.httpMethod else { return "$ curl command could not be created" } + + var components = ["$ curl -v"] + + components.append("-X \(method)") + + if let credentialStorage = delegate?.sessionConfiguration.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 = credential, let user = credential.user, let password = credential.password { + components.append("-u \(user):\(password)") + } + } + } + + if let configuration = delegate?.sessionConfiguration, configuration.httpShouldSetCookies { + if + let cookieStorage = configuration.httpCookieStorage, + let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty { + let allCookies = cookies.map { "\($0.name)=\($0.value)" }.joined(separator: ";") + + components.append("-b \"\(allCookies)\"") + } + } + + var headers = HTTPHeaders() + + if let sessionHeaders = delegate?.sessionConfiguration.headers { + for header in sessionHeaders where header.name != "Cookie" { + headers[header.name] = header.value + } + } + + for header in request.headers where header.name != "Cookie" { + headers[header.name] = header.value + } + + for header in headers { + let escapedValue = header.value.replacingOccurrences(of: "\"", with: "\\\"") + components.append("-H \"\(header.name): \(escapedValue)\"") + } + + if let httpBodyData = request.httpBody { + let httpBody = String(decoding: httpBodyData, as: UTF8.self) + var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"") + escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"") + + components.append("-d \"\(escapedBody)\"") + } + + components.append("\"\(url.absoluteString)\"") + + return components.joined(separator: " \\\n\t") + } +} + +/// Protocol abstraction for `Request`'s communication back to the `SessionDelegate`. +public protocol RequestDelegate: AnyObject { + /// `URLSessionConfiguration` used to create the underlying `URLSessionTask`s. + var sessionConfiguration: URLSessionConfiguration { get } + + /// Determines whether the `Request` should automatically call `resume()` when adding the first response handler. + var startImmediately: Bool { get } + + /// Notifies the delegate the `Request` has reached a point where it needs cleanup. + /// + /// - Parameter request: The `Request` to cleanup after. + func cleanup(after request: Request) + + /// Asynchronously ask the delegate whether a `Request` will be retried. + /// + /// - Parameters: + /// - request: `Request` which failed. + /// - error: `Error` which produced the failure. + /// - completion: Closure taking the `RetryResult` for evaluation. + func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void) + + /// Asynchronously retry the `Request`. + /// + /// - Parameters: + /// - request: `Request` which will be retried. + /// - timeDelay: `TimeInterval` after which the retry will be triggered. + func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?) +} + +// MARK: - Subclasses + +// MARK: - DataRequest + +/// `Request` subclass which handles in-memory `Data` download using `URLSessionDataTask`. +public class DataRequest: Request { + /// `URLRequestConvertible` value used to create `URLRequest`s for this instance. + public let convertible: URLRequestConvertible + /// `Data` read from the server so far. + public var data: Data? { mutableData } + + /// Protected storage for the `Data` read by the instance. + @Protected + private var mutableData: Data? = nil + + /// Creates a `DataRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - convertible: `URLRequestConvertible` value used to create `URLRequest`s for this instance. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. + init(id: UUID = UUID(), + convertible: URLRequestConvertible, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.convertible = convertible + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func reset() { + super.reset() + + mutableData = nil + } + + /// Called when `Data` is received by this instance. + /// + /// - Note: Also calls `updateDownloadProgress`. + /// + /// - Parameter data: The `Data` received. + func didReceive(data: Data) { + if self.data == nil { + mutableData = data + } else { + $mutableData.write { $0?.append(data) } + } + + updateDownloadProgress() + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + let copiedRequest = request + return session.dataTask(with: copiedRequest) + } + + /// Called to updated the `downloadProgress` of the instance. + func updateDownloadProgress() { + let totalBytesReceived = Int64(data?.count ?? 0) + let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown + + downloadProgress.totalUnitCount = totalBytesExpected + downloadProgress.completedUnitCount = totalBytesReceived + + downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) } + } + + /// Validates the request, using the specified closure. + /// + /// - Note: If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter validation: `Validation` closure used to validate the response. + /// + /// - Returns: The instance. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validator: () -> Void = { [unowned self] in + guard self.error == nil, let response = self.response else { return } + + let result = validation(self.request, response, self.data) + + if case let .failure(error) = result { self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) } + + self.eventMonitor?.request(self, + didValidateRequest: self.request, + response: response, + data: self.data, + withResult: result) + } + + $validators.write { $0.append(validator) } + + return self + } +} + +// MARK: - DataStreamRequest + +/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure. +public final class DataStreamRequest: Request { + /// Closure type handling `DataStreamRequest.Stream` values. + public typealias Handler = (Stream) throws -> Void + + /// Type encapsulating an `Event` as it flows through the stream, as well as a `CancellationToken` which can be used + /// to stop the stream at any time. + public struct Stream { + /// Latest `Event` from the stream. + public let event: Event + /// Token used to cancel the stream. + public let token: CancellationToken + + /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`. + public func cancel() { + token.cancel() + } + } + + /// Type representing an event flowing through the stream. Contains either the `Result` of processing streamed + /// `Data` or the completion of the stream. + public enum Event { + /// Output produced every time the instance receives additional `Data`. The associated value contains the + /// `Result` of processing the incoming `Data`. + case stream(Result) + /// Output produced when the instance has completed, whether due to stream end, cancellation, or an error. + /// Associated `Completion` value contains the final state. + case complete(Completion) + } + + /// Value containing the state of a `DataStreamRequest` when the stream was completed. + public struct Completion { + /// Last `URLRequest` issued by the instance. + public let request: URLRequest? + /// Last `HTTPURLResponse` received by the instance. + public let response: HTTPURLResponse? + /// Last `URLSessionTaskMetrics` produced for the instance. + public let metrics: URLSessionTaskMetrics? + /// `AFError` produced for the instance, if any. + public let error: AFError? + } + + /// Type used to cancel an ongoing stream. + public struct CancellationToken { + weak var request: DataStreamRequest? + + init(_ request: DataStreamRequest) { + self.request = request + } + + /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`. + public func cancel() { + request?.cancel() + } + } + + /// `URLRequestConvertible` value used to create `URLRequest`s for this instance. + public let convertible: URLRequestConvertible + /// Whether or not the instance will be cancelled if stream parsing encounters an error. + public let automaticallyCancelOnStreamError: Bool + + /// Internal mutable state specific to this type. + struct StreamMutableState { + /// `OutputStream` bound to the `InputStream` produced by `asInputStream`, if it has been called. + var outputStream: OutputStream? + /// Stream closures called as `Data` is received. + var streams: [(_ data: Data) -> Void] = [] + /// Number of currently executing streams. Used to ensure completions are only fired after all streams are + /// enqueued. + var numberOfExecutingStreams = 0 + /// Completion calls enqueued while streams are still executing. + var enqueuedCompletionEvents: [() -> Void] = [] + } + + @Protected + var streamMutableState = StreamMutableState() + + /// Creates a `DataStreamRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` + /// by default. + /// - convertible: `URLRequestConvertible` value used to create `URLRequest`s for this + /// instance. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance will be cancelled when an `Error` + /// is thrown while serializing stream `Data`. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default + /// targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by + /// the `Request`. + init(id: UUID = UUID(), + convertible: URLRequestConvertible, + automaticallyCancelOnStreamError: Bool, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate) { + self.convertible = convertible + self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + let copiedRequest = request + return session.dataTask(with: copiedRequest) + } + + override func finish(error: AFError? = nil) { + $streamMutableState.write { state in + state.outputStream?.close() + } + + super.finish(error: error) + } + + func didReceive(data: Data) { + $streamMutableState.write { state in + if let stream = state.outputStream { + underlyingQueue.async { + var bytes = Array(data) + stream.write(&bytes, maxLength: bytes.count) + } + } + state.numberOfExecutingStreams += state.streams.count + let localState = state + underlyingQueue.async { localState.streams.forEach { $0(data) } } + } + } + + /// Validates the `URLRequest` and `HTTPURLResponse` received for the instance using the provided `Validation` closure. + /// + /// - Parameter validation: `Validation` closure used to validate the request and response. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validator: () -> Void = { [unowned self] in + guard self.error == nil, let response = self.response else { return } + + let result = validation(self.request, response) + + if case let .failure(error) = result { + self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) + } + + self.eventMonitor?.request(self, + didValidateRequest: self.request, + response: response, + withResult: result) + } + + $validators.write { $0.append(validator) } + + return self + } + + /// Produces an `InputStream` that receives the `Data` received by the instance. + /// + /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`. + /// Additionally, this method will automatically call `resume()` on the instance, regardless of whether or + /// not the creating session has `startRequestsImmediately` set to `true`. + /// + /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`. + /// + /// - Returns: The `InputStream` bound to the internal `OutboundStream`. + public func asInputStream(bufferSize: Int = 1024) -> InputStream? { + defer { resume() } + + var inputStream: InputStream? + $streamMutableState.write { state in + Foundation.Stream.getBoundStreams(withBufferSize: bufferSize, + inputStream: &inputStream, + outputStream: &state.outputStream) + state.outputStream?.open() + } + + return inputStream + } + + func capturingError(from closure: () throws -> Void) { + do { + try closure() + } catch { + self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) + cancel() + } + } + + func appendStreamCompletion(on queue: DispatchQueue, + stream: @escaping Handler) { + appendResponseSerializer { + self.underlyingQueue.async { + self.responseSerializerDidComplete { + self.$streamMutableState.write { state in + guard state.numberOfExecutingStreams == 0 else { + state.enqueuedCompletionEvents.append { + self.enqueueCompletion(on: queue, stream: stream) + } + + return + } + + self.enqueueCompletion(on: queue, stream: stream) + } + } + } + } + } + + func enqueueCompletion(on queue: DispatchQueue, + stream: @escaping Handler) { + queue.async { + do { + let completion = Completion(request: self.request, + response: self.response, + metrics: self.metrics, + error: self.error) + try stream(.init(event: .complete(completion), token: .init(self))) + } catch { + // Ignore error, as errors on Completion can't be handled anyway. + } + } + } +} + +extension DataStreamRequest.Stream { + /// Incoming `Result` values from `Event.stream`. + public var result: Result? { + guard case let .stream(result) = event else { return nil } + + return result + } + + /// `Success` value of the instance, if any. + public var value: Success? { + guard case let .success(value) = result else { return nil } + + return value + } + + /// `Failure` value of the instance, if any. + public var error: Failure? { + guard case let .failure(error) = result else { return nil } + + return error + } + + /// `Completion` value of the instance, if any. + public var completion: DataStreamRequest.Completion? { + guard case let .complete(completion) = event else { return nil } + + return completion + } +} + +// MARK: - DownloadRequest + +/// `Request` subclass which downloads `Data` to a file on disk using `URLSessionDownloadTask`. +public class DownloadRequest: Request { + /// A set of options to be executed prior to moving a downloaded file from the temporary `URL` to the destination + /// `URL`. + public struct Options: OptionSet { + /// Specifies that intermediate directories for the destination URL should be created. + public static let createIntermediateDirectories = Options(rawValue: 1 << 0) + /// Specifies that any previous file at the destination `URL` should be removed. + public static let removePreviousFile = Options(rawValue: 1 << 1) + + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + } + + // MARK: Destination + + /// A closure executed once a `DownloadRequest` 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 `HTTPURLResponse`, and returns two values: the file URL where the temporary file should be moved and + /// the options defining how the file should be moved. + /// + /// - Note: Downloads from a local `file://` `URL`s do not use the `Destination` closure, as those downloads do not + /// return an `HTTPURLResponse`. Instead the file is merely moved within the temporary directory. + public typealias Destination = (_ temporaryURL: URL, + _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options) + + /// 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. + /// + /// - Parameters: + /// - directory: The search path directory. `.documentDirectory` by default. + /// - domain: The search path domain mask. `.userDomainMask` by default. + /// - options: `DownloadRequest.Options` used when moving the downloaded file to its destination. None by + /// default. + /// - Returns: The `Destination` closure. + public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory, + in domain: FileManager.SearchPathDomainMask = .userDomainMask, + options: Options = []) -> Destination { + { temporaryURL, response in + let directoryURLs = FileManager.default.urls(for: directory, in: domain) + let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL + + return (url, options) + } + } + + /// Default `Destination` used by Alamofire to ensure all downloads persist. This `Destination` prepends + /// `Alamofire_` to the automatically generated download name and moves it within the temporary directory. Files + /// with this destination must be additionally moved if they should survive the system reclamation of temporary + /// space. + static let defaultDestination: Destination = { url, _ in + (defaultDestinationURL(url), []) + } + + /// Default `URL` creation closure. Creates a `URL` in the temporary directory with `Alamofire_` prepended to the + /// provided file name. + static let defaultDestinationURL: (URL) -> URL = { url in + let filename = "Alamofire_\(url.lastPathComponent)" + let destination = url.deletingLastPathComponent().appendingPathComponent(filename) + + return destination + } + + // MARK: Downloadable + + /// Type describing the source used to create the underlying `URLSessionDownloadTask`. + public enum Downloadable { + /// Download should be started from the `URLRequest` produced by the associated `URLRequestConvertible` value. + case request(URLRequestConvertible) + /// Download should be started from the associated resume `Data` value. + case resumeData(Data) + } + + // MARK: Mutable State + + /// Type containing all mutable state for `DownloadRequest` instances. + private struct DownloadRequestMutableState { + /// Possible resume `Data` produced when cancelling the instance. + var resumeData: Data? + /// `URL` to which `Data` is being downloaded. + var fileURL: URL? + } + + /// Protected mutable state specific to `DownloadRequest`. + @Protected + private var mutableDownloadState = DownloadRequestMutableState() + + /// If the download is resumable and is eventually cancelled or fails, this value may be used to resume the download + /// using the `download(resumingWith data:)` API. + /// + /// - Note: For more information about `resumeData`, see [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel). + public var resumeData: Data? { mutableDownloadState.resumeData ?? error?.downloadResumeData } + /// If the download is successful, the `URL` where the file was downloaded. + public var fileURL: URL? { mutableDownloadState.fileURL } + + // MARK: Initial State + + /// `Downloadable` value used for this instance. + public let downloadable: Downloadable + /// The `Destination` to which the downloaded file is moved. + let destination: Destination + + /// Creates a `DownloadRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - downloadable: `Downloadable` value used to create `URLSessionDownloadTasks` for the instance. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request` + /// - destination: `Destination` closure used to move the downloaded file to its final location. + init(id: UUID = UUID(), + downloadable: Downloadable, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + delegate: RequestDelegate, + destination: @escaping Destination) { + self.downloadable = downloadable + self.destination = destination + + super.init(id: id, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + override func reset() { + super.reset() + + $mutableDownloadState.write { + $0.resumeData = nil + $0.fileURL = nil + } + } + + /// Called when a download has finished. + /// + /// - Parameters: + /// - task: `URLSessionTask` that finished the download. + /// - result: `Result` of the automatic move to `destination`. + func didFinishDownloading(using task: URLSessionTask, with result: Result) { + eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result) + + switch result { + case let .success(url): mutableDownloadState.fileURL = url + case let .failure(error): self.error = error + } + } + + /// Updates the `downloadProgress` using the provided values. + /// + /// - Parameters: + /// - bytesWritten: Total bytes written so far. + /// - totalBytesExpectedToWrite: Total bytes expected to write. + func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) { + downloadProgress.totalUnitCount = totalBytesExpectedToWrite + downloadProgress.completedUnitCount += bytesWritten + + downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) } + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + session.downloadTask(with: request) + } + + /// Creates a `URLSessionTask` from the provided resume data. + /// + /// - Parameters: + /// - data: `Data` used to resume the download. + /// - session: `URLSession` used to create the `URLSessionTask`. + /// + /// - Returns: The `URLSessionTask` created. + public func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask { + session.downloadTask(withResumeData: data) + } + + /// Cancels the instance. Once cancelled, a `DownloadRequest` can no longer be resumed or suspended. + /// + /// - Note: This method will NOT produce resume data. If you wish to cancel and produce resume data, use + /// `cancel(producingResumeData:)` or `cancel(byProducingResumeData:)`. + /// + /// - Returns: The instance. + @discardableResult + override public func cancel() -> Self { + cancel(producingResumeData: false) + } + + /// Cancels the instance, optionally producing resume data. Once cancelled, a `DownloadRequest` can no longer be + /// resumed or suspended. + /// + /// - Note: If `producingResumeData` is `true`, the `resumeData` property will be populated with any resume data, if + /// available. + /// + /// - Returns: The instance. + @discardableResult + public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self { + cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil) + } + + /// Cancels the instance while producing resume data. Once cancelled, a `DownloadRequest` can no longer be resumed + /// or suspended. + /// + /// - Note: The resume data passed to the completion handler will also be available on the instance's `resumeData` + /// property. + /// + /// - Parameter completionHandler: The completion handler that is called when the download has been successfully + /// cancelled. It is not guaranteed to be called on a particular queue, so you may + /// want use an appropriate queue to perform your work. + /// + /// - Returns: The instance. + @discardableResult + public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void) -> Self { + cancel(optionallyProducingResumeData: completionHandler) + } + + /// Internal implementation of cancellation that optionally takes a resume data handler. If no handler is passed, + /// cancellation is performed without producing resume data. + /// + /// - Parameter completionHandler: Optional resume data handler. + /// + /// - Returns: The instance. + private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self { + $mutableState.write { mutableState in + guard mutableState.state.canTransitionTo(.cancelled) else { return } + + mutableState.state = .cancelled + + underlyingQueue.async { self.didCancel() } + + guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state != .completed else { + underlyingQueue.async { self.finish() } + return + } + + if let completionHandler = completionHandler { + // Resume to ensure metrics are gathered. + task.resume() + task.cancel { resumeData in + self.mutableDownloadState.resumeData = resumeData + self.underlyingQueue.async { self.didCancelTask(task) } + completionHandler(resumeData) + } + } else { + // Resume to ensure metrics are gathered. + task.resume() + task.cancel(byProducingResumeData: { _ in }) + self.underlyingQueue.async { self.didCancelTask(task) } + } + } + + return self + } + + /// Validates the request, using the specified closure. + /// + /// - Note: If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - Parameter validation: `Validation` closure to validate the response. + /// + /// - Returns: The instance. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validator: () -> Void = { [unowned self] in + guard self.error == nil, let response = self.response else { return } + + let result = validation(self.request, response, self.fileURL) + + if case let .failure(error) = result { + self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) + } + + self.eventMonitor?.request(self, + didValidateRequest: self.request, + response: response, + fileURL: self.fileURL, + withResult: result) + } + + $validators.write { $0.append(validator) } + + return self + } +} + +// MARK: - UploadRequest + +/// `DataRequest` subclass which handles `Data` upload from memory, file, or stream using `URLSessionUploadTask`. +public class UploadRequest: DataRequest { + /// Type describing the origin of the upload, whether `Data`, file, or stream. + public enum Uploadable { + /// Upload from the provided `Data` value. + case data(Data) + /// Upload from the provided file `URL`, as well as a `Bool` determining whether the source file should be + /// automatically removed once uploaded. + case file(URL, shouldRemove: Bool) + /// Upload from the provided `InputStream`. + case stream(InputStream) + } + + // MARK: Initial State + + /// The `UploadableConvertible` value used to produce the `Uploadable` value for this instance. + public let upload: UploadableConvertible + + /// `FileManager` used to perform cleanup tasks, including the removal of multipart form encoded payloads written + /// to disk. + public let fileManager: FileManager + + // MARK: Mutable State + + /// `Uploadable` value used by the instance. + public var uploadable: Uploadable? + + /// Creates an `UploadRequest` using the provided parameters. + /// + /// - Parameters: + /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. + /// - convertible: `UploadConvertible` value used to determine the type of upload to be performed. + /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. + /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets + /// `underlyingQueue`, but can be passed another queue from a `Session`. + /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. + /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. + /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. + init(id: UUID = UUID(), + convertible: UploadConvertible, + underlyingQueue: DispatchQueue, + serializationQueue: DispatchQueue, + eventMonitor: EventMonitor?, + interceptor: RequestInterceptor?, + fileManager: FileManager, + delegate: RequestDelegate) { + upload = convertible + self.fileManager = fileManager + + super.init(id: id, + convertible: convertible, + underlyingQueue: underlyingQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: delegate) + } + + /// Called when the `Uploadable` value has been created from the `UploadConvertible`. + /// + /// - Parameter uploadable: The `Uploadable` that was created. + func didCreateUploadable(_ uploadable: Uploadable) { + self.uploadable = uploadable + + eventMonitor?.request(self, didCreateUploadable: uploadable) + } + + /// Called when the `Uploadable` value could not be created. + /// + /// - Parameter error: `AFError` produced by the failure. + func didFailToCreateUploadable(with error: AFError) { + self.error = error + + eventMonitor?.request(self, didFailToCreateUploadableWithError: error) + + retryOrFinish(error: error) + } + + override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { + guard let uploadable = uploadable else { + fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.") + } + + switch uploadable { + case let .data(data): return session.uploadTask(with: request, from: data) + case let .file(url, _): return session.uploadTask(with: request, fromFile: url) + case .stream: return session.uploadTask(withStreamedRequest: request) + } + } + + override func reset() { + // Uploadable must be recreated on every retry. + uploadable = nil + + super.reset() + } + + /// Produces the `InputStream` from `uploadable`, if it can. + /// + /// - Note: Calling this method with a non-`.stream` `Uploadable` is a logic error and will crash. + /// + /// - Returns: The `InputStream`. + func inputStream() -> InputStream { + guard let uploadable = uploadable else { + fatalError("Attempting to access the input stream but the uploadable doesn't exist.") + } + + guard case let .stream(stream) = uploadable else { + fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.") + } + + eventMonitor?.request(self, didProvideInputStream: stream) + + return stream + } + + override public func cleanup() { + defer { super.cleanup() } + + guard + let uploadable = self.uploadable, + case let .file(url, shouldRemove) = uploadable, + shouldRemove + else { return } + + try? fileManager.removeItem(at: url) + } +} + +/// A type that can produce an `UploadRequest.Uploadable` value. +public protocol UploadableConvertible { + /// Produces an `UploadRequest.Uploadable` value from the instance. + /// + /// - Returns: The `UploadRequest.Uploadable`. + /// - Throws: Any `Error` produced during creation. + func createUploadable() throws -> UploadRequest.Uploadable +} + +extension UploadRequest.Uploadable: UploadableConvertible { + public func createUploadable() throws -> UploadRequest.Uploadable { + self + } +} + +/// A type that can be converted to an upload, whether from an `UploadRequest.Uploadable` or `URLRequestConvertible`. +public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift new file mode 100644 index 00000000..1912e9c1 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift @@ -0,0 +1,244 @@ +// +// RequestInterceptor.swift +// +// Copyright (c) 2019 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 and calls the completion handler with the Result. + /// + /// - Parameters: + /// - urlRequest: The `URLRequest` to adapt. + /// - session: The `Session` that will execute the `URLRequest`. + /// - completion: The completion handler that must be called when adaptation is complete. + func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) +} + +// MARK: - + +/// Outcome of determination whether retry is necessary. +public enum RetryResult { + /// Retry should be attempted immediately. + case retry + /// Retry should be attempted after the associated `TimeInterval`. + case retryWithDelay(TimeInterval) + /// Do not retry. + case doNotRetry + /// Do not retry due to the associated `Error`. + case doNotRetryWithError(Error) +} + +extension RetryResult { + var retryRequired: Bool { + switch self { + case .retry, .retryWithDelay: return true + default: return false + } + } + + var delay: TimeInterval? { + switch self { + case let .retryWithDelay(delay): return delay + default: return nil + } + } + + var error: Error? { + guard case let .doNotRetryWithError(error) = self else { return nil } + return error + } +} + +/// 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. + /// + /// - Parameters: + /// - request: `Request` that failed due to the provided `Error`. + /// - session: `Session` that produced the `Request`. + /// - error: `Error` encountered while executing the `Request`. + /// - completion: Completion closure to be executed when a retry decision has been determined. + func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) +} + +// MARK: - + +/// Type that provides both `RequestAdapter` and `RequestRetrier` functionality. +public protocol RequestInterceptor: RequestAdapter, RequestRetrier {} + +extension RequestInterceptor { + public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + completion(.success(urlRequest)) + } + + public func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + completion(.doNotRetry) + } +} + +/// `RequestAdapter` closure definition. +public typealias AdaptHandler = (URLRequest, Session, _ completion: @escaping (Result) -> Void) -> Void +/// `RequestRetrier` closure definition. +public typealias RetryHandler = (Request, Session, Error, _ completion: @escaping (RetryResult) -> Void) -> Void + +// MARK: - + +/// Closure-based `RequestAdapter`. +open class Adapter: RequestInterceptor { + private let adaptHandler: AdaptHandler + + /// Creates an instance using the provided closure. + /// + /// - Parameter adaptHandler: `AdaptHandler` closure to be executed when handling request adaptation. + public init(_ adaptHandler: @escaping AdaptHandler) { + self.adaptHandler = adaptHandler + } + + open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + adaptHandler(urlRequest, session, completion) + } +} + +// MARK: - + +/// Closure-based `RequestRetrier`. +open class Retrier: RequestInterceptor { + private let retryHandler: RetryHandler + + /// Creates an instance using the provided closure. + /// + /// - Parameter retryHandler: `RetryHandler` closure to be executed when handling request retry. + public init(_ retryHandler: @escaping RetryHandler) { + self.retryHandler = retryHandler + } + + open func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + retryHandler(request, session, error, completion) + } +} + +// MARK: - + +/// `RequestInterceptor` which can use multiple `RequestAdapter` and `RequestRetrier` values. +open class Interceptor: RequestInterceptor { + /// All `RequestAdapter`s associated with the instance. These adapters will be run until one fails. + public let adapters: [RequestAdapter] + /// All `RequestRetrier`s associated with the instance. These retriers will be run one at a time until one triggers retry. + public let retriers: [RequestRetrier] + + /// Creates an instance from `AdaptHandler` and `RetryHandler` closures. + /// + /// - Parameters: + /// - adaptHandler: `AdaptHandler` closure to be used. + /// - retryHandler: `RetryHandler` closure to be used. + public init(adaptHandler: @escaping AdaptHandler, retryHandler: @escaping RetryHandler) { + adapters = [Adapter(adaptHandler)] + retriers = [Retrier(retryHandler)] + } + + /// Creates an instance from `RequestAdapter` and `RequestRetrier` values. + /// + /// - Parameters: + /// - adapter: `RequestAdapter` value to be used. + /// - retrier: `RequestRetrier` value to be used. + public init(adapter: RequestAdapter, retrier: RequestRetrier) { + adapters = [adapter] + retriers = [retrier] + } + + /// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values. + /// + /// - Parameters: + /// - adapters: `RequestAdapter` values to be used. + /// - retriers: `RequestRetrier` values to be used. + /// - interceptors: `RequestInterceptor`s to be used. + public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = [], interceptors: [RequestInterceptor] = []) { + self.adapters = adapters + interceptors + self.retriers = retriers + interceptors + } + + open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + adapt(urlRequest, for: session, using: adapters, completion: completion) + } + + private func adapt(_ urlRequest: URLRequest, + for session: Session, + using adapters: [RequestAdapter], + completion: @escaping (Result) -> Void) { + var pendingAdapters = adapters + + guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return } + + let adapter = pendingAdapters.removeFirst() + + adapter.adapt(urlRequest, for: session) { result in + switch result { + case let .success(urlRequest): + self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion) + case .failure: + completion(result) + } + } + } + + open func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + retry(request, for: session, dueTo: error, using: retriers, completion: completion) + } + + private func retry(_ request: Request, + for session: Session, + dueTo error: Error, + using retriers: [RequestRetrier], + completion: @escaping (RetryResult) -> Void) { + var pendingRetriers = retriers + + guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return } + + let retrier = pendingRetriers.removeFirst() + + retrier.retry(request, for: session, dueTo: error) { result in + switch result { + case .retry, .retryWithDelay, .doNotRetryWithError: + completion(result) + case .doNotRetry: + // Only continue to the next retrier if retry was not triggered and no error was encountered + self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion) + } + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift new file mode 100644 index 00000000..85b58f37 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift @@ -0,0 +1,149 @@ +// +// RequestTaskMap.swift +// +// Copyright (c) 2014-2018 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 maintains a two way, one to one map of `URLSessionTask`s to `Request`s. +struct RequestTaskMap { + private typealias Events = (completed: Bool, metricsGathered: Bool) + + private var tasksToRequests: [URLSessionTask: Request] + private var requestsToTasks: [Request: URLSessionTask] + private var taskEvents: [URLSessionTask: Events] + + var requests: [Request] { + Array(tasksToRequests.values) + } + + init(tasksToRequests: [URLSessionTask: Request] = [:], + requestsToTasks: [Request: URLSessionTask] = [:], + taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) { + self.tasksToRequests = tasksToRequests + self.requestsToTasks = requestsToTasks + self.taskEvents = taskEvents + } + + subscript(_ request: Request) -> URLSessionTask? { + get { requestsToTasks[request] } + set { + guard let newValue = newValue else { + guard let task = requestsToTasks[request] else { + fatalError("RequestTaskMap consistency error: no task corresponding to request found.") + } + + requestsToTasks.removeValue(forKey: request) + tasksToRequests.removeValue(forKey: task) + taskEvents.removeValue(forKey: task) + + return + } + + requestsToTasks[request] = newValue + tasksToRequests[newValue] = request + taskEvents[newValue] = (completed: false, metricsGathered: false) + } + } + + subscript(_ task: URLSessionTask) -> Request? { + get { tasksToRequests[task] } + set { + guard let newValue = newValue else { + guard let request = tasksToRequests[task] else { + fatalError("RequestTaskMap consistency error: no request corresponding to task found.") + } + + tasksToRequests.removeValue(forKey: task) + requestsToTasks.removeValue(forKey: request) + taskEvents.removeValue(forKey: task) + + return + } + + tasksToRequests[task] = newValue + requestsToTasks[newValue] = task + taskEvents[task] = (completed: false, metricsGathered: false) + } + } + + var count: Int { + precondition(tasksToRequests.count == requestsToTasks.count, + "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)") + + return tasksToRequests.count + } + + var eventCount: Int { + precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)") + + return taskEvents.count + } + + var isEmpty: Bool { + precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty, + "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)") + + return tasksToRequests.isEmpty + } + + var isEventsEmpty: Bool { + precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)") + + return taskEvents.isEmpty + } + + mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool { + guard let events = taskEvents[task] else { + fatalError("RequestTaskMap consistency error: no events corresponding to task found.") + } + + switch (events.completed, events.metricsGathered) { + case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.") + case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false + case (true, false): self[task] = nil; return true + } + } + + mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool { + guard let events = taskEvents[task] else { + fatalError("RequestTaskMap consistency error: no events corresponding to task found.") + } + + switch (events.completed, events.metricsGathered) { + case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.") + #if os(Linux) // Linux doesn't gather metrics, so unconditionally remove the reference and return true. + default: self[task] = nil; return true + #else + case (false, false): + if #available(macOS 10.12, iOS 10, watchOS 7, tvOS 10, *) { + taskEvents[task] = (completed: true, metricsGathered: false); return false + } else { + // watchOS < 7 doesn't gather metrics, so unconditionally remove the reference and return true. + self[task] = nil; return true + } + case (false, true): + self[task] = nil; return true + #endif + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift new file mode 100644 index 00000000..92e9c44e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift @@ -0,0 +1,454 @@ +// +// Response.swift +// +// Copyright (c) 2014-2018 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 + +/// Default type of `DataResponse` returned by Alamofire, with an `AFError` `Failure` type. +public typealias AFDataResponse = DataResponse +/// Default type of `DownloadResponse` returned by Alamofire, with an `AFError` `Failure` type. +public typealias AFDownloadResponse = DownloadResponse + +/// Type used to store all values associated with a serialized response of a `DataRequest` or `UploadRequest`. +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 final metrics of the response. + /// + /// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.` + /// + public let metrics: URLSessionTaskMetrics? + + /// The time taken to serialize the response. + public let serializationDuration: TimeInterval + + /// The result of response serialization. + public let result: Result + + /// Returns the associated value of the result if it is a success, `nil` otherwise. + public var value: Success? { result.success } + + /// Returns the associated error value if the result if it is a failure, `nil` otherwise. + public var error: Failure? { result.failure } + + /// Creates a `DataResponse` instance with the specified parameters derived from the response serialization. + /// + /// - Parameters: + /// - request: The `URLRequest` sent to the server. + /// - response: The `HTTPURLResponse` from the server. + /// - data: The `Data` returned by the server. + /// - metrics: The `URLSessionTaskMetrics` of the `DataRequest` or `UploadRequest`. + /// - serializationDuration: The duration taken by serialization. + /// - result: The `Result` of response serialization. + public init(request: URLRequest?, + response: HTTPURLResponse?, + data: Data?, + metrics: URLSessionTaskMetrics?, + serializationDuration: TimeInterval, + result: Result) { + self.request = request + self.response = response + self.data = data + self.metrics = metrics + self.serializationDuration = serializationDuration + self.result = result + } +} + +// 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 { + "\(result)" + } + + /// The debug textual representation used when written to an output stream, which includes (if available) a summary + /// of the `URLRequest`, the request's headers and body (if decodable as a `String` below 100KB); the + /// `HTTPURLResponse`'s status code, headers, and body; the duration of the network and serialization actions; and + /// the `Result` of serialization. + public var debugDescription: String { + guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" } + + let requestDescription = DebugDescription.description(of: urlRequest) + + let responseDescription = response.map { response in + let responseBodyDescription = DebugDescription.description(for: data, headers: response.headers) + + return """ + \(DebugDescription.description(of: response)) + \(responseBodyDescription.indentingNewlines()) + """ + } ?? "[Response]: None" + + let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None" + + return """ + \(requestDescription) + \(responseDescription) + [Network Duration]: \(networkDuration) + [Serialization Duration]: \(serializationDuration)s + [Result]: \(result) + """ + } +} + +// 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: (Success) -> NewSuccess) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.map(transform)) + } + + /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result + /// value as a parameter. + /// + /// Use the `tryMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.tryMap { + /// 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 tryMap(_ transform: (Success) throws -> NewSuccess) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMap(transform)) + } + + /// Evaluates the specified closure when the `DataResponse` 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: DataResponse = ... + /// let withMyError = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// + /// - Returns: A `DataResponse` instance containing the result of the transform. + public func mapError(_ transform: (Failure) -> NewFailure) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.mapError(transform)) + } + + /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `tryMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.tryMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `DataResponse` instance containing the result of the transform. + public func tryMapError(_ transform: (Failure) throws -> NewFailure) -> DataResponse { + DataResponse(request: request, + response: response, + data: data, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMapError(transform)) + } +} + +// 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 final destination URL of the data returned from the server after it is moved. + public let fileURL: URL? + + /// The resume data generated if the request was cancelled. + public let resumeData: Data? + + /// The final metrics of the response. + /// + /// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.` + /// + public let metrics: URLSessionTaskMetrics? + + /// The time taken to serialize the response. + public let serializationDuration: TimeInterval + + /// The result of response serialization. + public let result: Result + + /// Returns the associated value of the result if it is a success, `nil` otherwise. + public var value: Success? { result.success } + + /// Returns the associated error value if the result if it is a failure, `nil` otherwise. + public var error: Failure? { result.failure } + + /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. + /// + /// - Parameters: + /// - request: The `URLRequest` sent to the server. + /// - response: The `HTTPURLResponse` from the server. + /// - 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. + /// - metrics: The `URLSessionTaskMetrics` of the `DownloadRequest`. + /// - serializationDuration: The duration taken by serialization. + /// - result: The `Result` of response serialization. + public init(request: URLRequest?, + response: HTTPURLResponse?, + fileURL: URL?, + resumeData: Data?, + metrics: URLSessionTaskMetrics?, + serializationDuration: TimeInterval, + result: Result) { + self.request = request + self.response = response + self.fileURL = fileURL + self.resumeData = resumeData + self.metrics = metrics + self.serializationDuration = serializationDuration + self.result = result + } +} + +// 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 { + "\(result)" + } + + /// 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 durations of the network and serialization + /// actions, and the response serialization result. + public var debugDescription: String { + guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" } + + let requestDescription = DebugDescription.description(of: urlRequest) + let responseDescription = response.map(DebugDescription.description(of:)) ?? "[Response]: None" + let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None" + let resumeDataDescription = resumeData.map { "\($0)" } ?? "None" + + return """ + \(requestDescription) + \(responseDescription) + [File URL]: \(fileURL?.path ?? "None") + [Resume Data]: \(resumeDataDescription) + [Network Duration]: \(networkDuration) + [Serialization Duration]: \(serializationDuration)s + [Result]: \(result) + """ + } +} + +// 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: (Success) -> NewSuccess) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.map(transform)) + } + + /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `tryMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.tryMap { + /// 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 tryMap(_ transform: (Success) throws -> NewSuccess) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMap(transform)) + } + + /// Evaluates the specified closure when the `DownloadResponse` 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: DownloadResponse = ... + /// let withMyError = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// + /// - Returns: A `DownloadResponse` instance containing the result of the transform. + public func mapError(_ transform: (Failure) -> NewFailure) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.mapError(transform)) + } + + /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `tryMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.tryMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `DownloadResponse` instance containing the result of the transform. + public func tryMapError(_ transform: (Failure) throws -> NewFailure) -> DownloadResponse { + DownloadResponse(request: request, + response: response, + fileURL: fileURL, + resumeData: resumeData, + metrics: metrics, + serializationDuration: serializationDuration, + result: result.tryMapError(transform)) + } +} + +private enum DebugDescription { + static func description(of request: URLRequest) -> String { + let requestSummary = "\(request.httpMethod!) \(request)" + let requestHeadersDescription = DebugDescription.description(for: request.headers) + let requestBodyDescription = DebugDescription.description(for: request.httpBody, headers: request.headers) + + return """ + [Request]: \(requestSummary) + \(requestHeadersDescription.indentingNewlines()) + \(requestBodyDescription.indentingNewlines()) + """ + } + + static func description(of response: HTTPURLResponse) -> String { + """ + [Response]: + [Status Code]: \(response.statusCode) + \(DebugDescription.description(for: response.headers).indentingNewlines()) + """ + } + + static func description(for headers: HTTPHeaders) -> String { + guard !headers.isEmpty else { return "[Headers]: None" } + + let headerDescription = "\(headers.sorted())".indentingNewlines() + return """ + [Headers]: + \(headerDescription) + """ + } + + static func description(for data: Data?, + headers: HTTPHeaders, + allowingPrintableTypes printableTypes: [String] = ["json", "xml", "text"], + maximumLength: Int = 100_000) -> String { + guard let data = data, !data.isEmpty else { return "[Body]: None" } + + guard + data.count <= maximumLength, + printableTypes.compactMap({ headers["Content-Type"]?.contains($0) }).contains(true) + else { return "[Body]: \(data.count) bytes" } + + return """ + [Body]: + \(String(decoding: data, as: UTF8.self) + .trimmingCharacters(in: .whitespacesAndNewlines) + .indentingNewlines()) + """ + } +} + +extension String { + fileprivate func indentingNewlines(by spaceCount: Int = 4) -> String { + let spaces = String(repeating: " ", count: spaceCount) + return replacingOccurrences(of: "\n", with: "\n\(spaces)") + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift new file mode 100644 index 00000000..1b77016d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift @@ -0,0 +1,1116 @@ +// +// ResponseSerialization.swift +// +// Copyright (c) 2014-2018 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 + +// MARK: Protocols + +/// The type to which all data response serializers must conform in order to serialize a response. +public protocol DataResponseSerializerProtocol { + /// The type of serialized object to be created. + associatedtype SerializedObject + + /// Serialize the response `Data` into the provided type.. + /// + /// - Parameters: + /// - request: `URLRequest` which was used to perform the request, if any. + /// - response: `HTTPURLResponse` received from the server, if any. + /// - data: `Data` returned from the server, if any. + /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. + /// + /// - Returns: The `SerializedObject`. + /// - Throws: Any `Error` produced during serialization. + func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> SerializedObject +} + +/// The type to which all download response serializers must conform in order to serialize a response. +public protocol DownloadResponseSerializerProtocol { + /// The type of serialized object to be created. + associatedtype SerializedObject + + /// Serialize the downloaded response `Data` from disk into the provided type.. + /// + /// - Parameters: + /// - request: `URLRequest` which was used to perform the request, if any. + /// - response: `HTTPURLResponse` received from the server, if any. + /// - fileURL: File `URL` to which the response data was downloaded. + /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. + /// + /// - Returns: The `SerializedObject`. + /// - Throws: Any `Error` produced during serialization. + func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> SerializedObject +} + +/// A serializer that can handle both data and download responses. +public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol { + /// `DataPreprocessor` used to prepare incoming `Data` for serialization. + var dataPreprocessor: DataPreprocessor { get } + /// `HTTPMethod`s for which empty response bodies are considered appropriate. + var emptyRequestMethods: Set { get } + /// HTTP response codes for which empty response bodies are considered appropriate. + var emptyResponseCodes: Set { get } +} + +/// Type used to preprocess `Data` before it handled by a serializer. +public protocol DataPreprocessor { + /// Process `Data` before it's handled by a serializer. + /// - Parameter data: The raw `Data` to process. + func preprocess(_ data: Data) throws -> Data +} + +/// `DataPreprocessor` that returns passed `Data` without any transform. +public struct PassthroughPreprocessor: DataPreprocessor { + public init() {} + + public func preprocess(_ data: Data) throws -> Data { data } +} + +/// `DataPreprocessor` that trims Google's typical `)]}',\n` XSSI JSON header. +public struct GoogleXSSIPreprocessor: DataPreprocessor { + public init() {} + + public func preprocess(_ data: Data) throws -> Data { + (data.prefix(6) == Data(")]}',\n".utf8)) ? data.dropFirst(6) : data + } +} + +extension ResponseSerializer { + /// Default `DataPreprocessor`. `PassthroughPreprocessor` by default. + public static var defaultDataPreprocessor: DataPreprocessor { PassthroughPreprocessor() } + /// Default `HTTPMethod`s for which empty response bodies are considered appropriate. `[.head]` by default. + public static var defaultEmptyRequestMethods: Set { [.head] } + /// HTTP response codes for which empty response bodies are considered appropriate. `[204, 205]` by default. + public static var defaultEmptyResponseCodes: Set { [204, 205] } + + public var dataPreprocessor: DataPreprocessor { Self.defaultDataPreprocessor } + public var emptyRequestMethods: Set { Self.defaultEmptyRequestMethods } + public var emptyResponseCodes: Set { Self.defaultEmptyResponseCodes } + + /// Determines whether the `request` allows empty response bodies, if `request` exists. + /// + /// - Parameter request: `URLRequest` to evaluate. + /// + /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `request` was `nil`. + public func requestAllowsEmptyResponseData(_ request: URLRequest?) -> Bool? { + request.flatMap { $0.httpMethod } + .flatMap(HTTPMethod.init) + .map { emptyRequestMethods.contains($0) } + } + + /// Determines whether the `response` allows empty response bodies, if `response` exists`. + /// + /// - Parameter response: `HTTPURLResponse` to evaluate. + /// + /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `response` was `nil`. + public func responseAllowsEmptyResponseData(_ response: HTTPURLResponse?) -> Bool? { + response.flatMap { $0.statusCode } + .map { emptyResponseCodes.contains($0) } + } + + /// Determines whether `request` and `response` allow empty response bodies. + /// + /// - Parameters: + /// - request: `URLRequest` to evaluate. + /// - response: `HTTPURLResponse` to evaluate. + /// + /// - Returns: `true` if `request` or `response` allow empty bodies, `false` otherwise. + public func emptyResponseAllowed(forRequest request: URLRequest?, response: HTTPURLResponse?) -> Bool { + (requestAllowsEmptyResponseData(request) == true) || (responseAllowsEmptyResponseData(response) == true) + } +} + +/// By default, any serializer declared to conform to both types will get file serialization for free, as it just feeds +/// the data read from disk into the data response serializer. +extension DownloadResponseSerializerProtocol where Self: DataResponseSerializerProtocol { + public func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> Self.SerializedObject { + guard error == nil else { throw error! } + + guard let fileURL = fileURL else { + throw AFError.responseSerializationFailed(reason: .inputFileNil) + } + + let data: Data + do { + data = try Data(contentsOf: fileURL) + } catch { + throw AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)) + } + + do { + return try serialize(request: request, response: response, data: data, error: error) + } catch { + throw error + } + } +} + +// MARK: - Default + +extension DataRequest { + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let result = AFResult(value: self.data, error: self.error) + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DataResponse(request: self.request, + response: self.response, + data: self.data, + metrics: self.metrics, + serializationDuration: 0, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + } + } + + return self + } + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default + /// - responseSerializer: The response serializer responsible for serializing the request, response, and data. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDataResponse) -> Void) + -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let start = ProcessInfo.processInfo.systemUptime + let result: AFResult = Result { + try responseSerializer.serialize(request: self.request, + response: self.response, + data: self.data, + error: self.error) + }.mapError { error in + error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) + } + + let end = ProcessInfo.processInfo.systemUptime + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DataResponse(request: self.request, + response: self.response, + data: self.data, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + guard let serializerError = result.failure, let delegate = self.delegate else { + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + return + } + + delegate.retryResult(for: self, dueTo: serializerError) { retryResult in + var didComplete: (() -> Void)? + + defer { + if let didComplete = didComplete { + self.responseSerializerDidComplete { queue.async { didComplete() } } + } + } + + switch retryResult { + case .doNotRetry: + didComplete = { completionHandler(response) } + + case let .doNotRetryWithError(retryError): + let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) + + let response = DataResponse(request: self.request, + response: self.response, + data: self.data, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + didComplete = { completionHandler(response) } + + case .retry, .retryWithDelay: + delegate.retryRequest(self, withDelay: retryResult.delay) + } + } + } + } + + return self + } +} + +extension DownloadRequest { + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + completionHandler: @escaping (AFDownloadResponse) -> Void) + -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let result = AFResult(value: self.fileURL, error: self.error) + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DownloadResponse(request: self.request, + response: self.response, + fileURL: self.fileURL, + resumeData: self.resumeData, + metrics: self.metrics, + serializationDuration: 0, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + } + } + + return self + } + + /// Adds a handler to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - responseSerializer: The response serializer responsible for serializing the request, response, and data + /// contained in the destination `URL`. + /// - completionHandler: The code to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func response(queue: DispatchQueue = .main, + responseSerializer: Serializer, + completionHandler: @escaping (AFDownloadResponse) -> Void) + -> Self { + appendResponseSerializer { + // Start work that should be on the serialization queue. + let start = ProcessInfo.processInfo.systemUptime + let result: AFResult = Result { + try responseSerializer.serializeDownload(request: self.request, + response: self.response, + fileURL: self.fileURL, + error: self.error) + }.mapError { error in + error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) + } + let end = ProcessInfo.processInfo.systemUptime + // End work that should be on the serialization queue. + + self.underlyingQueue.async { + let response = DownloadResponse(request: self.request, + response: self.response, + fileURL: self.fileURL, + resumeData: self.resumeData, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + self.eventMonitor?.request(self, didParseResponse: response) + + guard let serializerError = result.failure, let delegate = self.delegate else { + self.responseSerializerDidComplete { queue.async { completionHandler(response) } } + return + } + + delegate.retryResult(for: self, dueTo: serializerError) { retryResult in + var didComplete: (() -> Void)? + + defer { + if let didComplete = didComplete { + self.responseSerializerDidComplete { queue.async { didComplete() } } + } + } + + switch retryResult { + case .doNotRetry: + didComplete = { completionHandler(response) } + + case let .doNotRetryWithError(retryError): + let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) + + let response = DownloadResponse(request: self.request, + response: self.response, + fileURL: self.fileURL, + resumeData: self.resumeData, + metrics: self.metrics, + serializationDuration: end - start, + result: result) + + didComplete = { completionHandler(response) } + + case .retry, .retryWithDelay: + delegate.retryRequest(self, withDelay: retryResult.delay) + } + } + } + } + + return self + } +} + +// MARK: - URL + +/// A `DownloadResponseSerializerProtocol` that performs only `Error` checking and ensures that a downloaded `fileURL` +/// is present. +public struct URLResponseSerializer: DownloadResponseSerializerProtocol { + /// Creates an instance. + public init() {} + + public func serializeDownload(request: URLRequest?, + response: HTTPURLResponse?, + fileURL: URL?, + error: Error?) throws -> URL { + guard error == nil else { throw error! } + + guard let url = fileURL else { + throw AFError.responseSerializationFailed(reason: .inputFileNil) + } + + return url + } +} + +extension DownloadRequest { + /// Adds a handler using a `URLResponseSerializer` to be called once the request is finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is called. `.main` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseURL(queue: DispatchQueue = .main, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, responseSerializer: URLResponseSerializer(), completionHandler: completionHandler) + } +} + +// MARK: - Data + +/// A `ResponseSerializer` that performs minimal response checking and returns any response `Data` as-is. By default, a +/// request returning `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the +/// response has an HTTP status code valid for empty responses, then an empty `Data` value is returned. +public final class DataResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + + /// Creates an instance using the provided values. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + public init(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) { + self.dataPreprocessor = dataPreprocessor + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Data { + guard error == nil else { throw error! } + + guard var data = data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + return Data() + } + + data = try dataPreprocessor.preprocess(data) + + return data + } +} + +extension DataRequest { + /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is called. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseData(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} + +extension DownloadRequest { + /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is called. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseData(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} + +// MARK: - String + +/// A `ResponseSerializer` that decodes the response data as a `String`. By default, a request returning `nil` or no +/// data is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code +/// valid for empty responses, then an empty `String` is returned. +public final class StringResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + /// Optional string encoding used to validate the response. + public let encoding: String.Encoding? + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + + /// Creates an instance with the provided values. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - encoding: A string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + public init(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) { + self.dataPreprocessor = dataPreprocessor + self.encoding = encoding + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> String { + guard error == nil else { throw error! } + + guard var data = data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + return "" + } + + data = try dataPreprocessor.preprocess(data) + + var convertedEncoding = encoding + + if let encodingName = response?.textEncodingName, convertedEncoding == nil { + convertedEncoding = String.Encoding(ianaCharsetName: encodingName) + } + + let actualEncoding = convertedEncoding ?? .isoLatin1 + + guard let string = String(data: data, encoding: actualEncoding) else { + throw AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)) + } + + return string + } +} + +extension DataRequest { + /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseString(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} + +extension DownloadRequest { + /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseString(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, + encoding: String.Encoding? = nil, + emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, + encoding: encoding, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} + +// MARK: - JSON + +/// A `ResponseSerializer` that decodes the response data using `JSONSerialization`. By default, a request returning +/// `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the response has an +/// HTTP status code valid for empty responses, then an `NSNull` value is returned. +public final class JSONResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + /// `JSONSerialization.ReadingOptions` used when serializing a response. + public let options: JSONSerialization.ReadingOptions + + /// Creates an instance with the provided values. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + /// - options: The options to use. `.allowFragments` by default. + public init(dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, + options: JSONSerialization.ReadingOptions = .allowFragments) { + self.dataPreprocessor = dataPreprocessor + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + self.options = options + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any { + guard error == nil else { throw error! } + + guard var data = data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + return NSNull() + } + + data = try dataPreprocessor.preprocess(data) + + do { + return try JSONSerialization.jsonObject(with: data, options: options) + } catch { + throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)) + } + } +} + +extension DataRequest { + /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` + /// by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseJSON(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods, + options: options), + completionHandler: completionHandler) + } +} + +extension DownloadRequest { + /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` + /// by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseJSON(queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, + emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods, + options: options), + completionHandler: completionHandler) + } +} + +// MARK: - Empty + +/// Protocol representing an empty response. Use `T.emptyValue()` to get an instance. +public protocol EmptyResponse { + /// Empty value for the conforming type. + /// + /// - Returns: Value of `Self` to use for empty values. + static func emptyValue() -> Self +} + +/// Type representing an empty value. Use `Empty.value` to get the static instance. +public struct Empty: Codable { + /// Static `Empty` instance used for all `Empty` responses. + public static let value = Empty() +} + +extension Empty: EmptyResponse { + public static func emptyValue() -> Empty { + value + } +} + +// MARK: - DataDecoder Protocol + +/// Any type which can decode `Data` into a `Decodable` type. +public protocol DataDecoder { + /// Decode `Data` into the provided type. + /// + /// - Parameters: + /// - type: The `Type` to be decoded. + /// - data: The `Data` to be decoded. + /// + /// - Returns: The decoded value of type `D`. + /// - Throws: Any error that occurs during decode. + func decode(_ type: D.Type, from data: Data) throws -> D +} + +/// `JSONDecoder` automatically conforms to `DataDecoder`. +extension JSONDecoder: DataDecoder {} +/// `PropertyListDecoder` automatically conforms to `DataDecoder`. +extension PropertyListDecoder: DataDecoder {} + +// MARK: - Decodable + +/// A `ResponseSerializer` that decodes the response data as a generic value using any type that conforms to +/// `DataDecoder`. By default, this is an instance of `JSONDecoder`. Additionally, a request returning `nil` or no data +/// is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code valid +/// for empty responses then an empty value will be returned. If the decoded type conforms to `EmptyResponse`, the +/// type's `emptyValue()` will be returned. If the decoded type is `Empty`, the `.value` instance is returned. If the +/// decoded type *does not* conform to `EmptyResponse` and isn't `Empty`, an error will be produced. +public final class DecodableResponseSerializer: ResponseSerializer { + public let dataPreprocessor: DataPreprocessor + /// The `DataDecoder` instance used to decode responses. + public let decoder: DataDecoder + public let emptyResponseCodes: Set + public let emptyRequestMethods: Set + + /// Creates an instance using the values provided. + /// + /// - Parameters: + /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. + /// - decoder: The `DataDecoder`. `JSONDecoder()` by default. + /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. + /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. + public init(dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) { + self.dataPreprocessor = dataPreprocessor + self.decoder = decoder + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T { + guard error == nil else { throw error! } + + guard var data = data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + guard let emptyResponseType = T.self as? EmptyResponse.Type, let emptyValue = emptyResponseType.emptyValue() as? T else { + throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) + } + + return emptyValue + } + + data = try dataPreprocessor.preprocess(data) + + do { + return try decoder.decode(T.self, from: data) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } + } +} + +extension DataRequest { + /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` + /// by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseDecodable(of type: T.Type = T.self, + queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDataResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} + +extension DownloadRequest { + /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. + /// + /// - Parameters: + /// - type: `Decodable` type to decode from response data. + /// - queue: The queue on which the completion handler is dispatched. `.main` by default. + /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the + /// `completionHandler`. `PassthroughPreprocessor()` by default. + /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. + /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined + /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. + /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. + /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. + /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` + /// by default. + /// - completionHandler: A closure to be executed once the request has finished. + /// + /// - Returns: The request. + @discardableResult + public func responseDecodable(of type: T.Type = T.self, + queue: DispatchQueue = .main, + dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, + decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, + completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { + response(queue: queue, + responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, + decoder: decoder, + emptyResponseCodes: emptyResponseCodes, + emptyRequestMethods: emptyRequestMethods), + completionHandler: completionHandler) + } +} + +// MARK: - DataStreamRequest + +/// A type which can serialize incoming `Data`. +public protocol DataStreamSerializer { + /// Type produced from the serialized `Data`. + associatedtype SerializedObject + + /// Serializes incoming `Data` into a `SerializedObject` value. + /// + /// - Parameter data: `Data` to be serialized. + /// + /// - Throws: Any error produced during serialization. + func serialize(_ data: Data) throws -> SerializedObject +} + +/// `DataStreamSerializer` which uses the provided `DataPreprocessor` and `DataDecoder` to serialize the incoming `Data`. +public struct DecodableStreamSerializer: DataStreamSerializer { + /// `DataDecoder` used to decode incoming `Data`. + public let decoder: DataDecoder + /// `DataPreprocessor` incoming `Data` is passed through before being passed to the `DataDecoder`. + public let dataPreprocessor: DataPreprocessor + + /// Creates an instance with the provided `DataDecoder` and `DataPreprocessor`. + /// - Parameters: + /// - decoder: ` DataDecoder` used to decode incoming `Data`. + /// - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the `decoder`. + public init(decoder: DataDecoder = JSONDecoder(), dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) { + self.decoder = decoder + self.dataPreprocessor = dataPreprocessor + } + + public func serialize(_ data: Data) throws -> T { + let processedData = try dataPreprocessor.preprocess(data) + do { + return try decoder.decode(T.self, from: processedData) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } + } +} + +/// `DataStreamSerializer` which performs no serialization on incoming `Data`. +public struct PassthroughStreamSerializer: DataStreamSerializer { + public func serialize(_ data: Data) throws -> Data { data } +} + +/// `DataStreamSerializer` which serializes incoming stream `Data` into `UTF8`-decoded `String` values. +public struct StringStreamSerializer: DataStreamSerializer { + public func serialize(_ data: Data) throws -> String { + String(decoding: data, as: UTF8.self) + } +} + +extension DataStreamRequest { + /// Adds a `StreamHandler` which performs no parsing on incoming `Data`. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler) -> Self { + let parser = { [unowned self] (data: Data) in + queue.async { + self.capturingError { + try stream(.init(event: .stream(.success(data)), token: .init(self))) + } + + self.updateAndCompleteIfPossible() + } + } + + $streamMutableState.write { $0.streams.append(parser) } + appendStreamCompletion(on: queue, stream: stream) + + return self + } + + /// Adds a `StreamHandler` which uses the provided `DataStreamSerializer` to process incoming `Data`. + /// + /// - Parameters: + /// - serializer: `DataStreamSerializer` used to process incoming `Data`. Its work is done on the `serializationQueue`. + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStream(using serializer: Serializer, + on queue: DispatchQueue = .main, + stream: @escaping Handler) -> Self { + let parser = { [unowned self] (data: Data) in + self.serializationQueue.async { + // Start work on serialization queue. + let result = Result { try serializer.serialize(data) } + .mapError { $0.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: $0))) } + // End work on serialization queue. + self.underlyingQueue.async { + self.eventMonitor?.request(self, didParseStream: result) + + if result.isFailure, self.automaticallyCancelOnStreamError { + self.cancel() + } + + queue.async { + self.capturingError { + try stream(.init(event: .stream(result), token: .init(self))) + } + + self.updateAndCompleteIfPossible() + } + } + } + } + + $streamMutableState.write { $0.streams.append(parser) } + appendStreamCompletion(on: queue, stream: stream) + + return self + } + + /// Adds a `StreamHandler` which parses incoming `Data` as a UTF8 `String`. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStreamString(on queue: DispatchQueue = .main, + stream: @escaping Handler) -> Self { + let parser = { [unowned self] (data: Data) in + self.serializationQueue.async { + // Start work on serialization queue. + let string = String(decoding: data, as: UTF8.self) + // End work on serialization queue. + self.underlyingQueue.async { + self.eventMonitor?.request(self, didParseStream: .success(string)) + + queue.async { + self.capturingError { + try stream(.init(event: .stream(.success(string)), token: .init(self))) + } + + self.updateAndCompleteIfPossible() + } + } + } + } + + $streamMutableState.write { $0.streams.append(parser) } + appendStreamCompletion(on: queue, stream: stream) + + return self + } + + private func updateAndCompleteIfPossible() { + $streamMutableState.write { state in + state.numberOfExecutingStreams -= 1 + + guard state.numberOfExecutingStreams == 0, !state.enqueuedCompletionEvents.isEmpty else { return } + + let completionEvents = state.enqueuedCompletionEvents + self.underlyingQueue.async { completionEvents.forEach { $0() } } + state.enqueuedCompletionEvents.removeAll() + } + } + + /// Adds a `StreamHandler` which parses incoming `Data` using the provided `DataDecoder`. + /// + /// - Parameters: + /// - type: `Decodable` type to parse incoming `Data` into. + /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. + /// - decoder: `DataDecoder` used to decode the incoming `Data`. + /// - preprocessor: `DataPreprocessor` used to process the incoming `Data` before it's passed to the `decoder`. + /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. + /// + /// - Returns: The `DataStreamRequest`. + @discardableResult + public func responseStreamDecodable(of type: T.Type = T.self, + on queue: DispatchQueue = .main, + using decoder: DataDecoder = JSONDecoder(), + preprocessor: DataPreprocessor = PassthroughPreprocessor(), + stream: @escaping Handler) -> Self { + responseStream(using: DecodableStreamSerializer(decoder: decoder, dataPreprocessor: preprocessor), + stream: stream) + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift new file mode 100644 index 00000000..39ac2860 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift @@ -0,0 +1,120 @@ +// +// Result+Alamofire.swift +// +// Copyright (c) 2019 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 + +/// Default type of `Result` returned by Alamofire, with an `AFError` `Failure` type. +public typealias AFResult = Result + +// MARK: - Internal APIs + +extension Result { + /// Returns whether the instance is `.success`. + var isSuccess: Bool { + guard case .success = self else { return false } + return true + } + + /// Returns whether the instance is `.failure`. + var isFailure: Bool { + !isSuccess + } + + /// Returns the associated value if the result is a success, `nil` otherwise. + var success: Success? { + guard case let .success(value) = self else { return nil } + return value + } + + /// Returns the associated error value if the result is a failure, `nil` otherwise. + var failure: Failure? { + guard case let .failure(error) = self else { return nil } + return error + } + + /// Initializes a `Result` from value or error. Returns `.failure` if the error is non-nil, `.success` otherwise. + /// + /// - Parameters: + /// - value: A value. + /// - error: An `Error`. + init(value: Success, error: Failure?) { + if let error = error { + self = .failure(error) + } else { + self = .success(value) + } + } + + /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. + /// + /// Use the `tryMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.tryMap { + /// 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. + func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { + switch self { + case let .success(value): + do { + return try .success(transform(value)) + } catch { + return .failure(error) + } + case let .failure(error): + return .failure(error) + } + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `tryMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.tryMapError { + /// 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 success. + func tryMapError(_ transform: (Failure) throws -> NewFailure) -> Result { + switch self { + case let .failure(error): + do { + return try .failure(transform(error)) + } catch { + return .failure(error) + } + case let .success(value): + return .success(value) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift new file mode 100644 index 00000000..e9cbcaf4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift @@ -0,0 +1,370 @@ +// +// RetryPolicy.swift +// +// Copyright (c) 2019-2020 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 retry policy that retries requests using an exponential backoff for allowed HTTP methods and HTTP status codes +/// as well as certain types of networking errors. +open class RetryPolicy: RequestInterceptor { + /// The default retry limit for retry policies. + public static let defaultRetryLimit: UInt = 2 + + /// The default exponential backoff base for retry policies (must be a minimum of 2). + public static let defaultExponentialBackoffBase: UInt = 2 + + /// The default exponential backoff scale for retry policies. + public static let defaultExponentialBackoffScale: Double = 0.5 + + /// The default HTTP methods to retry. + /// See [RFC 2616 - Section 9.1.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) for more information. + public static let defaultRetryableHTTPMethods: Set = [.delete, // [Delete](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7) - not always idempotent + .get, // [GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3) - generally idempotent + .head, // [HEAD](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4) - generally idempotent + .options, // [OPTIONS](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2) - inherently idempotent + .put, // [PUT](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) - not always idempotent + .trace // [TRACE](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8) - inherently idempotent + ] + + /// The default HTTP status codes to retry. + /// See [RFC 2616 - Section 10](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10) for more information. + public static let defaultRetryableHTTPStatusCodes: Set = [408, // [Request Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9) + 500, // [Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) + 502, // [Bad Gateway](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3) + 503, // [Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) + 504 // [Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5) + ] + + /// The default URL error codes to retry. + public static let defaultRetryableURLErrorCodes: Set = [// [Security] App Transport Security disallowed a connection because there is no secure network connection. + // - [Disabled] ATS settings do not change at runtime. + // .appTransportSecurityRequiresSecureConnection, + + // [System] An app or app extension attempted to connect to a background session that is already connected to a + // process. + // - [Enabled] The other process could release the background session. + .backgroundSessionInUseByAnotherProcess, + + // [System] The shared container identifier of the URL session configuration is needed but has not been set. + // - [Disabled] Cannot change at runtime. + // .backgroundSessionRequiresSharedContainer, + + // [System] The app is suspended or exits while a background data task is processing. + // - [Enabled] App can be foregrounded or launched to recover. + .backgroundSessionWasDisconnected, + + // [Network] The URL Loading system received bad data from the server. + // - [Enabled] Server could return valid data when retrying. + .badServerResponse, + + // [Resource] A malformed URL prevented a URL request from being initiated. + // - [Disabled] URL was most likely constructed incorrectly. + // .badURL, + + // [System] A connection was attempted while a phone call is active on a network that does not support + // simultaneous phone and data communication (EDGE or GPRS). + // - [Enabled] Phone call could be ended to allow request to recover. + .callIsActive, + + // [Client] An asynchronous load has been canceled. + // - [Disabled] Request was cancelled by the client. + // .cancelled, + + // [File System] A download task couldn’t close the downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotCloseFile, + + // [Network] An attempt to connect to a host failed. + // - [Enabled] Server or DNS lookup could recover during retry. + .cannotConnectToHost, + + // [File System] A download task couldn’t create the downloaded file on disk because of an I/O failure. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotCreateFile, + + // [Data] Content data received during a connection request had an unknown content encoding. + // - [Disabled] Server is unlikely to modify the content encoding during a retry. + // .cannotDecodeContentData, + + // [Data] Content data received during a connection request could not be decoded for a known content encoding. + // - [Disabled] Server is unlikely to modify the content encoding during a retry. + // .cannotDecodeRawData, + + // [Network] The host name for a URL could not be resolved. + // - [Enabled] Server or DNS lookup could recover during retry. + .cannotFindHost, + + // [Network] A request to load an item only from the cache could not be satisfied. + // - [Enabled] Cache could be populated during a retry. + .cannotLoadFromNetwork, + + // [File System] A download task was unable to move a downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotMoveFile, + + // [File System] A download task was unable to open the downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotOpenFile, + + // [Data] A task could not parse a response. + // - [Disabled] Invalid response is unlikely to recover with retry. + // .cannotParseResponse, + + // [File System] A download task was unable to remove a downloaded file from disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotRemoveFile, + + // [File System] A download task was unable to write to the downloaded file on disk. + // - [Disabled] File system error is unlikely to recover with retry. + // .cannotWriteToFile, + + // [Security] A client certificate was rejected. + // - [Disabled] Client certificate is unlikely to change with retry. + // .clientCertificateRejected, + + // [Security] A client certificate was required to authenticate an SSL connection during a request. + // - [Disabled] Client certificate is unlikely to be provided with retry. + // .clientCertificateRequired, + + // [Data] The length of the resource data exceeds the maximum allowed. + // - [Disabled] Resource will likely still exceed the length maximum on retry. + // .dataLengthExceedsMaximum, + + // [System] The cellular network disallowed a connection. + // - [Enabled] WiFi connection could be established during retry. + .dataNotAllowed, + + // [Network] The host address could not be found via DNS lookup. + // - [Enabled] DNS lookup could succeed during retry. + .dnsLookupFailed, + + // [Data] A download task failed to decode an encoded file during the download. + // - [Enabled] Server could correct the decoding issue with retry. + .downloadDecodingFailedMidStream, + + // [Data] A download task failed to decode an encoded file after downloading. + // - [Enabled] Server could correct the decoding issue with retry. + .downloadDecodingFailedToComplete, + + // [File System] A file does not exist. + // - [Disabled] File system error is unlikely to recover with retry. + // .fileDoesNotExist, + + // [File System] A request for an FTP file resulted in the server responding that the file is not a plain file, + // but a directory. + // - [Disabled] FTP directory is not likely to change to a file during a retry. + // .fileIsDirectory, + + // [Network] A redirect loop has been detected or the threshold for number of allowable redirects has been + // exceeded (currently 16). + // - [Disabled] The redirect loop is unlikely to be resolved within the retry window. + // .httpTooManyRedirects, + + // [System] The attempted connection required activating a data context while roaming, but international roaming + // is disabled. + // - [Enabled] WiFi connection could be established during retry. + .internationalRoamingOff, + + // [Connectivity] A client or server connection was severed in the middle of an in-progress load. + // - [Enabled] A network connection could be established during retry. + .networkConnectionLost, + + // [File System] A resource couldn’t be read because of insufficient permissions. + // - [Disabled] Permissions are unlikely to be granted during retry. + // .noPermissionsToReadFile, + + // [Connectivity] A network resource was requested, but an internet connection has not been established and + // cannot be established automatically. + // - [Enabled] A network connection could be established during retry. + .notConnectedToInternet, + + // [Resource] A redirect was specified by way of server response code, but the server did not accompany this + // code with a redirect URL. + // - [Disabled] The redirect URL is unlikely to be supplied during a retry. + // .redirectToNonExistentLocation, + + // [Client] A body stream is needed but the client did not provide one. + // - [Disabled] The client will be unlikely to supply a body stream during retry. + // .requestBodyStreamExhausted, + + // [Resource] A requested resource couldn’t be retrieved. + // - [Disabled] The resource is unlikely to become available during the retry window. + // .resourceUnavailable, + + // [Security] An attempt to establish a secure connection failed for reasons that can’t be expressed more + // specifically. + // - [Enabled] The secure connection could be established during a retry given the lack of specificity + // provided by the error. + .secureConnectionFailed, + + // [Security] A server certificate had a date which indicates it has expired, or is not yet valid. + // - [Enabled] The server certificate could become valid within the retry window. + .serverCertificateHasBadDate, + + // [Security] A server certificate was not signed by any root server. + // - [Disabled] The server certificate is unlikely to change during the retry window. + // .serverCertificateHasUnknownRoot, + + // [Security] A server certificate is not yet valid. + // - [Enabled] The server certificate could become valid within the retry window. + .serverCertificateNotYetValid, + + // [Security] A server certificate was signed by a root server that isn’t trusted. + // - [Disabled] The server certificate is unlikely to become trusted within the retry window. + // .serverCertificateUntrusted, + + // [Network] An asynchronous operation timed out. + // - [Enabled] The request timed out for an unknown reason and should be retried. + .timedOut + + // [System] The URL Loading System encountered an error that it can’t interpret. + // - [Disabled] The error could not be interpreted and is unlikely to be recovered from during a retry. + // .unknown, + + // [Resource] A properly formed URL couldn’t be handled by the framework. + // - [Disabled] The URL is unlikely to change during a retry. + // .unsupportedURL, + + // [Client] Authentication is required to access a resource. + // - [Disabled] The user authentication is unlikely to be provided by retrying. + // .userAuthenticationRequired, + + // [Client] An asynchronous request for authentication has been canceled by the user. + // - [Disabled] The user cancelled authentication and explicitly took action to not retry. + // .userCancelledAuthentication, + + // [Resource] A server reported that a URL has a non-zero content length, but terminated the network connection + // gracefully without sending any data. + // - [Disabled] The server is unlikely to provide data during the retry window. + // .zeroByteResource, + ] + + /// The total number of times the request is allowed to be retried. + public let retryLimit: UInt + + /// The base of the exponential backoff policy (should always be greater than or equal to 2). + public let exponentialBackoffBase: UInt + + /// The scale of the exponential backoff. + public let exponentialBackoffScale: Double + + /// The HTTP methods that are allowed to be retried. + public let retryableHTTPMethods: Set + + /// The HTTP status codes that are automatically retried by the policy. + public let retryableHTTPStatusCodes: Set + + /// The URL error codes that are automatically retried by the policy. + public let retryableURLErrorCodes: Set + + /// Creates an `ExponentialBackoffRetryPolicy` from the specified parameters. + /// + /// - Parameters: + /// - retryLimit: The total number of times the request is allowed to be retried. `2` by default. + /// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default. + /// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default. + /// - retryableHTTPMethods: The HTTP methods that are allowed to be retried. + /// `RetryPolicy.defaultRetryableHTTPMethods` by default. + /// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy. + /// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default. + /// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy. + /// `RetryPolicy.defaultRetryableURLErrorCodes` by default. + public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit, + exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, + exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, + retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods, + retryableHTTPStatusCodes: Set = RetryPolicy.defaultRetryableHTTPStatusCodes, + retryableURLErrorCodes: Set = RetryPolicy.defaultRetryableURLErrorCodes) { + precondition(exponentialBackoffBase >= 2, "The `exponentialBackoffBase` must be a minimum of 2.") + + self.retryLimit = retryLimit + self.exponentialBackoffBase = exponentialBackoffBase + self.exponentialBackoffScale = exponentialBackoffScale + self.retryableHTTPMethods = retryableHTTPMethods + self.retryableHTTPStatusCodes = retryableHTTPStatusCodes + self.retryableURLErrorCodes = retryableURLErrorCodes + } + + open func retry(_ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void) { + if request.retryCount < retryLimit, shouldRetry(request: request, dueTo: error) { + completion(.retryWithDelay(pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale)) + } else { + completion(.doNotRetry) + } + } + + /// Determines whether or not to retry the provided `Request`. + /// + /// - Parameters: + /// - request: `Request` that failed due to the provided `Error`. + /// - error: `Error` encountered while executing the `Request`. + /// + /// - Returns: `Bool` determining whether or not to retry the `Request`. + open func shouldRetry(request: Request, dueTo error: Error) -> Bool { + guard let httpMethod = request.request?.method, retryableHTTPMethods.contains(httpMethod) else { return false } + + if let statusCode = request.response?.statusCode, retryableHTTPStatusCodes.contains(statusCode) { + return true + } else { + let errorCode = (error as? URLError)?.code + let afErrorCode = (error.asAFError?.underlyingError as? URLError)?.code + + guard let code = errorCode ?? afErrorCode else { return false } + + return retryableURLErrorCodes.contains(code) + } + } +} + +// MARK: - + +/// A retry policy that automatically retries idempotent requests for network connection lost errors. For more +/// information about retrying network connection lost errors, please refer to Apple's +/// [technical document](https://developer.apple.com/library/content/qa/qa1941/_index.html). +open class ConnectionLostRetryPolicy: RetryPolicy { + /// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters. + /// + /// - Parameters: + /// - retryLimit: The total number of times the request is allowed to be retried. + /// `RetryPolicy.defaultRetryLimit` by default. + /// - exponentialBackoffBase: The base of the exponential backoff policy. + /// `RetryPolicy.defaultExponentialBackoffBase` by default. + /// - exponentialBackoffScale: The scale of the exponential backoff. + /// `RetryPolicy.defaultExponentialBackoffScale` by default. + /// - retryableHTTPMethods: The idempotent http methods to retry. + /// `RetryPolicy.defaultRetryableHTTPMethods` by default. + public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit, + exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, + exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, + retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods) { + super.init(retryLimit: retryLimit, + exponentialBackoffBase: exponentialBackoffBase, + exponentialBackoffScale: exponentialBackoffScale, + retryableHTTPMethods: retryableHTTPMethods, + retryableHTTPStatusCodes: [], + retryableURLErrorCodes: [.networkConnectionLost]) + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift new file mode 100644 index 00000000..9ca94f16 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift @@ -0,0 +1,619 @@ +// +// ServerTrustPolicy.swift +// +// Copyright (c) 2014-2016 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 `ServerTrustEvaluating` values to given hosts. +open class ServerTrustManager { + /// Determines whether all hosts for this `ServerTrustManager` must be evaluated. `true` by default. + public let allHostsMustBeEvaluated: Bool + + /// The dictionary of policies mapped to a particular host. + public let evaluators: [String: ServerTrustEvaluating] + + /// Initializes the `ServerTrustManager` instance with the given evaluators. + /// + /// Since different servers and web services can have different leaf certificates, intermediate and even root + /// certificates, 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. + /// + /// - Parameters: + /// - allHostsMustBeEvaluated: The value determining whether all hosts for this instance must be evaluated. `true` + /// by default. + /// - evaluators: A dictionary of evaluators mapped to hosts. + public init(allHostsMustBeEvaluated: Bool = true, evaluators: [String: ServerTrustEvaluating]) { + self.allHostsMustBeEvaluated = allHostsMustBeEvaluated + self.evaluators = evaluators + } + + /// Returns the `ServerTrustEvaluating` value for the given host, if one is set. + /// + /// 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 `ServerTrustEvaluating` value for the given host if found, `nil` otherwise. + /// - Throws: `AFError.serverTrustEvaluationFailed` if `allHostsMustBeEvaluated` is `true` and no matching + /// evaluators are found. + open func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? { + guard let evaluator = evaluators[host] else { + if allHostsMustBeEvaluated { + throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host)) + } + + return nil + } + + return evaluator + } +} + +/// A protocol describing the API used to evaluate server trusts. +public protocol ServerTrustEvaluating { + #if os(Linux) + // Implement this once Linux has API for evaluating server trusts. + #else + /// Evaluates the given `SecTrust` value for the given `host`. + /// + /// - Parameters: + /// - trust: The `SecTrust` value to evaluate. + /// - host: The host for which to evaluate the `SecTrust` value. + /// + /// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`. + func evaluate(_ trust: SecTrust, forHost host: String) throws + #endif +} + +// MARK: - Server Trust Evaluators + +/// An evaluator which 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. +public final class DefaultTrustEvaluator: ServerTrustEvaluating { + private let validateHost: Bool + + /// Creates a `DefaultTrustEvaluator`. + /// + /// - Parameter validateHost: Determines whether or not the evaluator should validate the host. `true` by default. + public init(validateHost: Bool = true) { + self.validateHost = validateHost + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + if validateHost { + try trust.af.performValidation(forHost: host) + } + + try trust.af.performDefaultValidation(forHost: host) + } +} + +/// An evaluator which 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. +public final class RevocationTrustEvaluator: ServerTrustEvaluating { + /// Represents the options to be use when evaluating the status of a certificate. + /// Only Revocation Policy Constants are valid, and can be found in [Apple's documentation](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/policies/1563600-revocation_policy_constants). + public struct Options: OptionSet { + /// Perform revocation checking using the CRL (Certification Revocation List) method. + public static let crl = Options(rawValue: kSecRevocationCRLMethod) + /// Consult only locally cached replies; do not use network access. + public static let networkAccessDisabled = Options(rawValue: kSecRevocationNetworkAccessDisabled) + /// Perform revocation checking using OCSP (Online Certificate Status Protocol). + public static let ocsp = Options(rawValue: kSecRevocationOCSPMethod) + /// Prefer CRL revocation checking over OCSP; by default, OCSP is preferred. + public static let preferCRL = Options(rawValue: kSecRevocationPreferCRL) + /// Require a positive response to pass the policy. If the flag is not set, revocation checking is done on a + /// "best attempt" basis, where failure to reach the server is not considered fatal. + public static let requirePositiveResponse = Options(rawValue: kSecRevocationRequirePositiveResponse) + /// Perform either OCSP or CRL checking. The checking is performed according to the method(s) specified in the + /// certificate and the value of `preferCRL`. + public static let any = Options(rawValue: kSecRevocationUseAnyAvailableMethod) + + /// The raw value of the option. + public let rawValue: CFOptionFlags + + /// Creates an `Options` value with the given `CFOptionFlags`. + /// + /// - Parameter rawValue: The `CFOptionFlags` value to initialize with. + public init(rawValue: CFOptionFlags) { + self.rawValue = rawValue + } + } + + private let performDefaultValidation: Bool + private let validateHost: Bool + private let options: Options + + /// Creates a `RevocationTrustEvaluator`. + /// + /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use + /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. + /// + /// - Parameters: + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition + /// to performing the default evaluation, even if `performDefaultValidation` is + /// `false`. `true` by default. + /// - options: The `Options` to use to check the revocation status of the certificate. `.any` + /// by default. + public init(performDefaultValidation: Bool = true, validateHost: Bool = true, options: Options = .any) { + self.performDefaultValidation = performDefaultValidation + self.validateHost = validateHost + self.options = options + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + if performDefaultValidation { + try trust.af.performDefaultValidation(forHost: host) + } + + if validateHost { + try trust.af.performValidation(forHost: host) + } + + if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) { + try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options)) + } else { + try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in + AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options)) + } + } + } +} + +/// 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. +public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating { + private let certificates: [SecCertificate] + private let acceptSelfSignedCertificates: Bool + private let performDefaultValidation: Bool + private let validateHost: Bool + + /// Creates a `PinnedCertificatesTrustEvaluator`. + /// + /// - Parameters: + /// - certificates: The certificates to use to evaluate the trust. All `cer`, `crt`, and `der` + /// certificates in `Bundle.main` by default. + /// - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing + /// self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE + /// FALSE IN PRODUCTION! + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition + /// to performing the default evaluation, even if `performDefaultValidation` is + /// `false`. `true` by default. + public init(certificates: [SecCertificate] = Bundle.main.af.certificates, + acceptSelfSignedCertificates: Bool = false, + performDefaultValidation: Bool = true, + validateHost: Bool = true) { + self.certificates = certificates + self.acceptSelfSignedCertificates = acceptSelfSignedCertificates + self.performDefaultValidation = performDefaultValidation + self.validateHost = validateHost + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + guard !certificates.isEmpty else { + throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound) + } + + if acceptSelfSignedCertificates { + try trust.af.setAnchorCertificates(certificates) + } + + if performDefaultValidation { + try trust.af.performDefaultValidation(forHost: host) + } + + if validateHost { + try trust.af.performValidation(forHost: host) + } + + let serverCertificatesData = Set(trust.af.certificateData) + let pinnedCertificatesData = Set(certificates.af.data) + let pinnedCertificatesInServerData = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData) + if !pinnedCertificatesInServerData { + throw AFError.serverTrustEvaluationFailed(reason: .certificatePinningFailed(host: host, + trust: trust, + pinnedCertificates: certificates, + serverCertificates: trust.af.certificates)) + } + } +} + +/// 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. +public final class PublicKeysTrustEvaluator: ServerTrustEvaluating { + private let keys: [SecKey] + private let performDefaultValidation: Bool + private let validateHost: Bool + + /// Creates a `PublicKeysTrustEvaluator`. + /// + /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use + /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. + /// + /// - Parameters: + /// - keys: The `SecKey`s to use to validate public keys. Defaults to the public keys of all + /// certificates included in the main bundle. + /// - performDefaultValidation: Determines whether default validation should be performed in addition to + /// evaluating the pinned certificates. `true` by default. + /// - validateHost: Determines whether or not the evaluator should validate the host, in addition to + /// performing the default evaluation, even if `performDefaultValidation` is `false`. + /// `true` by default. + public init(keys: [SecKey] = Bundle.main.af.publicKeys, + performDefaultValidation: Bool = true, + validateHost: Bool = true) { + self.keys = keys + self.performDefaultValidation = performDefaultValidation + self.validateHost = validateHost + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + guard !keys.isEmpty else { + throw AFError.serverTrustEvaluationFailed(reason: .noPublicKeysFound) + } + + if performDefaultValidation { + try trust.af.performDefaultValidation(forHost: host) + } + + if validateHost { + try trust.af.performValidation(forHost: host) + } + + let pinnedKeysInServerKeys: Bool = { + for serverPublicKey in trust.af.publicKeys { + for pinnedPublicKey in keys { + if serverPublicKey == pinnedPublicKey { + return true + } + } + } + return false + }() + + if !pinnedKeysInServerKeys { + throw AFError.serverTrustEvaluationFailed(reason: .publicKeyPinningFailed(host: host, + trust: trust, + pinnedKeys: keys, + serverKeys: trust.af.publicKeys)) + } + } +} + +/// Uses the provided evaluators to validate the server trust. The trust is only considered valid if all of the +/// evaluators consider it valid. +public final class CompositeTrustEvaluator: ServerTrustEvaluating { + private let evaluators: [ServerTrustEvaluating] + + /// Creates a `CompositeTrustEvaluator`. + /// + /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust. + public init(evaluators: [ServerTrustEvaluating]) { + self.evaluators = evaluators + } + + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + try evaluators.evaluate(trust, forHost: host) + } +} + +/// Disables all evaluation which in turn will always consider any server trust as valid. +/// +/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test +/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html). +/// +/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!** +@available(*, deprecated, renamed: "DisabledTrustEvaluator", message: "DisabledEvaluator has been renamed DisabledTrustEvaluator.") +public typealias DisabledEvaluator = DisabledTrustEvaluator + +/// Disables all evaluation which in turn will always consider any server trust as valid. +/// +/// +/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test +/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html). +/// +/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!** +public final class DisabledTrustEvaluator: ServerTrustEvaluating { + /// Creates an instance. + public init() {} + + public func evaluate(_ trust: SecTrust, forHost host: String) throws {} +} + +// MARK: - Extensions + +extension Array where Element == ServerTrustEvaluating { + #if os(Linux) + // Add this same convenience method for Linux. + #else + /// Evaluates the given `SecTrust` value for the given `host`. + /// + /// - Parameters: + /// - trust: The `SecTrust` value to evaluate. + /// - host: The host for which to evaluate the `SecTrust` value. + /// + /// - Returns: Whether or not the evaluator considers the `SecTrust` value valid for `host`. + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + for evaluator in self { + try evaluator.evaluate(trust, forHost: host) + } + } + #endif +} + +extension Bundle: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: Bundle { + /// Returns all valid `cer`, `crt`, and `der` certificates in the bundle. + public var certificates: [SecCertificate] { + paths(forResourcesOfTypes: [".cer", ".CER", ".crt", ".CRT", ".der", ".DER"]).compactMap { path in + guard + let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, + let certificate = SecCertificateCreateWithData(nil, certificateData) else { return nil } + + return certificate + } + } + + /// Returns all public keys for the valid certificates in the bundle. + public var publicKeys: [SecKey] { + certificates.af.publicKeys + } + + /// Returns all pathnames for the resources identified by the provided file extensions. + /// + /// - Parameter types: The filename extensions locate. + /// + /// - Returns: All pathnames for the given filename extensions. + public func paths(forResourcesOfTypes types: [String]) -> [String] { + Array(Set(types.flatMap { type.paths(forResourcesOfType: $0, inDirectory: nil) })) + } +} + +extension SecTrust: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecTrust { + /// Evaluates `self` after applying the `SecPolicy` value provided. + /// + /// - Parameter policy: The `SecPolicy` to apply to `self` before evaluation. + /// + /// - Throws: Any `Error` from applying the `SecPolicy` or from evaluation. + @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) + public func evaluate(afterApplying policy: SecPolicy) throws { + try apply(policy: policy).af.evaluate() + } + + /// Attempts to validate `self` using the `SecPolicy` provided and transforming any error produced using the closure passed. + /// + /// - Parameters: + /// - policy: The `SecPolicy` used to evaluate `self`. + /// - errorProducer: The closure used transform the failed `OSStatus` and `SecTrustResultType`. + /// - Throws: Any `Error` from applying the `policy`, or the result of `errorProducer` if validation fails. + @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)") + @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate(afterApplying:)") + @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)") + @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate(afterApplying:)") + public func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { + try apply(policy: policy).af.validate(errorProducer: errorProducer) + } + + /// Applies a `SecPolicy` to `self`, throwing if it fails. + /// + /// - Parameter policy: The `SecPolicy`. + /// + /// - Returns: `self`, with the policy applied. + /// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.policyApplicationFailed` reason. + public func apply(policy: SecPolicy) throws -> SecTrust { + let status = SecTrustSetPolicies(type, policy) + + guard status.af.isSuccess else { + throw AFError.serverTrustEvaluationFailed(reason: .policyApplicationFailed(trust: type, + policy: policy, + status: status)) + } + + return type + } + + /// Evaluate `self`, throwing an `Error` if evaluation fails. + /// + /// - Throws: `AFError.serverTrustEvaluationFailed` with reason `.trustValidationFailed` and associated error from + /// the underlying evaluation. + @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) + public func evaluate() throws { + var error: CFError? + let evaluationSucceeded = SecTrustEvaluateWithError(type, &error) + + if !evaluationSucceeded { + throw AFError.serverTrustEvaluationFailed(reason: .trustEvaluationFailed(error: error)) + } + } + + /// Validate `self`, passing any failure values through `errorProducer`. + /// + /// - Parameter errorProducer: The closure used to transform the failed `OSStatus` and `SecTrustResultType` into an + /// `Error`. + /// - Throws: The `Error` produced by the `errorProducer` closure. + @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate()") + @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate()") + @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate()") + @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate()") + public func validate(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { + var result = SecTrustResultType.invalid + let status = SecTrustEvaluate(type, &result) + + guard status.af.isSuccess && result.af.isSuccess else { + throw errorProducer(status, result) + } + } + + /// Sets a custom certificate chain on `self`, allowing full validation of a self-signed certificate and its chain. + /// + /// - Parameter certificates: The `SecCertificate`s to add to the chain. + /// - Throws: Any error produced when applying the new certificate chain. + public func setAnchorCertificates(_ certificates: [SecCertificate]) throws { + // Add additional anchor certificates. + let status = SecTrustSetAnchorCertificates(type, certificates as CFArray) + guard status.af.isSuccess else { + throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: status, + certificates: certificates)) + } + + // Trust only the set anchor certs. + let onlyStatus = SecTrustSetAnchorCertificatesOnly(type, true) + guard onlyStatus.af.isSuccess else { + throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: onlyStatus, + certificates: certificates)) + } + } + + /// The public keys contained in `self`. + public var publicKeys: [SecKey] { + certificates.af.publicKeys + } + + /// The `SecCertificate`s contained i `self`. + public var certificates: [SecCertificate] { + (0.. SecPolicy { + SecPolicyCreateSSL(true, hostname as CFString) + } + + /// Creates a `SecPolicy` which checks the revocation of certificates. + /// + /// - Parameter options: The `RevocationTrustEvaluator.Options` for evaluation. + /// + /// - Returns: The `SecPolicy`. + /// - Throws: An `AFError.serverTrustEvaluationFailed` error with reason `.revocationPolicyCreationFailed` + /// if the policy cannot be created. + public static func revocation(options: RevocationTrustEvaluator.Options) throws -> SecPolicy { + guard let policy = SecPolicyCreateRevocation(options.rawValue) else { + throw AFError.serverTrustEvaluationFailed(reason: .revocationPolicyCreationFailed) + } + + return policy + } +} + +extension Array: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == [SecCertificate] { + /// All `Data` values for the contained `SecCertificate`s. + public var data: [Data] { + type.map { SecCertificateCopyData($0) as Data } + } + + /// All public `SecKey` values for the contained `SecCertificate`s. + public var publicKeys: [SecKey] { + type.compactMap { $0.af.publicKey } + } +} + +extension SecCertificate: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecCertificate { + /// The public key for `self`, if it can be extracted. + public var publicKey: SecKey? { + let policy = SecPolicyCreateBasicX509() + var trust: SecTrust? + let trustCreationStatus = SecTrustCreateWithCertificates(type, policy, &trust) + + guard let createdTrust = trust, trustCreationStatus == errSecSuccess else { return nil } + + return SecTrustCopyPublicKey(createdTrust) + } +} + +extension OSStatus: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == OSStatus { + /// Returns whether `self` is `errSecSuccess`. + public var isSuccess: Bool { type == errSecSuccess } +} + +extension SecTrustResultType: AlamofireExtended {} +extension AlamofireExtension where ExtendedType == SecTrustResultType { + /// Returns whether `self is `.unspecified` or `.proceed`. + public var isSuccess: Bool { + type == .unspecified || type == .proceed + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift new file mode 100644 index 00000000..ac0ab229 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift @@ -0,0 +1,1258 @@ +// +// Session.swift +// +// Copyright (c) 2014-2018 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 + +/// `Session` creates and manages Alamofire's `Request` types during their lifetimes. It also provides common +/// functionality for all `Request`s, including queuing, interception, trust management, redirect handling, and response +/// cache handling. +open class Session { + /// Shared singleton instance used by all `AF.request` APIs. Cannot be modified. + public static let `default` = Session() + + /// Underlying `URLSession` used to create `URLSessionTasks` for this instance, and for which this instance's + /// `delegate` handles `URLSessionDelegate` callbacks. + /// + /// - Note: This instance should **NOT** be used to interact with the underlying `URLSessionTask`s. Doing so will + /// break internal Alamofire logic that tracks those tasks. + /// + public let session: URLSession + /// Instance's `SessionDelegate`, which handles the `URLSessionDelegate` methods and `Request` interaction. + public let delegate: SessionDelegate + /// Root `DispatchQueue` for all internal callbacks and state update. **MUST** be a serial queue. + public let rootQueue: DispatchQueue + /// Value determining whether this instance automatically calls `resume()` on all created `Request`s. + public let startRequestsImmediately: Bool + /// `DispatchQueue` on which `URLRequest`s are created asynchronously. By default this queue uses `rootQueue` as its + /// `target`, but a separate queue can be used if request creation is determined to be a bottleneck. Always profile + /// and test before introducing an additional queue. + public let requestQueue: DispatchQueue + /// `DispatchQueue` passed to all `Request`s on which they perform their response serialization. By default this + /// queue uses `rootQueue` as its `target` but a separate queue can be used if response serialization is determined + /// to be a bottleneck. Always profile and test before introducing an additional queue. + public let serializationQueue: DispatchQueue + /// `RequestInterceptor` used for all `Request` created by the instance. `RequestInterceptor`s can also be set on a + /// per-`Request` basis, in which case the `Request`'s interceptor takes precedence over this value. + public let interceptor: RequestInterceptor? + /// `ServerTrustManager` instance used to evaluate all trust challenges and provide certificate and key pinning. + public let serverTrustManager: ServerTrustManager? + /// `RedirectHandler` instance used to provide customization for request redirection. + public let redirectHandler: RedirectHandler? + /// `CachedResponseHandler` instance used to provide customization of cached response handling. + public let cachedResponseHandler: CachedResponseHandler? + /// `CompositeEventMonitor` used to compose Alamofire's `defaultEventMonitors` and any passed `EventMonitor`s. + public let eventMonitor: CompositeEventMonitor + /// `EventMonitor`s included in all instances. `[AlamofireNotifications()]` by default. + public let defaultEventMonitors: [EventMonitor] = [AlamofireNotifications()] + + /// Internal map between `Request`s and any `URLSessionTasks` that may be in flight for them. + var requestTaskMap = RequestTaskMap() + /// `Set` of currently active `Request`s. + var activeRequests: Set = [] + /// Completion events awaiting `URLSessionTaskMetrics`. + var waitingCompletions: [URLSessionTask: () -> Void] = [:] + + /// Creates a `Session` from a `URLSession` and other parameters. + /// + /// - Note: When passing a `URLSession`, you must create the `URLSession` with a specific `delegateQueue` value and + /// pass the `delegateQueue`'s `underlyingQueue` as the `rootQueue` parameter of this initializer. + /// + /// - Parameters: + /// - session: Underlying `URLSession` for this instance. + /// - delegate: `SessionDelegate` that handles `session`'s delegate callbacks as well as `Request` + /// interaction. + /// - rootQueue: Root `DispatchQueue` for all internal callbacks and state updates. **MUST** be a + /// serial queue. + /// - startRequestsImmediately: Determines whether this instance will automatically start all `Request`s. `true` + /// by default. If set to `false`, all `Request`s created must have `.resume()` called. + /// on them for them to start. + /// - requestQueue: `DispatchQueue` on which to perform `URLRequest` creation. By default this queue + /// will use the `rootQueue` as its `target`. A separate queue can be used if it's + /// determined request creation is a bottleneck, but that should only be done after + /// careful testing and profiling. `nil` by default. + /// - serializationQueue: `DispatchQueue` on which to perform all response serialization. By default this + /// queue will use the `rootQueue` as its `target`. A separate queue can be used if + /// it's determined response serialization is a bottleneck, but that should only be + /// done after careful testing and profiling. `nil` by default. + /// - interceptor: `RequestInterceptor` to be used for all `Request`s created by this instance. `nil` + /// by default. + /// - serverTrustManager: `ServerTrustManager` to be used for all trust evaluations by this instance. `nil` + /// by default. + /// - redirectHandler: `RedirectHandler` to be used by all `Request`s created by this instance. `nil` by + /// default. + /// - cachedResponseHandler: `CachedResponseHandler` to be used by all `Request`s created by this instance. + /// `nil` by default. + /// - eventMonitors: Additional `EventMonitor`s used by the instance. Alamofire always adds a + /// `AlamofireNotifications` `EventMonitor` to the array passed here. `[]` by default. + public init(session: URLSession, + delegate: SessionDelegate, + rootQueue: DispatchQueue, + startRequestsImmediately: Bool = true, + requestQueue: DispatchQueue? = nil, + serializationQueue: DispatchQueue? = nil, + interceptor: RequestInterceptor? = nil, + serverTrustManager: ServerTrustManager? = nil, + redirectHandler: RedirectHandler? = nil, + cachedResponseHandler: CachedResponseHandler? = nil, + eventMonitors: [EventMonitor] = []) { + precondition(session.configuration.identifier == nil, + "Alamofire does not support background URLSessionConfigurations.") + precondition(session.delegateQueue.underlyingQueue === rootQueue, + "Session(session:) initializer must be passed the DispatchQueue used as the delegateQueue's underlyingQueue as rootQueue.") + + self.session = session + self.delegate = delegate + self.rootQueue = rootQueue + self.startRequestsImmediately = startRequestsImmediately + self.requestQueue = requestQueue ?? DispatchQueue(label: "\(rootQueue.label).requestQueue", target: rootQueue) + self.serializationQueue = serializationQueue ?? DispatchQueue(label: "\(rootQueue.label).serializationQueue", target: rootQueue) + self.interceptor = interceptor + self.serverTrustManager = serverTrustManager + self.redirectHandler = redirectHandler + self.cachedResponseHandler = cachedResponseHandler + eventMonitor = CompositeEventMonitor(monitors: defaultEventMonitors + eventMonitors) + delegate.eventMonitor = eventMonitor + delegate.stateProvider = self + } + + /// Creates a `Session` from a `URLSessionConfiguration`. + /// + /// - Note: This initializer lets Alamofire handle the creation of the underlying `URLSession` and its + /// `delegateQueue`, and is the recommended initializer for most uses. + /// + /// - Parameters: + /// - configuration: `URLSessionConfiguration` to be used to create the underlying `URLSession`. Changes + /// to this value after being passed to this initializer will have no effect. + /// `URLSessionConfiguration.af.default` by default. + /// - delegate: `SessionDelegate` that handles `session`'s delegate callbacks as well as `Request` + /// interaction. `SessionDelegate()` by default. + /// - rootQueue: Root `DispatchQueue` for all internal callbacks and state updates. **MUST** be a + /// serial queue. `DispatchQueue(label: "org.alamofire.session.rootQueue")` by default. + /// - startRequestsImmediately: Determines whether this instance will automatically start all `Request`s. `true` + /// by default. If set to `false`, all `Request`s created must have `.resume()` called. + /// on them for them to start. + /// - requestQueue: `DispatchQueue` on which to perform `URLRequest` creation. By default this queue + /// will use the `rootQueue` as its `target`. A separate queue can be used if it's + /// determined request creation is a bottleneck, but that should only be done after + /// careful testing and profiling. `nil` by default. + /// - serializationQueue: `DispatchQueue` on which to perform all response serialization. By default this + /// queue will use the `rootQueue` as its `target`. A separate queue can be used if + /// it's determined response serialization is a bottleneck, but that should only be + /// done after careful testing and profiling. `nil` by default. + /// - interceptor: `RequestInterceptor` to be used for all `Request`s created by this instance. `nil` + /// by default. + /// - serverTrustManager: `ServerTrustManager` to be used for all trust evaluations by this instance. `nil` + /// by default. + /// - redirectHandler: `RedirectHandler` to be used by all `Request`s created by this instance. `nil` by + /// default. + /// - cachedResponseHandler: `CachedResponseHandler` to be used by all `Request`s created by this instance. + /// `nil` by default. + /// - eventMonitors: Additional `EventMonitor`s used by the instance. Alamofire always adds a + /// `AlamofireNotifications` `EventMonitor` to the array passed here. `[]` by default. + public convenience init(configuration: URLSessionConfiguration = URLSessionConfiguration.af.default, + delegate: SessionDelegate = SessionDelegate(), + rootQueue: DispatchQueue = DispatchQueue(label: "org.alamofire.session.rootQueue"), + startRequestsImmediately: Bool = true, + requestQueue: DispatchQueue? = nil, + serializationQueue: DispatchQueue? = nil, + interceptor: RequestInterceptor? = nil, + serverTrustManager: ServerTrustManager? = nil, + redirectHandler: RedirectHandler? = nil, + cachedResponseHandler: CachedResponseHandler? = nil, + eventMonitors: [EventMonitor] = []) { + precondition(configuration.identifier == nil, "Alamofire does not support background URLSessionConfigurations.") + + let delegateQueue = OperationQueue(maxConcurrentOperationCount: 1, underlyingQueue: rootQueue, name: "org.alamofire.session.sessionDelegateQueue") + let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue) + + self.init(session: session, + delegate: delegate, + rootQueue: rootQueue, + startRequestsImmediately: startRequestsImmediately, + requestQueue: requestQueue, + serializationQueue: serializationQueue, + interceptor: interceptor, + serverTrustManager: serverTrustManager, + redirectHandler: redirectHandler, + cachedResponseHandler: cachedResponseHandler, + eventMonitors: eventMonitors) + } + + deinit { + finishRequestsForDeinit() + session.invalidateAndCancel() + } + + // MARK: - All Requests API + + /// Perform an action on all active `Request`s. + /// + /// - Note: The provided `action` closure is performed asynchronously, meaning that some `Request`s may complete and + /// be unavailable by time it runs. Additionally, this action is performed on the instances's `rootQueue`, + /// so care should be taken that actions are fast. Once the work on the `Request`s is complete, any + /// additional work should be performed on another queue. + /// + /// - Parameters: + /// - action: Closure to perform with all `Request`s. + public func withAllRequests(perform action: @escaping (Set) -> Void) { + rootQueue.async { + action(self.activeRequests) + } + } + + /// Cancel all active `Request`s, optionally calling a completion handler when complete. + /// + /// - Note: This is an asynchronous operation and does not block the creation of future `Request`s. Cancelled + /// `Request`s may not cancel immediately due internal work, and may not cancel at all if they are close to + /// completion when cancelled. + /// + /// - Parameters: + /// - queue: `DispatchQueue` on which the completion handler is run. `.main` by default. + /// - completion: Closure to be called when all `Request`s have been cancelled. + public func cancelAllRequests(completingOnQueue queue: DispatchQueue = .main, completion: (() -> Void)? = nil) { + withAllRequests { requests in + requests.forEach { $0.cancel() } + queue.async { + completion?() + } + } + } + + // MARK: - DataRequest + + /// Closure which provides a `URLRequest` for mutation. + public typealias RequestModifier = (inout URLRequest) throws -> Void + + struct RequestConvertible: URLRequestConvertible { + let url: URLConvertible + let method: HTTPMethod + let parameters: Parameters? + let encoding: ParameterEncoding + let headers: HTTPHeaders? + let requestModifier: RequestModifier? + + func asURLRequest() throws -> URLRequest { + var request = try URLRequest(url: url, method: method, headers: headers) + try requestModifier?(&request) + + return try encoding.encode(request, with: parameters) + } + } + + /// Creates a `DataRequest` from a `URLRequest` created using the passed components and a `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by + /// default. + /// - encoding: `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`. + /// `URLEncoding.default` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `DataRequest`. + open func request(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataRequest { + let convertible = RequestConvertible(url: convertible, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers, + requestModifier: requestModifier) + + return request(convertible, interceptor: interceptor) + } + + struct RequestEncodableConvertible: URLRequestConvertible { + let url: URLConvertible + let method: HTTPMethod + let parameters: Parameters? + let encoder: ParameterEncoder + let headers: HTTPHeaders? + let requestModifier: RequestModifier? + + func asURLRequest() throws -> URLRequest { + var request = try URLRequest(url: url, method: method, headers: headers) + try requestModifier?(&request) + + return try parameters.map { try encoder.encode($0, into: request) } ?? request + } + } + + /// Creates a `DataRequest` from a `URLRequest` created using the passed components, `Encodable` parameters, and a + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Encodable` value to be encoded into the `URLRequest`. `nil` by default. + /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the `URLRequest`. + /// `URLEncodedFormParameterEncoder.default` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// + /// - Returns: The created `DataRequest`. + open func request(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + + return request(convertible, interceptor: interceptor) + } + + /// Creates a `DataRequest` from a `URLRequestConvertible` value and a `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// + /// - Returns: The created `DataRequest`. + open func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest { + let request = DataRequest(convertible: convertible, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self) + + perform(request) + + return request + } + + // MARK: - DataStreamRequest + + /// Creates a `DataStreamRequest` from the passed components, `Encodable` parameters, and `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Encodable` value to be encoded into the `URLRequest`. `nil` by default. + /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the + /// `URLRequest`. + /// `URLEncodedFormParameterEncoder.default` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` + /// is thrown while serializing stream `Data`. `false` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` + /// by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from + /// the provided parameters. `nil` by default. + /// + /// - Returns: The created `DataStream` request. + open func streamRequest(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataStreamRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + + return streamRequest(convertible, + automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, + interceptor: interceptor) + } + + /// Creates a `DataStreamRequest` from the passed components and `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` + /// is thrown while serializing stream `Data`. `false` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` + /// by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from + /// the provided parameters. `nil` by default. + /// + /// - Returns: The created `DataStream` request. + open func streamRequest(_ convertible: URLConvertible, + method: HTTPMethod = .get, + headers: HTTPHeaders? = nil, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataStreamRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: Empty?.none, + encoder: URLEncodedFormParameterEncoder.default, + headers: headers, + requestModifier: requestModifier) + + return streamRequest(convertible, + automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, + interceptor: interceptor) + } + + /// Creates a `DataStreamRequest` from the passed `URLRequestConvertible` value and `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` + /// is thrown while serializing stream `Data`. `false` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` + /// by default. + /// + /// - Returns: The created `DataStreamRequest`. + open func streamRequest(_ convertible: URLRequestConvertible, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil) -> DataStreamRequest { + let request = DataStreamRequest(convertible: convertible, + automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self) + + perform(request) + + return request + } + + // MARK: - DownloadRequest + + /// Creates a `DownloadRequest` using a `URLRequest` created using the passed components, `RequestInterceptor`, and + /// `Destination`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by + /// default. + /// - encoding: `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`. + /// Defaults to `URLEncoding.default`. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let convertible = RequestConvertible(url: convertible, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers, + requestModifier: requestModifier) + + return download(convertible, interceptor: interceptor, to: destination) + } + + /// Creates a `DownloadRequest` from a `URLRequest` created using the passed components, `Encodable` parameters, and + /// a `RequestInterceptor`. + /// + /// - Parameters: + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. + /// - parameters: Value conforming to `Encodable` to be encoded into the `URLRequest`. `nil` by default. + /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the `URLRequest`. + /// Defaults to `URLEncodedFormParameterEncoder.default`. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(_ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let convertible = RequestEncodableConvertible(url: convertible, + method: method, + parameters: parameters, + encoder: encoder, + headers: headers, + requestModifier: requestModifier) + + return download(convertible, interceptor: interceptor, to: destination) + } + + /// Creates a `DownloadRequest` from a `URLRequestConvertible` value, a `RequestInterceptor`, and a `Destination`. + /// + /// - Parameters: + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(_ convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let request = DownloadRequest(downloadable: .request(convertible), + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self, + destination: destination ?? DownloadRequest.defaultDestination) + + perform(request) + + return request + } + + /// Creates a `DownloadRequest` from the `resumeData` produced from a previously cancelled `DownloadRequest`, as + /// well as a `RequestInterceptor`, and a `Destination`. + /// + /// - Note: If `destination` is not specified, the download will be moved to a temporary location determined by + /// Alamofire. The file will not be deleted until the system purges the temporary files. + /// + /// - Note: On some versions of all Apple platforms (iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1), + /// `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 [this Stack Overflow post](http://stackoverflow.com/a/39347461/1342462). + /// + /// - Parameters: + /// - data: The resume data from a previously cancelled `DownloadRequest` or `URLSessionDownloadTask`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file + /// should be moved. `nil` by default. + /// + /// - Returns: The created `DownloadRequest`. + open func download(resumingWith data: Data, + interceptor: RequestInterceptor? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + let request = DownloadRequest(downloadable: .resumeData(data), + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + delegate: self, + destination: destination ?? DownloadRequest.defaultDestination) + + perform(request) + + return request + } + + // MARK: - UploadRequest + + struct ParameterlessRequestConvertible: URLRequestConvertible { + let url: URLConvertible + let method: HTTPMethod + let headers: HTTPHeaders? + let requestModifier: RequestModifier? + + func asURLRequest() throws -> URLRequest { + var request = try URLRequest(url: url, method: method, headers: headers) + try requestModifier?(&request) + + return request + } + } + + struct Upload: UploadConvertible { + let request: URLRequestConvertible + let uploadable: UploadableConvertible + + func createUploadable() throws -> UploadRequest.Uploadable { + try uploadable.createUploadable() + } + + func asURLRequest() throws -> URLRequest { + try request.asURLRequest() + } + } + + // MARK: Data + + /// Creates an `UploadRequest` for the given `Data`, `URLRequest` components, and `RequestInterceptor`. + /// + /// - Parameters: + /// - data: The `Data` to upload. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ data: Data, + to convertible: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: convertible, + method: method, + headers: headers, + requestModifier: requestModifier) + + return upload(data, with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the given `Data` using the `URLRequestConvertible` value and `RequestInterceptor`. + /// + /// - Parameters: + /// - data: The `Data` to upload. + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ data: Data, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + upload(.data(data), with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: File + + /// Creates an `UploadRequest` for the file at the given file `URL`, using a `URLRequest` from the provided + /// components and `RequestInterceptor`. + /// + /// - Parameters: + /// - fileURL: The `URL` of the file to upload. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `UploadRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ fileURL: URL, + to convertible: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: convertible, + method: method, + headers: headers, + requestModifier: requestModifier) + + return upload(fileURL, with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the file at the given file `URL` using the `URLRequestConvertible` value and + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - fileURL: The `URL` of the file to upload. + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ fileURL: URL, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + upload(.file(fileURL, shouldRemove: false), with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: InputStream + + /// Creates an `UploadRequest` from the `InputStream` provided using a `URLRequest` from the provided components and + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - stream: The `InputStream` that provides the data to upload. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided + /// parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ stream: InputStream, + to convertible: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: convertible, + method: method, + headers: headers, + requestModifier: requestModifier) + + return upload(stream, with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` from the provided `InputStream` using the `URLRequestConvertible` value and + /// `RequestInterceptor`. + /// + /// - Parameters: + /// - stream: The `InputStream` that provides the data to upload. + /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(_ stream: InputStream, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + upload(.stream(stream), with: convertible, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: MultipartFormData + + /// Creates an `UploadRequest` for the multipart form data built using a closure and sent using the provided + /// `URLRequest` components and `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// 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. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` building closure. + /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is + /// written to disk before being uploaded. `.default` instance by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the + /// provided parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: @escaping (MultipartFormData) -> Void, + to url: URLConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: url, + method: method, + headers: headers, + requestModifier: requestModifier) + + let formData = MultipartFormData(fileManager: fileManager) + multipartFormData(formData) + + return upload(multipartFormData: formData, + with: convertible, + usingThreshold: encodingMemoryThreshold, + interceptor: interceptor, + fileManager: fileManager) + } + + /// Creates an `UploadRequest` using a `MultipartFormData` building closure, the provided `URLRequestConvertible` + /// value, and a `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// 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. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` building closure. + /// - request: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is + /// written to disk before being uploaded. `.default` instance by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: @escaping (MultipartFormData) -> Void, + with request: URLRequestConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + let formData = MultipartFormData(fileManager: fileManager) + multipartFormData(formData) + + return upload(multipartFormData: formData, + with: request, + usingThreshold: encodingMemoryThreshold, + interceptor: interceptor, + fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the provided `URLRequest` components + /// and `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// 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. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` instance to upload. + /// - url: `URLConvertible` value to be used as the `URLRequest`'s `URL`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. + /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is + /// written to disk before being uploaded. `.default` instance by default. + /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the + /// provided parameters. `nil` by default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: MultipartFormData, + to url: URLConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default, + requestModifier: RequestModifier? = nil) -> UploadRequest { + let convertible = ParameterlessRequestConvertible(url: url, + method: method, + headers: headers, + requestModifier: requestModifier) + + let multipartUpload = MultipartUpload(encodingMemoryThreshold: encodingMemoryThreshold, + request: convertible, + multipartFormData: multipartFormData) + + return upload(multipartUpload, interceptor: interceptor, fileManager: fileManager) + } + + /// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the providing `URLRequestConvertible` + /// value and `RequestInterceptor`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative + /// 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. + /// + /// - Parameters: + /// - multipartFormData: `MultipartFormData` instance to upload. + /// - request: `URLRequestConvertible` value to be used to create the `URLRequest`. + /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or + /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by + /// default. + /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. + /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by + /// default. + /// + /// - Returns: The created `UploadRequest`. + open func upload(multipartFormData: MultipartFormData, + with request: URLRequestConvertible, + usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, + interceptor: RequestInterceptor? = nil, + fileManager: FileManager = .default) -> UploadRequest { + let multipartUpload = MultipartUpload(encodingMemoryThreshold: encodingMemoryThreshold, + request: request, + multipartFormData: multipartFormData) + + return upload(multipartUpload, interceptor: interceptor, fileManager: fileManager) + } + + // MARK: - Internal API + + // MARK: Uploadable + + func upload(_ uploadable: UploadRequest.Uploadable, + with convertible: URLRequestConvertible, + interceptor: RequestInterceptor?, + fileManager: FileManager) -> UploadRequest { + let uploadable = Upload(request: convertible, uploadable: uploadable) + + return upload(uploadable, interceptor: interceptor, fileManager: fileManager) + } + + func upload(_ upload: UploadConvertible, interceptor: RequestInterceptor?, fileManager: FileManager) -> UploadRequest { + let request = UploadRequest(convertible: upload, + underlyingQueue: rootQueue, + serializationQueue: serializationQueue, + eventMonitor: eventMonitor, + interceptor: interceptor, + fileManager: fileManager, + delegate: self) + + perform(request) + + return request + } + + // MARK: Perform + + /// Starts performing the provided `Request`. + /// + /// - Parameter request: The `Request` to perform. + func perform(_ request: Request) { + rootQueue.async { + guard !request.isCancelled else { return } + + self.activeRequests.insert(request) + + self.requestQueue.async { + // Leaf types must come first, otherwise they will cast as their superclass. + switch request { + case let r as UploadRequest: self.performUploadRequest(r) // UploadRequest must come before DataRequest due to subtype relationship. + case let r as DataRequest: self.performDataRequest(r) + case let r as DownloadRequest: self.performDownloadRequest(r) + case let r as DataStreamRequest: self.performDataStreamRequest(r) + default: fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))") + } + } + } + } + + func performDataRequest(_ request: DataRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) + } + + func performDataStreamRequest(_ request: DataStreamRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) + } + + func performUploadRequest(_ request: UploadRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + performSetupOperations(for: request, convertible: request.convertible) { + do { + let uploadable = try request.upload.createUploadable() + self.rootQueue.async { request.didCreateUploadable(uploadable) } + return true + } catch { + self.rootQueue.async { request.didFailToCreateUploadable(with: error.asAFError(or: .createUploadableFailed(error: error))) } + return false + } + } + } + + func performDownloadRequest(_ request: DownloadRequest) { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + switch request.downloadable { + case let .request(convertible): + performSetupOperations(for: request, convertible: convertible) + case let .resumeData(resumeData): + rootQueue.async { self.didReceiveResumeData(resumeData, for: request) } + } + } + + func performSetupOperations(for request: Request, + convertible: URLRequestConvertible, + shouldCreateTask: @escaping () -> Bool = { true }) + { + dispatchPrecondition(condition: .onQueue(requestQueue)) + + let initialRequest: URLRequest + + do { + initialRequest = try convertible.asURLRequest() + try initialRequest.validate() + } catch { + rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) } + return + } + + rootQueue.async { request.didCreateInitialURLRequest(initialRequest) } + + guard !request.isCancelled else { return } + + guard let adapter = adapter(for: request) else { + guard shouldCreateTask() else { return } + rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) } + return + } + + adapter.adapt(initialRequest, for: self) { result in + do { + let adaptedRequest = try result.get() + try adaptedRequest.validate() + + self.rootQueue.async { request.didAdaptInitialRequest(initialRequest, to: adaptedRequest) } + + guard shouldCreateTask() else { return } + + self.rootQueue.async { self.didCreateURLRequest(adaptedRequest, for: request) } + } catch { + self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) } + } + } + } + + // MARK: - Task Handling + + func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + request.didCreateURLRequest(urlRequest) + + guard !request.isCancelled else { return } + + let task = request.task(for: urlRequest, using: session) + requestTaskMap[request] = task + request.didCreateTask(task) + + updateStatesForTask(task, request: request) + } + + func didReceiveResumeData(_ data: Data, for request: DownloadRequest) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + guard !request.isCancelled else { return } + + let task = request.task(forResumeData: data, using: session) + requestTaskMap[request] = task + request.didCreateTask(task) + + updateStatesForTask(task, request: request) + } + + func updateStatesForTask(_ task: URLSessionTask, request: Request) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + request.withState { state in + switch state { + case .initialized, .finished: + // Do nothing. + break + case .resumed: + task.resume() + rootQueue.async { request.didResumeTask(task) } + case .suspended: + task.suspend() + rootQueue.async { request.didSuspendTask(task) } + case .cancelled: + // Resume to ensure metrics are gathered. + task.resume() + task.cancel() + rootQueue.async { request.didCancelTask(task) } + } + } + } + + // MARK: - Adapters and Retriers + + func adapter(for request: Request) -> RequestAdapter? { + if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { + return Interceptor(adapters: [requestInterceptor, sessionInterceptor]) + } else { + return request.interceptor ?? interceptor + } + } + + func retrier(for request: Request) -> RequestRetrier? { + if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { + return Interceptor(retriers: [requestInterceptor, sessionInterceptor]) + } else { + return request.interceptor ?? interceptor + } + } + + // MARK: - Invalidation + + func finishRequestsForDeinit() { + requestTaskMap.requests.forEach { request in + rootQueue.async { + request.finish(error: AFError.sessionDeinitialized) + } + } + } +} + +// MARK: - RequestDelegate + +extension Session: RequestDelegate { + public var sessionConfiguration: URLSessionConfiguration { + session.configuration + } + + public var startImmediately: Bool { startRequestsImmediately } + + public func cleanup(after request: Request) { + activeRequests.remove(request) + } + + public func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void) { + guard let retrier = retrier(for: request) else { + rootQueue.async { completion(.doNotRetry) } + return + } + + retrier.retry(request, for: self, dueTo: error) { retryResult in + self.rootQueue.async { + guard let retryResultError = retryResult.error else { completion(retryResult); return } + + let retryError = AFError.requestRetryFailed(retryError: retryResultError, originalError: error) + completion(.doNotRetryWithError(retryError)) + } + } + } + + public func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?) { + rootQueue.async { + let retry: () -> Void = { + guard !request.isCancelled else { return } + + request.prepareForRetry() + self.perform(request) + } + + if let retryDelay = timeDelay { + self.rootQueue.after(retryDelay) { retry() } + } else { + retry() + } + } + } +} + +// MARK: - SessionStateProvider + +extension Session: SessionStateProvider { + func request(for task: URLSessionTask) -> Request? { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + return requestTaskMap[task] + } + + func didGatherMetricsForTask(_ task: URLSessionTask) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterGatheringMetricsForTask(task) + + if didDisassociate { + waitingCompletions[task]?() + waitingCompletions[task] = nil + } + } + + func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterCompletingTask(task) + + if didDisassociate { + completion() + } else { + waitingCompletions[task] = completion + } + } + + func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + return requestTaskMap[task]?.credential ?? + session.configuration.urlCredentialStorage?.defaultCredential(for: protectionSpace) + } + + func cancelRequestsForSessionInvalidation(with error: Error?) { + dispatchPrecondition(condition: .onQueue(rootQueue)) + + requestTaskMap.requests.forEach { $0.finish(error: AFError.sessionInvalidated(error: error)) } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift new file mode 100644 index 00000000..befc80ea --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift @@ -0,0 +1,330 @@ +// +// SessionDelegate.swift +// +// Copyright (c) 2014-2018 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 + +/// Class which implements the various `URLSessionDelegate` methods to connect various Alamofire features. +open class SessionDelegate: NSObject { + private let fileManager: FileManager + + weak var stateProvider: SessionStateProvider? + var eventMonitor: EventMonitor? + + /// Creates an instance from the given `FileManager`. + /// + /// - Parameter fileManager: `FileManager` to use for underlying file management, such as moving downloaded files. + /// `.default` by default. + public init(fileManager: FileManager = .default) { + self.fileManager = fileManager + } + + /// Internal method to find and cast requests while maintaining some integrity checking. + /// + /// - Parameters: + /// - task: The `URLSessionTask` for which to find the associated `Request`. + /// - type: The `Request` subclass type to cast any `Request` associate with `task`. + func request(for task: URLSessionTask, as type: R.Type) -> R? { + guard let provider = stateProvider else { + assertionFailure("StateProvider is nil.") + return nil + } + + return provider.request(for: task) as? R + } +} + +/// Type which provides various `Session` state values. +protocol SessionStateProvider: AnyObject { + var serverTrustManager: ServerTrustManager? { get } + var redirectHandler: RedirectHandler? { get } + var cachedResponseHandler: CachedResponseHandler? { get } + + func request(for task: URLSessionTask) -> Request? + func didGatherMetricsForTask(_ task: URLSessionTask) + func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) + func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? + func cancelRequestsForSessionInvalidation(with error: Error?) +} + +// MARK: URLSessionDelegate + +extension SessionDelegate: URLSessionDelegate { + open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + eventMonitor?.urlSession(session, didBecomeInvalidWithError: error) + + stateProvider?.cancelRequestsForSessionInvalidation(with: error) + } +} + +// MARK: URLSessionTaskDelegate + +extension SessionDelegate: URLSessionTaskDelegate { + /// Result of a `URLAuthenticationChallenge` evaluation. + typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?, error: AFError?) + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + eventMonitor?.urlSession(session, task: task, didReceive: challenge) + + let evaluation: ChallengeEvaluation + switch challenge.protectionSpace.authenticationMethod { + case NSURLAuthenticationMethodServerTrust: + evaluation = attemptServerTrustAuthentication(with: challenge) + case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM, + NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate: + evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task) + default: + evaluation = (.performDefaultHandling, nil, nil) + } + + if let error = evaluation.error { + stateProvider?.request(for: task)?.didFailTask(task, earlyWithError: error) + } + + completionHandler(evaluation.disposition, evaluation.credential) + } + + /// Evaluates the server trust `URLAuthenticationChallenge` received. + /// + /// - Parameter challenge: The `URLAuthenticationChallenge`. + /// + /// - Returns: The `ChallengeEvaluation`. + func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation { + let host = challenge.protectionSpace.host + + guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, + let trust = challenge.protectionSpace.serverTrust + else { + return (.performDefaultHandling, nil, nil) + } + + do { + guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else { + return (.performDefaultHandling, nil, nil) + } + + try evaluator.evaluate(trust, forHost: host) + + return (.useCredential, URLCredential(trust: trust), nil) + } catch { + return (.cancelAuthenticationChallenge, nil, error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error)))) + } + } + + /// Evaluates the credential-based authentication `URLAuthenticationChallenge` received for `task`. + /// + /// - Parameters: + /// - challenge: The `URLAuthenticationChallenge`. + /// - task: The `URLSessionTask` which received the challenge. + /// + /// - Returns: The `ChallengeEvaluation`. + func attemptCredentialAuthentication(for challenge: URLAuthenticationChallenge, + belongingTo task: URLSessionTask) -> ChallengeEvaluation { + guard challenge.previousFailureCount == 0 else { + return (.rejectProtectionSpace, nil, nil) + } + + guard let credential = stateProvider?.credential(for: task, in: challenge.protectionSpace) else { + return (.performDefaultHandling, nil, nil) + } + + return (.useCredential, credential, nil) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) { + eventMonitor?.urlSession(session, + task: task, + didSendBodyData: bytesSent, + totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend) + + stateProvider?.request(for: task)?.updateUploadProgress(totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) { + eventMonitor?.urlSession(session, taskNeedsNewBodyStream: task) + + guard let request = request(for: task, as: UploadRequest.self) else { + assertionFailure("needNewBodyStream did not find UploadRequest.") + completionHandler(nil) + return + } + + completionHandler(request.inputStream()) + } + + open func urlSession(_ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: @escaping (URLRequest?) -> Void) { + eventMonitor?.urlSession(session, task: task, willPerformHTTPRedirection: response, newRequest: request) + + if let redirectHandler = stateProvider?.request(for: task)?.redirectHandler ?? stateProvider?.redirectHandler { + redirectHandler.task(task, willBeRedirectedTo: request, for: response, completion: completionHandler) + } else { + completionHandler(request) + } + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + eventMonitor?.urlSession(session, task: task, didFinishCollecting: metrics) + + stateProvider?.request(for: task)?.didGatherMetrics(metrics) + + stateProvider?.didGatherMetricsForTask(task) + } + + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + eventMonitor?.urlSession(session, task: task, didCompleteWithError: error) + + let request = stateProvider?.request(for: task) + + stateProvider?.didCompleteTask(task) { + request?.didCompleteTask(task, with: error.map { $0.asAFError(or: .sessionTaskFailed(error: $0)) }) + } + } + + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) + open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { + eventMonitor?.urlSession(session, taskIsWaitingForConnectivity: task) + } +} + +// MARK: URLSessionDataDelegate + +extension SessionDelegate: URLSessionDataDelegate { + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data) + + if let request = request(for: dataTask, as: DataRequest.self) { + request.didReceive(data: data) + } else if let request = request(for: dataTask, as: DataStreamRequest.self) { + request.didReceive(data: data) + } else { + assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive") + return + } + } + + open func urlSession(_ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse, + completionHandler: @escaping (CachedURLResponse?) -> Void) { + eventMonitor?.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) + + if let handler = stateProvider?.request(for: dataTask)?.cachedResponseHandler ?? stateProvider?.cachedResponseHandler { + handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler) + } else { + completionHandler(proposedResponse) + } + } +} + +// MARK: URLSessionDownloadDelegate + +extension SessionDelegate: URLSessionDownloadDelegate { + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) { + eventMonitor?.urlSession(session, + downloadTask: downloadTask, + didResumeAtOffset: fileOffset, + expectedTotalBytes: expectedTotalBytes) + guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else { + assertionFailure("downloadTask did not find DownloadRequest.") + return + } + + downloadRequest.updateDownloadProgress(bytesWritten: fileOffset, + totalBytesExpectedToWrite: expectedTotalBytes) + } + + open func urlSession(_ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) { + eventMonitor?.urlSession(session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite) + guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else { + assertionFailure("downloadTask did not find DownloadRequest.") + return + } + + downloadRequest.updateDownloadProgress(bytesWritten: bytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite) + } + + open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { + eventMonitor?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) + + guard let request = request(for: downloadTask, as: DownloadRequest.self) else { + assertionFailure("downloadTask did not find DownloadRequest.") + return + } + + let (destination, options): (URL, DownloadRequest.Options) + if let response = request.response { + (destination, options) = request.destination(location, response) + } else { + // If there's no response this is likely a local file download, so generate the temporary URL directly. + (destination, options) = (DownloadRequest.defaultDestinationURL(location), []) + } + + eventMonitor?.request(request, didCreateDestinationURL: destination) + + do { + if options.contains(.removePreviousFile), fileManager.fileExists(atPath: destination.path) { + try fileManager.removeItem(at: destination) + } + + if options.contains(.createIntermediateDirectories) { + let directory = destination.deletingLastPathComponent() + try fileManager.createDirectory(at: directory, withIntermediateDirectories: true) + } + + try fileManager.moveItem(at: location, to: destination) + + request.didFinishDownloading(using: downloadTask, with: .success(destination)) + } catch { + request.didFinishDownloading(using: downloadTask, with: .failure(.downloadedFileMoveFailed(error: error, + source: location, + destination: destination))) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift new file mode 100644 index 00000000..8fa61333 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift @@ -0,0 +1,55 @@ +// +// StringEncoding+Alamofire.swift +// +// Copyright (c) 2020 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 String.Encoding { + /// Creates an encoding from the IANA charset name. + /// + /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html) + /// + /// - Parameter name: IANA charset name. + init?(ianaCharsetName name: String) { + switch name.lowercased() { + case "utf-8": + self = .utf8 + case "iso-8859-1": + self = .isoLatin1 + case "unicode-1-1", "iso-10646-ucs-2", "utf-16": + self = .utf16 + case "utf-16be": + self = .utf16BigEndian + case "utf-16le": + self = .utf16LittleEndian + case "utf-32": + self = .utf32 + case "utf-32be": + self = .utf32BigEndian + case "utf-32le": + self = .utf32LittleEndian + default: + return nil + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift new file mode 100644 index 00000000..455c4bcb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift @@ -0,0 +1,105 @@ +// +// URLConvertible+URLRequestConvertible.swift +// +// Copyright (c) 2014-2018 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 `URL`s, which can then be used to construct +/// `URLRequests`. +public protocol URLConvertible { + /// Returns a `URL` from the conforming instance or throws. + /// + /// - Returns: The `URL` created from the instance. + /// - Throws: Any error thrown while creating the `URL`. + func asURL() throws -> URL +} + +extension String: URLConvertible { + /// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws. + /// + /// - Returns: The `URL` initialized with `self`. + /// - Throws: An `AFError.invalidURL` instance. + 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 { self } +} + +extension URLComponents: URLConvertible { + /// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws. + /// + /// - Returns: The `URL` from the `url` property. + /// - Throws: An `AFError.invalidURL` instance. + 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 safely construct `URLRequest`s. +public protocol URLRequestConvertible { + /// Returns a `URLRequest` or throws if an `Error` was encountered. + /// + /// - Returns: A `URLRequest`. + /// - Throws: Any error thrown while constructing the `URLRequest`. + func asURLRequest() throws -> URLRequest +} + +extension URLRequestConvertible { + /// The `URLRequest` returned by discarding any `Error` encountered. + public var urlRequest: URLRequest? { try? asURLRequest() } +} + +extension URLRequest: URLRequestConvertible { + /// Returns `self`. + public func asURLRequest() throws -> URLRequest { self } +} + +// MARK: - + +extension URLRequest { + /// Creates an instance with the specified `url`, `method`, and `headers`. + /// + /// - Parameters: + /// - url: The `URLConvertible` value. + /// - method: The `HTTPMethod`. + /// - headers: The `HTTPHeaders`, `nil` by default. + /// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`. + public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { + let url = try url.asURL() + + self.init(url: url) + + httpMethod = method.rawValue + allHTTPHeaderFields = headers?.dictionary + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift new file mode 100644 index 00000000..e5cc0c50 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift @@ -0,0 +1,976 @@ +// +// URLEncodedFormEncoder.swift +// +// Copyright (c) 2019 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 + +/// An object that encodes instances into URL-encoded query strings. +/// +/// There is no published specification for how to encode collection types. By default, 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`) is used. Optionally, `ArrayEncoding` can be used to omit the +/// square brackets appended to array keys. +/// +/// `BoolEncoding` can be used to configure how `Bool` values are encoded. The default behavior is to encode +/// `true` as 1 and `false` as 0. +/// +/// `DateEncoding` can be used to configure how `Date` values are encoded. By default, the `.deferredToDate` +/// strategy is used, which formats dates from their structure. +/// +/// `SpaceEncoding` can be used to configure how spaces are encoded. Modern encodings use percent replacement (`%20`), +/// while older encodings may expect spaces to be replaced with `+`. +/// +/// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project. +public final class URLEncodedFormEncoder { + /// Encoding to use for `Array` values. + public enum ArrayEncoding { + /// An empty set of square brackets ("[]") are appended to the key for every value. This is the default encoding. + case brackets + /// No brackets are appended to the key and the key is encoded as is. + case noBrackets + + /// Encodes the key according to the encoding. + /// + /// - Parameter key: The `key` to encode. + /// - Returns: The encoded key. + func encode(_ key: String) -> String { + switch self { + case .brackets: return "\(key)[]" + case .noBrackets: return key + } + } + } + + /// Encoding to use for `Bool` values. + public enum BoolEncoding { + /// Encodes `true` as `1`, `false` as `0`. + case numeric + /// Encodes `true` as "true", `false` as "false". This is the default encoding. + case literal + + /// Encodes the given `Bool` as a `String`. + /// + /// - Parameter value: The `Bool` to encode. + /// + /// - Returns: The encoded `String`. + func encode(_ value: Bool) -> String { + switch self { + case .numeric: return value ? "1" : "0" + case .literal: return value ? "true" : "false" + } + } + } + + /// Encoding to use for `Data` values. + public enum DataEncoding { + /// Defers encoding to the `Data` type. + case deferredToData + /// Encodes `Data` as a Base64-encoded string. This is the default encoding. + case base64 + /// Encode the `Data` as a custom value encoded by the given closure. + case custom((Data) throws -> String) + + /// Encodes `Data` according to the encoding. + /// + /// - Parameter data: The `Data` to encode. + /// + /// - Returns: The encoded `String`, or `nil` if the `Data` should be encoded according to its + /// `Encodable` implementation. + func encode(_ data: Data) throws -> String? { + switch self { + case .deferredToData: return nil + case .base64: return data.base64EncodedString() + case let .custom(encoding): return try encoding(data) + } + } + } + + /// Encoding to use for `Date` values. + public enum DateEncoding { + /// ISO8601 and RFC3339 formatter. + private static let iso8601Formatter: ISO8601DateFormatter = { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = .withInternetDateTime + return formatter + }() + + /// Defers encoding to the `Date` type. This is the default encoding. + case deferredToDate + /// Encodes `Date`s as seconds since midnight UTC on January 1, 1970. + case secondsSince1970 + /// Encodes `Date`s as milliseconds since midnight UTC on January 1, 1970. + case millisecondsSince1970 + /// Encodes `Date`s according to the ISO8601 and RFC3339 standards. + case iso8601 + /// Encodes `Date`s using the given `DateFormatter`. + case formatted(DateFormatter) + /// Encodes `Date`s using the given closure. + case custom((Date) throws -> String) + + /// Encodes the date according to the encoding. + /// + /// - Parameter date: The `Date` to encode. + /// + /// - Returns: The encoded `String`, or `nil` if the `Date` should be encoded according to its + /// `Encodable` implementation. + func encode(_ date: Date) throws -> String? { + switch self { + case .deferredToDate: + return nil + case .secondsSince1970: + return String(date.timeIntervalSince1970) + case .millisecondsSince1970: + return String(date.timeIntervalSince1970 * 1000.0) + case .iso8601: + return DateEncoding.iso8601Formatter.string(from: date) + case let .formatted(formatter): + return formatter.string(from: date) + case let .custom(closure): + return try closure(date) + } + } + } + + /// Encoding to use for keys. + /// + /// This type is derived from [`JSONEncoder`'s `KeyEncodingStrategy`](https://github.com/apple/swift/blob/6aa313b8dd5f05135f7f878eccc1db6f9fbe34ff/stdlib/public/Darwin/Foundation/JSONEncoder.swift#L128) + /// and [`XMLEncoder`s `KeyEncodingStrategy`](https://github.com/MaxDesiatov/XMLCoder/blob/master/Sources/XMLCoder/Encoder/XMLEncoder.swift#L102). + public enum KeyEncoding { + /// Use the keys specified by each type. This is the default encoding. + case useDefaultKeys + /// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key. + /// + /// Capital characters are determined by testing membership in + /// `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` + /// (Unicode General Categories Lu and Lt). + /// The conversion to lower case uses `Locale.system`, also known as + /// the ICU "root" locale. This means the result is consistent + /// regardless of the current user's locale and language preferences. + /// + /// Converting from camel case to snake case: + /// 1. Splits words at the boundary of lower-case to upper-case + /// 2. Inserts `_` between words + /// 3. Lowercases the entire string + /// 4. Preserves starting and ending `_`. + /// + /// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`. + /// + /// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted. + case convertToSnakeCase + /// Same as convertToSnakeCase, but using `-` instead of `_`. + /// For example `oneTwoThree` becomes `one-two-three`. + case convertToKebabCase + /// Capitalize the first letter only. + /// For example `oneTwoThree` becomes `OneTwoThree`. + case capitalized + /// Uppercase all letters. + /// For example `oneTwoThree` becomes `ONETWOTHREE`. + case uppercased + /// Lowercase all letters. + /// For example `oneTwoThree` becomes `onetwothree`. + case lowercased + /// A custom encoding using the provided closure. + case custom((String) -> String) + + func encode(_ key: String) -> String { + switch self { + case .useDefaultKeys: return key + case .convertToSnakeCase: return convertToSnakeCase(key) + case .convertToKebabCase: return convertToKebabCase(key) + case .capitalized: return String(key.prefix(1).uppercased() + key.dropFirst()) + case .uppercased: return key.uppercased() + case .lowercased: return key.lowercased() + case let .custom(encoding): return encoding(key) + } + } + + private func convertToSnakeCase(_ key: String) -> String { + convert(key, usingSeparator: "_") + } + + private func convertToKebabCase(_ key: String) -> String { + convert(key, usingSeparator: "-") + } + + private func convert(_ key: String, usingSeparator separator: String) -> String { + guard !key.isEmpty else { return key } + + var words: [Range] = [] + // The general idea of this algorithm is to split words on + // transition from lower to upper case, then on transition of >1 + // upper case characters to lowercase + // + // myProperty -> my_property + // myURLProperty -> my_url_property + // + // It is assumed, per Swift naming conventions, that the first character of the key is lowercase. + var wordStart = key.startIndex + var searchRange = key.index(after: wordStart)..1 capital letters. Turn those into a word, stopping at the capital before the lower case character. + let beforeLowerIndex = key.index(before: lowerCaseRange.lowerBound) + words.append(upperCaseRange.lowerBound.. String { + switch self { + case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20") + case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+") + } + } + } + + /// `URLEncodedFormEncoder` error. + public enum Error: Swift.Error { + /// An invalid root object was created by the encoder. Only keyed values are valid. + case invalidRootObject(String) + + var localizedDescription: String { + switch self { + case let .invalidRootObject(object): + return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead." + } + } + } + + /// Whether or not to sort the encoded key value pairs. + /// + /// - Note: This setting ensures a consistent ordering for all encodings of the same parameters. When set to `false`, + /// encoded `Dictionary` values may have a different encoded order each time they're encoded due to + /// ` Dictionary`'s random storage order, but `Encodable` types will maintain their encoded order. + public let alphabetizeKeyValuePairs: Bool + /// The `ArrayEncoding` to use. + public let arrayEncoding: ArrayEncoding + /// The `BoolEncoding` to use. + public let boolEncoding: BoolEncoding + /// THe `DataEncoding` to use. + public let dataEncoding: DataEncoding + /// The `DateEncoding` to use. + public let dateEncoding: DateEncoding + /// The `KeyEncoding` to use. + public let keyEncoding: KeyEncoding + /// The `SpaceEncoding` to use. + public let spaceEncoding: SpaceEncoding + /// The `CharacterSet` of allowed (non-escaped) characters. + public var allowedCharacters: CharacterSet + + /// Creates an instance from the supplied parameters. + /// + /// - Parameters: + /// - alphabetizeKeyValuePairs: Whether or not to sort the encoded key value pairs. `true` by default. + /// - arrayEncoding: The `ArrayEncoding` to use. `.brackets` by default. + /// - boolEncoding: The `BoolEncoding` to use. `.numeric` by default. + /// - dataEncoding: The `DataEncoding` to use. `.base64` by default. + /// - dateEncoding: The `DateEncoding` to use. `.deferredToDate` by default. + /// - keyEncoding: The `KeyEncoding` to use. `.useDefaultKeys` by default. + /// - spaceEncoding: The `SpaceEncoding` to use. `.percentEscaped` by default. + /// - allowedCharacters: The `CharacterSet` of allowed (non-escaped) characters. `.afURLQueryAllowed` by + /// default. + public init(alphabetizeKeyValuePairs: Bool = true, + arrayEncoding: ArrayEncoding = .brackets, + boolEncoding: BoolEncoding = .numeric, + dataEncoding: DataEncoding = .base64, + dateEncoding: DateEncoding = .deferredToDate, + keyEncoding: KeyEncoding = .useDefaultKeys, + spaceEncoding: SpaceEncoding = .percentEscaped, + allowedCharacters: CharacterSet = .afURLQueryAllowed) { + self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs + self.arrayEncoding = arrayEncoding + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + self.keyEncoding = keyEncoding + self.spaceEncoding = spaceEncoding + self.allowedCharacters = allowedCharacters + } + + func encode(_ value: Encodable) throws -> URLEncodedFormComponent { + let context = URLEncodedFormContext(.object([])) + let encoder = _URLEncodedFormEncoder(context: context, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + try value.encode(to: encoder) + + return context.component + } + + /// Encodes the `value` as a URL form encoded `String`. + /// + /// - Parameter value: The `Encodable` value.` + /// + /// - Returns: The encoded `String`. + /// - Throws: An `Error` or `EncodingError` instance if encoding fails. + public func encode(_ value: Encodable) throws -> String { + let component: URLEncodedFormComponent = try encode(value) + + guard case let .object(object) = component else { + throw Error.invalidRootObject("\(component)") + } + + let serializer = URLEncodedFormSerializer(alphabetizeKeyValuePairs: alphabetizeKeyValuePairs, + arrayEncoding: arrayEncoding, + keyEncoding: keyEncoding, + spaceEncoding: spaceEncoding, + allowedCharacters: allowedCharacters) + let query = serializer.serialize(object) + + return query + } + + /// Encodes the value as `Data`. This is performed by first creating an encoded `String` and then returning the + /// `.utf8` data. + /// + /// - Parameter value: The `Encodable` value. + /// + /// - Returns: The encoded `Data`. + /// + /// - Throws: An `Error` or `EncodingError` instance if encoding fails. + public func encode(_ value: Encodable) throws -> Data { + let string: String = try encode(value) + + return Data(string.utf8) + } +} + +final class _URLEncodedFormEncoder { + var codingPath: [CodingKey] + // Returns an empty dictionary, as this encoder doesn't support userInfo. + var userInfo: [CodingUserInfoKey: Any] { [:] } + + let context: URLEncodedFormContext + + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey] = [], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + } +} + +extension _URLEncodedFormEncoder: Encoder { + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key: CodingKey { + let container = _URLEncodedFormEncoder.KeyedContainer(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + return KeyedEncodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + _URLEncodedFormEncoder.UnkeyedContainer(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } + + func singleValueContainer() -> SingleValueEncodingContainer { + _URLEncodedFormEncoder.SingleValueContainer(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } +} + +final class URLEncodedFormContext { + var component: URLEncodedFormComponent + + init(_ component: URLEncodedFormComponent) { + self.component = component + } +} + +enum URLEncodedFormComponent { + typealias Object = [(key: String, value: URLEncodedFormComponent)] + + case string(String) + case array([URLEncodedFormComponent]) + case object(Object) + + /// Converts self to an `[URLEncodedFormData]` or returns `nil` if not convertible. + var array: [URLEncodedFormComponent]? { + switch self { + case let .array(array): return array + default: return nil + } + } + + /// Converts self to an `Object` or returns `nil` if not convertible. + var object: Object? { + switch self { + case let .object(object): return object + default: return nil + } + } + + /// Sets self to the supplied value at a given path. + /// + /// data.set(to: "hello", at: ["path", "to", "value"]) + /// + /// - parameters: + /// - value: Value of `Self` to set at the supplied path. + /// - path: `CodingKey` path to update with the supplied value. + public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) { + set(&self, to: value, at: path) + } + + /// Recursive backing method to `set(to:at:)`. + private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) { + guard path.count >= 1 else { + context = value + return + } + + let end = path[0] + var child: URLEncodedFormComponent + switch path.count { + case 1: + child = value + case 2...: + if let index = end.intValue { + let array = context.array ?? [] + if array.count > index { + child = array[index] + } else { + child = .array([]) + } + set(&child, to: value, at: Array(path[1...])) + } else { + child = context.object?.first { $0.key == end.stringValue }?.value ?? .object(.init()) + set(&child, to: value, at: Array(path[1...])) + } + default: fatalError("Unreachable") + } + + if let index = end.intValue { + if var array = context.array { + if array.count > index { + array[index] = child + } else { + array.append(child) + } + context = .array(array) + } else { + context = .array([child]) + } + } else { + if var object = context.object { + if let index = object.firstIndex(where: { $0.key == end.stringValue }) { + object[index] = (key: end.stringValue, value: child) + } else { + object.append((key: end.stringValue, value: child)) + } + context = .object(object) + } else { + context = .object([(key: end.stringValue, value: child)]) + } + } + } +} + +struct AnyCodingKey: CodingKey, Hashable { + let stringValue: String + let intValue: Int? + + init?(stringValue: String) { + self.stringValue = stringValue + intValue = nil + } + + init?(intValue: Int) { + stringValue = "\(intValue)" + self.intValue = intValue + } + + init(_ base: Key) where Key: CodingKey { + if let intValue = base.intValue { + self.init(intValue: intValue)! + } else { + self.init(stringValue: base.stringValue)! + } + } +} + +extension _URLEncodedFormEncoder { + final class KeyedContainer where Key: CodingKey { + var codingPath: [CodingKey] + + private let context: URLEncodedFormContext + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + } + + private func nestedCodingPath(for key: CodingKey) -> [CodingKey] { + codingPath + [key] + } + } +} + +extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol { + func encodeNil(forKey key: Key) throws { + let context = EncodingError.Context(codingPath: codingPath, + debugDescription: "URLEncodedFormEncoder cannot encode nil values.") + throw EncodingError.invalidValue("\(key): nil", context) + } + + func encode(_ value: T, forKey key: Key) throws where T: Encodable { + var container = nestedSingleValueEncoder(for: key) + try container.encode(value) + } + + func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer { + let container = _URLEncodedFormEncoder.SingleValueContainer(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + + return container + } + + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + + return container + } + + func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey: CodingKey { + let container = _URLEncodedFormEncoder.KeyedContainer(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + + return KeyedEncodingContainer(container) + } + + func superEncoder() -> Encoder { + _URLEncodedFormEncoder(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } + + func superEncoder(forKey key: Key) -> Encoder { + _URLEncodedFormEncoder(context: context, + codingPath: nestedCodingPath(for: key), + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } +} + +extension _URLEncodedFormEncoder { + final class SingleValueContainer { + var codingPath: [CodingKey] + + private var canEncodeNewValue = true + + private let context: URLEncodedFormContext + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + } + + private func checkCanEncode(value: Any?) throws { + guard canEncodeNewValue else { + let context = EncodingError.Context(codingPath: codingPath, + debugDescription: "Attempt to encode value through single value container when previously value already encoded.") + throw EncodingError.invalidValue(value as Any, context) + } + } + } +} + +extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer { + func encodeNil() throws { + try checkCanEncode(value: nil) + defer { canEncodeNewValue = false } + + let context = EncodingError.Context(codingPath: codingPath, + debugDescription: "URLEncodedFormEncoder cannot encode nil values.") + throw EncodingError.invalidValue("nil", context) + } + + func encode(_ value: Bool) throws { + try encode(value, as: String(boolEncoding.encode(value))) + } + + func encode(_ value: String) throws { + try encode(value, as: value) + } + + func encode(_ value: Double) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Float) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int8) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int16) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int32) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: Int64) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt8) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt16) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt32) throws { + try encode(value, as: String(value)) + } + + func encode(_ value: UInt64) throws { + try encode(value, as: String(value)) + } + + private func encode(_ value: T, as string: String) throws where T: Encodable { + try checkCanEncode(value: value) + defer { canEncodeNewValue = false } + + context.component.set(to: .string(string), at: codingPath) + } + + func encode(_ value: T) throws where T: Encodable { + switch value { + case let date as Date: + guard let string = try dateEncoding.encode(date) else { + try attemptToEncode(value) + return + } + + try encode(value, as: string) + case let data as Data: + guard let string = try dataEncoding.encode(data) else { + try attemptToEncode(value) + return + } + + try encode(value, as: string) + case let decimal as Decimal: + // Decimal's `Encodable` implementation returns an object, not a single value, so override it. + try encode(value, as: String(describing: decimal)) + default: + try attemptToEncode(value) + } + } + + private func attemptToEncode(_ value: T) throws where T: Encodable { + try checkCanEncode(value: value) + defer { canEncodeNewValue = false } + + let encoder = _URLEncodedFormEncoder(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + try value.encode(to: encoder) + } +} + +extension _URLEncodedFormEncoder { + final class UnkeyedContainer { + var codingPath: [CodingKey] + + var count = 0 + var nestedCodingPath: [CodingKey] { + codingPath + [AnyCodingKey(intValue: count)!] + } + + private let context: URLEncodedFormContext + private let boolEncoding: URLEncodedFormEncoder.BoolEncoding + private let dataEncoding: URLEncodedFormEncoder.DataEncoding + private let dateEncoding: URLEncodedFormEncoder.DateEncoding + + init(context: URLEncodedFormContext, + codingPath: [CodingKey], + boolEncoding: URLEncodedFormEncoder.BoolEncoding, + dataEncoding: URLEncodedFormEncoder.DataEncoding, + dateEncoding: URLEncodedFormEncoder.DateEncoding) { + self.context = context + self.codingPath = codingPath + self.boolEncoding = boolEncoding + self.dataEncoding = dataEncoding + self.dateEncoding = dateEncoding + } + } +} + +extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer { + func encodeNil() throws { + let context = EncodingError.Context(codingPath: codingPath, + debugDescription: "URLEncodedFormEncoder cannot encode nil values.") + throw EncodingError.invalidValue("nil", context) + } + + func encode(_ value: T) throws where T: Encodable { + var container = nestedSingleValueContainer() + try container.encode(value) + } + + func nestedSingleValueContainer() -> SingleValueEncodingContainer { + defer { count += 1 } + + return _URLEncodedFormEncoder.SingleValueContainer(context: context, + codingPath: nestedCodingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey: CodingKey { + defer { count += 1 } + let container = _URLEncodedFormEncoder.KeyedContainer(context: context, + codingPath: nestedCodingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + + return KeyedEncodingContainer(container) + } + + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + defer { count += 1 } + + return _URLEncodedFormEncoder.UnkeyedContainer(context: context, + codingPath: nestedCodingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } + + func superEncoder() -> Encoder { + defer { count += 1 } + + return _URLEncodedFormEncoder(context: context, + codingPath: codingPath, + boolEncoding: boolEncoding, + dataEncoding: dataEncoding, + dateEncoding: dateEncoding) + } +} + +final class URLEncodedFormSerializer { + private let alphabetizeKeyValuePairs: Bool + private let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding + private let keyEncoding: URLEncodedFormEncoder.KeyEncoding + private let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding + private let allowedCharacters: CharacterSet + + init(alphabetizeKeyValuePairs: Bool, + arrayEncoding: URLEncodedFormEncoder.ArrayEncoding, + keyEncoding: URLEncodedFormEncoder.KeyEncoding, + spaceEncoding: URLEncodedFormEncoder.SpaceEncoding, + allowedCharacters: CharacterSet) { + self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs + self.arrayEncoding = arrayEncoding + self.keyEncoding = keyEncoding + self.spaceEncoding = spaceEncoding + self.allowedCharacters = allowedCharacters + } + + func serialize(_ object: URLEncodedFormComponent.Object) -> String { + var output: [String] = [] + for (key, component) in object { + let value = serialize(component, forKey: key) + output.append(value) + } + output = alphabetizeKeyValuePairs ? output.sorted() : output + + return output.joinedWithAmpersands() + } + + func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String { + switch component { + case let .string(string): return "\(escape(keyEncoding.encode(key)))=\(escape(string))" + case let .array(array): return serialize(array, forKey: key) + case let .object(object): return serialize(object, forKey: key) + } + } + + func serialize(_ object: URLEncodedFormComponent.Object, forKey key: String) -> String { + var segments: [String] = object.map { subKey, value in + let keyPath = "[\(subKey)]" + return serialize(value, forKey: key + keyPath) + } + segments = alphabetizeKeyValuePairs ? segments.sorted() : segments + + return segments.joinedWithAmpersands() + } + + func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String { + var segments: [String] = array.map { component in + let keyPath = arrayEncoding.encode(key) + return serialize(component, forKey: keyPath) + } + segments = alphabetizeKeyValuePairs ? segments.sorted() : segments + + return segments.joinedWithAmpersands() + } + + func escape(_ query: String) -> String { + var allowedCharactersWithSpace = allowedCharacters + allowedCharactersWithSpace.insert(charactersIn: " ") + let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query + let spaceEncodedQuery = spaceEncoding.encode(escapedQuery) + + return spaceEncodedQuery + } +} + +extension Array where Element == String { + func joinedWithAmpersands() -> String { + joined(separator: "&") + } +} + +extension CharacterSet { + /// Creates a CharacterSet from RFC 3986 allowed characters. + /// + /// 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. + public static let afURLQueryAllowed: CharacterSet = { + let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 + let subDelimitersToEncode = "!$&'()*+,;=" + let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") + + return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters) + }() +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift new file mode 100644 index 00000000..be27c8ed --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift @@ -0,0 +1,39 @@ +// +// URLRequest+Alamofire.swift +// +// Copyright (c) 2019 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 URLRequest { + /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. + public var method: HTTPMethod? { + get { httpMethod.flatMap(HTTPMethod.init) } + set { httpMethod = newValue?.rawValue } + } + + public func validate() throws { + if method == .get, let bodyData = httpBody { + throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift new file mode 100644 index 00000000..de3e290a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift @@ -0,0 +1,37 @@ +// +// URLSessionConfiguration+Alamofire.swift +// +// Copyright (c) 2014-2018 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 URLSessionConfiguration: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: URLSessionConfiguration { + /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default + /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. + public static var `default`: URLSessionConfiguration { + let configuration = URLSessionConfiguration.default + configuration.headers = .default + + return configuration + } +} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift new file mode 100644 index 00000000..bd2a2795 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift @@ -0,0 +1,302 @@ +// +// Validation.swift +// +// Copyright (c) 2014-2018 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 a validation succeeded or failed. + public typealias ValidationResult = Result + + fileprivate struct MIMEType { + let type: String + let subtype: String + + var isWildcard: Bool { type == "*" && subtype == "*" } + + init?(_ string: String) { + let components: [String] = { + let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) + let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] + + 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: Range { 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.isEmpty else { return .success(()) } + + return validate(contentType: acceptableContentTypes, response: response) + } + + fileprivate func validate(contentType acceptableContentTypes: S, + response: HTTPURLResponse) + -> ValidationResult + where S.Iterator.Element == String { + 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 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 statusCode: `Sequence` of acceptable response status codes. + /// + /// - Returns: The instance. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + validate { [unowned self] _, response, _ in + 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: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { + validate { [unowned self] _, response, data in + 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 { + let contentTypes: () -> [String] = { [unowned self] in + self.acceptableContentTypes + } + return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) + } +} + +extension DataStreamRequest { + /// A closure used to validate a request that takes a `URLRequest` and `HTTPURLResponse` and returns whether the + /// request was valid. + public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> ValidationResult + + /// 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 statusCode: `Sequence` of acceptable response status codes. + /// + /// - Returns: The instance. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + validate { [unowned self] _, response in + 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: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { + validate { [unowned self] _, response in + self.validate(contentType: acceptableContentTypes(), response: response) + } + } + + /// 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 instance. + @discardableResult + public func validate() -> Self { + let contentTypes: () -> [String] = { [unowned self] in + self.acceptableContentTypes + } + return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) + } +} + +// 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, + _ fileURL: URL?) + -> ValidationResult + + /// 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 statusCode: `Sequence` of acceptable response status codes. + /// + /// - Returns: The instance. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + validate { [unowned self] _, response, _ in + 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: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { + validate { [unowned self] _, response, fileURL in + 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 { + let contentTypes = { [unowned self] in + self.acceptableContentTypes + } + return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift new file mode 100644 index 00000000..8d2529e4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift @@ -0,0 +1,123 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public enum ChatItemVisibility { + case hidden + case appearing + case visible +} + +open class BaseChatItemPresenter: ChatItemPresenterProtocol { + public final weak var cell: CellT? + + public init() {} + + open class func registerCells(_ collectionView: UICollectionView) { + assert(false, "Implement in subclass") + } + + open var isItemUpdateSupported: Bool { + fatalError("Implement in subclass") + } + + open func update(with chatItem: ChatItemProtocol) { + fatalError("Implement in subclass") + } + + open var canCalculateHeightInBackground: Bool { + return false + } + + open func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat { + assert(false, "Implement in subclass") + return 0 + } + + open func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { + assert(false, "Implemenent in subclass") + return UICollectionViewCell() + } + + open func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) { + assert(false, "Implemenent in subclass") + } + + final public private(set) var itemVisibility: ChatItemVisibility = .hidden + + // Need to override default implementatios. Otherwise subclasses's code won't be executed + // http://stackoverflow.com/questions/31795158/swift-2-protocol-extension-not-calling-overriden-method-correctly + public final func cellWillBeShown(_ cell: UICollectionViewCell) { + if let cell = cell as? CellT { + self.cell = cell + self.itemVisibility = .appearing + self.cellWillBeShown() + self.itemVisibility = .visible + } else { + assert(false, "Invalid cell was given to presenter!") + } + } + + open func cellWillBeShown() { + // Hook for subclasses + } + + open func shouldShowMenu() -> Bool { + return false + } + + public final func cellWasHidden(_ cell: UICollectionViewCell) { + // Carefull!! This doesn't mean that this is no longer visible + // If cell is replaced (due to a reload for instance) we can have the following sequence: + // - New cell is taken from the pool and configured. We'll get cellWillBeShown + // - Old cell is removed. We'll get cellWasHidden + // --> We need to check that this cell is the last one made visible + if let cell = cell as? CellT { + if cell === self.cell { + self.cell = nil + self.itemVisibility = .hidden + self.cellWasHidden() + } + } else { + assert(false, "Invalid cell was given to presenter!") + } + } + + open func cellWasHidden() { + // Hook for subclasses. Here we are not visible for real. + } + + open func canPerformMenuControllerAction(_ action: Selector) -> Bool { + return false + } + + open func performMenuControllerAction(_ action: Selector) { + assert(self.canPerformMenuControllerAction(action)) + } + + // MARK: - ChatItemSpotlighting + + open func spotlight() {} +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift new file mode 100644 index 00000000..489b1a25 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift @@ -0,0 +1,52 @@ +/* +The MIT License (MIT) + +Copyright (c) 2015-present Badoo Trading Limited. + +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 protocol ChatItemsDecoratorProtocol { + func decorateItems(_ chatItems: [ChatItemProtocol]) -> [DecoratedChatItem] +} + +public struct DecoratedChatItem: UniqueIdentificable { + public let uid: String + public let chatItem: ChatItemProtocol + public let decorationAttributes: ChatItemDecorationAttributesProtocol? + + public init(chatItem: ChatItemProtocol, decorationAttributes: ChatItemDecorationAttributesProtocol?) { + self.init(uid: chatItem.uid, chatItem: chatItem, decorationAttributes: decorationAttributes) + } + + public init(uid: String, chatItem: ChatItemProtocol, decorationAttributes: ChatItemDecorationAttributesProtocol?) { + self.uid = uid + self.chatItem = chatItem + self.decorationAttributes = decorationAttributes + } +} + +public struct ChatItemCompanion: UniqueIdentificable { + public let uid: String + public let chatItem: ChatItemProtocol + public let presenter: ChatItemPresenterProtocol + public var decorationAttributes: ChatItemDecorationAttributesProtocol? +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift new file mode 100644 index 00000000..c4a975a5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift @@ -0,0 +1,84 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public typealias ChatItemType = String + +public protocol ChatItemProtocol: AnyObject, UniqueIdentificable { + var type: ChatItemType { get } +} + +public protocol ChatItemDecorationAttributesProtocol { + var bottomMargin: CGFloat { get } +} + +public protocol ChatItemMenuPresenterProtocol { + func shouldShowMenu() -> Bool + func canPerformMenuControllerAction(_ action: Selector) -> Bool + func performMenuControllerAction(_ action: Selector) +} + +public protocol ChatItemSpotlighting { + func spotlight() +} + +extension ChatItemSpotlighting { + public func spotlight() {} +} + +public protocol ChatItemPresenterProtocol: AnyObject, ChatItemMenuPresenterProtocol, ChatItemSpotlighting { + static func registerCells(_ collectionView: UICollectionView) + + var isItemUpdateSupported: Bool { get } + func update(with chatItem: ChatItemProtocol) + + var canCalculateHeightInBackground: Bool { get } // Default is false + func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat + func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell + func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) + func cellWillBeShown(_ cell: UICollectionViewCell) // optional + func cellWasHidden(_ cell: UICollectionViewCell) // optional +} + +public extension ChatItemPresenterProtocol { // Optionals + var canCalculateHeightInBackground: Bool { return false } + func cellWillBeShown(_ cell: UICollectionViewCell) {} + func cellWasHidden(_ cell: UICollectionViewCell) {} + func shouldShowMenu() -> Bool { return false } + func canPerformMenuControllerAction(_ action: Selector) -> Bool { return false } + func performMenuControllerAction(_ action: Selector) {} +} + +public protocol ChatItemPresenterBuilderProtocol { + func canHandleChatItem(_ chatItem: ChatItemProtocol) -> Bool + func createPresenterWithChatItem(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol + var presenterType: ChatItemPresenterProtocol.Type { get } +} + +// MARK: - Updatable Chat Items + +public protocol ContentEquatableChatItemProtocol: ChatItemProtocol { + func hasSameContent(as anotherItem: ChatItemProtocol) -> Bool +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift new file mode 100644 index 00000000..9c0ddd65 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift @@ -0,0 +1,57 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +// Handles messages which aren't supported. So, they appear as invisible. +class DummyChatItemPresenter: ChatItemPresenterProtocol { + + class func registerCells(_ collectionView: UICollectionView) { + collectionView.register(DummyCollectionViewCell.self, forCellWithReuseIdentifier: "cell-id-unhandled-message") + } + + let isItemUpdateSupported = true + + func update(with chatItem: ChatItemProtocol) { + // Does nothing + } + + var canCalculateHeightInBackground: Bool { + return true + } + + func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat { + return 0 + } + + func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: "cell-id-unhandled-message", for: indexPath) + } + + func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) { + cell.isHidden = true + } +} + +class DummyCollectionViewCell: UICollectionViewCell {} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift new file mode 100644 index 00000000..0f5bdb66 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift @@ -0,0 +1,36 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 BaseChatViewController { // Cell Pan Gesture Handler + + final var cellPanGestureHandlerIsEnabled: Bool { + get { + return self.cellPanGestureHandler.isEnabled + } set { + self.cellPanGestureHandler.isEnabled = newValue + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift new file mode 100644 index 00000000..0d2ff2b0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift @@ -0,0 +1,373 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 struct HashableItem: Hashable { + private let uid: String + private let type: String + + init(_ decoratedChatItem: DecoratedChatItem) { + self.uid = decoratedChatItem.uid + self.type = decoratedChatItem.chatItem.type + } + + init(_ chatItemCompanion: ChatItemCompanion) { + self.uid = chatItemCompanion.uid + self.type = chatItemCompanion.chatItem.type + } +} + +extension BaseChatViewController { + + public func enqueueModelUpdate(updateType: UpdateType, completion: (() -> Void)? = nil) { + let newItems = self.chatDataSource?.chatItems ?? [] + + if self.updatesConfig.coalesceUpdates { + self.updateQueue.flushQueue() + } + + self.updateQueue.addTask({ [weak self] (runNextTask) -> Void in + guard let sSelf = self else { return } + + let oldItems = sSelf.chatItemCompanionCollection + sSelf.updateModels(newItems: newItems, oldItems: oldItems, updateType: updateType, completion: { + guard let sSelf = self else { return } + if sSelf.updateQueue.isEmpty { + sSelf.enqueueMessageCountReductionIfNeeded() + } + completion?() + DispatchQueue.main.async(execute: { () -> Void in + // Reduces inconsistencies before next update: https://github.com/diegosanchezr/UICollectionViewStressing + runNextTask() + }) + }) + }) + } + + public func enqueueMessageCountReductionIfNeeded() { + guard let preferredMaxMessageCount = self.constants.preferredMaxMessageCount, (self.chatDataSource?.chatItems.count ?? 0) > preferredMaxMessageCount else { return } + self.updateQueue.addTask { [weak self] (completion) -> Void in + guard let sSelf = self else { return } + sSelf.chatDataSource?.adjustNumberOfMessages(preferredMaxCount: sSelf.constants.preferredMaxMessageCountAdjustment, focusPosition: sSelf.focusPosition, completion: { (didAdjust) -> Void in + guard didAdjust, let sSelf = self else { + completion() + return + } + let newItems = sSelf.chatDataSource?.chatItems ?? [] + let oldItems = sSelf.chatItemCompanionCollection + sSelf.updateModels(newItems: newItems, oldItems: oldItems, updateType: .messageCountReduction, completion: completion ) + }) + } + } + + // Returns scrolling position in interval [0, 1], 0 top, 1 bottom + public var focusPosition: Double { + guard let collectionView = self.collectionView else { return 0 } + if self.isCloseToBottom() { + return 1 + } else if self.isCloseToTop() { + return 0 + } + + let contentHeight = collectionView.contentSize.height + guard contentHeight > 0 else { + return 0.5 + } + + // Rough estimation + let collectionViewContentYOffset = collectionView.contentOffset.y + let midContentOffset = collectionViewContentYOffset + self.visibleRect().height / 2 + return min(max(0, Double(midContentOffset / contentHeight)), 1.0) + } + + func updateVisibleCells(_ changes: CollectionChanges) { + // Datasource should be already updated! + + assert(self.visibleCellsAreValid(changes: changes), "Invalid visible cells. Don't call me") + + let cellsToUpdate = updated(collection: self.visibleCellsFromCollectionViewApi(), withChanges: changes) + self.visibleCells = cellsToUpdate + + cellsToUpdate.forEach { (indexPath, cell) in + let presenter = self.presenterForIndexPath(indexPath) + presenter.configureCell(cell, decorationAttributes: self.decorationAttributesForIndexPath(indexPath)) + presenter.cellWillBeShown(cell) // `createModelUpdates` may have created a new presenter instance for existing visible cell so we need to tell it that its cell is visible + } + } + + private func visibleCellsFromCollectionViewApi() -> [IndexPath: UICollectionViewCell] { + var visibleCells: [IndexPath: UICollectionViewCell] = [:] + guard let collectionView = self.collectionView else { return visibleCells } + collectionView.indexPathsForVisibleItems.forEach({ (indexPath) in + if let cell = collectionView.cellForItem(at: indexPath) { + visibleCells[indexPath] = cell + } + }) + return visibleCells + } + + private func visibleCellsAreValid(changes: CollectionChanges) -> Bool { + // Afer performBatchUpdates, indexPathForCell may return a cell refering to the state before the update + // if self.updatesConfig.fastUpdates is enabled, very fast updates could result in `updateVisibleCells` updating wrong cells. + // See more: https://github.com/diegosanchezr/UICollectionViewStressing + + if self.updatesConfig.fastUpdates { + return updated(collection: self.visibleCells, withChanges: changes) == updated(collection: self.visibleCellsFromCollectionViewApi(), withChanges: changes) + } else { + return true // never seen inconsistency without fastUpdates + } + } + + private enum ScrollAction { + case scrollToBottom + case preservePosition(rectForReferenceIndexPathBeforeUpdate: CGRect?, referenceIndexPathAfterUpdate: IndexPath?) + } + + func performBatchUpdates(updateModelClosure: @escaping () -> Void, // swiftlint:disable:this cyclomatic_complexity + changes: CollectionChanges, + updateType: UpdateType, + completion: @escaping () -> Void) { + guard let collectionView = self.collectionView else { + completion() + return + } + let usesBatchUpdates: Bool + do { // Recover from too fast updates... + let visibleCellsAreValid = self.visibleCellsAreValid(changes: changes) + let wantsReloadData = updateType != .normal + let hasUnfinishedBatchUpdates = self.unfinishedBatchUpdatesCount > 0 // This can only happen when enabling self.updatesConfig.fastUpdates + + // a) It's unsafe to perform reloadData while there's a performBatchUpdates animating: https://github.com/diegosanchezr/UICollectionViewStressing/tree/master/GhostCells + // Note: using reloadSections instead reloadData is safe and might not need a delay. However, using always reloadSections causes flickering on pagination and a crash on the first layout that needs a workaround. Let's stick to reloaData for now + // b) If it's a performBatchUpdates but visible cells are invalid let's wait until all finish (otherwise we would give wrong cells to presenters in updateVisibleCells) + let mustDelayUpdate = hasUnfinishedBatchUpdates && (wantsReloadData || !visibleCellsAreValid) + guard !mustDelayUpdate else { + // For reference, it is possible to force the current performBatchUpdates to finish in the next run loop, by cancelling animations: + // self.collectionView.subviews.forEach { $0.layer.removeAllAnimations() } + self.onAllBatchUpdatesFinished = { [weak self] in + self?.onAllBatchUpdatesFinished = nil + self?.performBatchUpdates(updateModelClosure: updateModelClosure, changes: changes, updateType: updateType, completion: completion) + } + return + } + // ... if they are still invalid the only thing we can do is a reloadData + let mustDoReloadData = !visibleCellsAreValid // Only way to recover from this inconsistent state + usesBatchUpdates = !wantsReloadData && !mustDoReloadData + } + + let scrollAction: ScrollAction + do { // Scroll action + if updateType != .pagination && self.isScrolledAtBottom() { + scrollAction = .scrollToBottom + } else { + let (oldReferenceIndexPath, newReferenceIndexPath) = self.referenceIndexPathsToRestoreScrollPositionOnUpdate(itemsBeforeUpdate: self.chatItemCompanionCollection, changes: changes) + let oldRect = self.rectAtIndexPath(oldReferenceIndexPath) + scrollAction = .preservePosition(rectForReferenceIndexPathBeforeUpdate: oldRect, referenceIndexPathAfterUpdate: newReferenceIndexPath) + } + } + + let myCompletion: () -> Void + do { // Completion + var myCompletionExecuted = false + myCompletion = { + if myCompletionExecuted { return } + myCompletionExecuted = true + completion() + } + } + + if usesBatchUpdates { + UIView.animate(withDuration: self.constants.updatesAnimationDuration, animations: { () -> Void in + self.unfinishedBatchUpdatesCount += 1 + collectionView.performBatchUpdates({ () -> Void in + updateModelClosure() + self.updateVisibleCells(changes) // For instance, to support removal of tails + + collectionView.deleteItems(at: Array(changes.deletedIndexPaths)) + collectionView.insertItems(at: Array(changes.insertedIndexPaths)) + for move in changes.movedIndexPaths { + collectionView.moveItem(at: move.indexPathOld, to: move.indexPathNew) + } + }, completion: { [weak self] (_) -> Void in + defer { myCompletion() } + guard let sSelf = self else { return } + sSelf.unfinishedBatchUpdatesCount -= 1 + if sSelf.unfinishedBatchUpdatesCount == 0, let onAllBatchUpdatesFinished = self?.onAllBatchUpdatesFinished { + DispatchQueue.main.async(execute: onAllBatchUpdatesFinished) + } + }) + if self.placeMessagesFromBottom { + self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) + } + }) + } else { + self.visibleCells = [:] + updateModelClosure() + collectionView.reloadData() + collectionView.collectionViewLayout.prepare() + if self.placeMessagesFromBottom { + self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) + } + } + + switch scrollAction { + case .scrollToBottom: + self.scrollToBottom(animated: updateType == .normal) + case .preservePosition(rectForReferenceIndexPathBeforeUpdate: let oldRect, referenceIndexPathAfterUpdate: let indexPath): + let newRect = self.rectAtIndexPath(indexPath) + self.scrollToPreservePosition(oldRefRect: oldRect, newRefRect: newRect) + } + + if !usesBatchUpdates || self.updatesConfig.fastUpdates { + myCompletion() + } + } + + private func updateModels(newItems: [ChatItemProtocol], oldItems: ChatItemCompanionCollection, updateType: UpdateType, completion: @escaping () -> Void) { + guard let collectionView = self.collectionView else { + completion() + return + } + let collectionViewWidth = collectionView.bounds.width + let updateType = self.isFirstLayout ? .firstLoad : updateType + let performInBackground = updateType != .firstLoad + + self.autoLoadingEnabled = false + let perfomBatchUpdates: (_ changes: CollectionChanges, _ updateModelClosure: @escaping () -> Void) -> Void = { [weak self] (changes, updateModelClosure) in + self?.performBatchUpdates( + updateModelClosure: updateModelClosure, + changes: changes, + updateType: updateType, + completion: { () -> Void in + self?.autoLoadingEnabled = true + completion() + }) + } + + let createModelUpdate = { + return self.createModelUpdates( + newItems: newItems, + oldItems: oldItems, + collectionViewWidth: collectionViewWidth) + } + + if performInBackground { + DispatchQueue.global(qos: .userInitiated).async { () -> Void in + let modelUpdate = createModelUpdate() + DispatchQueue.main.async(execute: { () -> Void in + perfomBatchUpdates(modelUpdate.changes, modelUpdate.updateModelClosure) + }) + } + } else { + let modelUpdate = createModelUpdate() + perfomBatchUpdates(modelUpdate.changes, modelUpdate.updateModelClosure) + } + } + + private func createModelUpdates(newItems: [ChatItemProtocol], oldItems: ChatItemCompanionCollection, collectionViewWidth: CGFloat) -> (changes: CollectionChanges, updateModelClosure: () -> Void) { + let newDecoratedItems = self.chatItemsDecorator?.decorateItems(newItems) ?? newItems.map { DecoratedChatItem(chatItem: $0, decorationAttributes: nil) } + let changes = Chatto.generateChanges(oldCollection: oldItems.map(HashableItem.init), + newCollection: newDecoratedItems.map(HashableItem.init)) + let itemCompanionCollection = self.createCompanionCollection(fromChatItems: newDecoratedItems, previousCompanionCollection: oldItems) + let layoutModel = self.createLayoutModel(itemCompanionCollection, collectionViewWidth: collectionViewWidth) + let updateModelClosure : () -> Void = { [weak self] in + self?.layoutModel = layoutModel + self?.chatItemCompanionCollection = itemCompanionCollection + } + return (changes, updateModelClosure) + } + + private func createCompanionCollection(fromChatItems newItems: [DecoratedChatItem], previousCompanionCollection oldItems: ChatItemCompanionCollection) -> ChatItemCompanionCollection { + return ChatItemCompanionCollection(items: newItems.map { (decoratedChatItem) -> ChatItemCompanion in + + /* + We use an assumption, that message having a specific messageId never changes its type. + If such changes has to be supported, then generation of changes has to suppport reloading items. + Otherwise, updateVisibleCells may try to update the existing cells with new presenters which aren't able to work with another types. + */ + + let presenter: ChatItemPresenterProtocol = { + guard let oldChatItemCompanion = oldItems[decoratedChatItem.uid] ?? oldItems[decoratedChatItem.chatItem.uid], + oldChatItemCompanion.chatItem.type == decoratedChatItem.chatItem.type, + oldChatItemCompanion.presenter.isItemUpdateSupported else { + return self.createPresenterForChatItem(decoratedChatItem.chatItem) + } + + oldChatItemCompanion.presenter.update(with: decoratedChatItem.chatItem) + return oldChatItemCompanion.presenter + }() + + return ChatItemCompanion(uid: decoratedChatItem.uid, chatItem: decoratedChatItem.chatItem, presenter: presenter, decorationAttributes: decoratedChatItem.decorationAttributes) + }) + } + + private func createLayoutModel(_ items: ChatItemCompanionCollection, collectionViewWidth: CGFloat) -> ChatCollectionViewLayoutModel { + // swiftlint:disable:next nesting + typealias IntermediateItemLayoutData = (height: CGFloat?, bottomMargin: CGFloat) + typealias ItemLayoutData = (height: CGFloat, bottomMargin: CGFloat) + // swiftlint:disable:previous nesting + + func createLayoutModel(intermediateLayoutData: [IntermediateItemLayoutData]) -> ChatCollectionViewLayoutModel { + let layoutData = intermediateLayoutData.map { (intermediateLayoutData: IntermediateItemLayoutData) -> ItemLayoutData in + return (height: intermediateLayoutData.height!, bottomMargin: intermediateLayoutData.bottomMargin) + } + return ChatCollectionViewLayoutModel.createModel(collectionViewWidth, itemsLayoutData: layoutData) + } + + let isInbackground = !Thread.isMainThread + var intermediateLayoutData = [IntermediateItemLayoutData]() + var itemsForMainThread = [(index: Int, itemCompanion: ChatItemCompanion)]() + + for (index, itemCompanion) in items.enumerated() { + var height: CGFloat? + let bottomMargin: CGFloat = itemCompanion.decorationAttributes?.bottomMargin ?? 0 + if !isInbackground || itemCompanion.presenter.canCalculateHeightInBackground { + height = itemCompanion.presenter.heightForCell(maximumWidth: collectionViewWidth, decorationAttributes: itemCompanion.decorationAttributes) + } else { + itemsForMainThread.append((index: index, itemCompanion: itemCompanion)) + } + intermediateLayoutData.append((height: height, bottomMargin: bottomMargin)) + } + + if itemsForMainThread.count > 0 { + DispatchQueue.main.sync(execute: { () -> Void in + for (index, itemCompanion) in itemsForMainThread { + let height = itemCompanion.presenter.heightForCell(maximumWidth: collectionViewWidth, decorationAttributes: itemCompanion.decorationAttributes) + intermediateLayoutData[index].height = height + } + }) + } + return createLayoutModel(intermediateLayoutData: intermediateLayoutData) + } + + public func chatCollectionViewLayoutModel() -> ChatCollectionViewLayoutModel { + guard let collectionView = self.collectionView else { return self.layoutModel } + if self.layoutModel.calculatedForWidth != collectionView.bounds.width { + self.layoutModel = self.createLayoutModel(self.chatItemCompanionCollection, collectionViewWidth: collectionView.bounds.width) + } + return self.layoutModel + } + +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift new file mode 100644 index 00000000..c3aa5663 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift @@ -0,0 +1,137 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +extension BaseChatViewController: ChatCollectionViewLayoutDelegate { + + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.chatItemCompanionCollection.count + } + + @objc(collectionView:cellForItemAtIndexPath:) + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let presenter = self.presenterForIndexPath(indexPath) + let cell = presenter.dequeueCell(collectionView: collectionView, indexPath: indexPath) + let decorationAttributes = self.decorationAttributesForIndexPath(indexPath) + presenter.configureCell(cell, decorationAttributes: decorationAttributes) + return cell + } + + @objc(collectionView:didEndDisplayingCell:forItemAtIndexPath:) + open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + // Carefull: this index path can refer to old data source after an update. Don't use it to grab items from the model + // Instead let's use a mapping presenter <--> cell + if let oldPresenterForCell = self.presentersByCell.object(forKey: cell) as? ChatItemPresenterProtocol { + self.presentersByCell.removeObject(forKey: cell) + oldPresenterForCell.cellWasHidden(cell) + } + + if self.updatesConfig.fastUpdates { + if let visibleCell = self.visibleCells[indexPath], visibleCell === cell { + self.visibleCells[indexPath] = nil + } else { + self.visibleCells.forEach({ (indexPath, storedCell) in + if cell === storedCell { + // Inconsistency found, likely due to very fast updates + self.visibleCells[indexPath] = nil + } + }) + } + } + } + + @objc(collectionView:willDisplayCell:forItemAtIndexPath:) + open func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + // Here indexPath should always referer to updated data source. + + let presenter = self.presenterForIndexPath(indexPath) + self.presentersByCell.setObject(presenter, forKey: cell) + if self.updatesConfig.fastUpdates { + self.visibleCells[indexPath] = cell + } + + if self.isAdjustingInputContainer { + UIView.performWithoutAnimation({ + // See https://github.com/badoo/Chatto/issues/133 + presenter.cellWillBeShown(cell) + cell.layoutIfNeeded() + }) + } else { + presenter.cellWillBeShown(cell) + } + } + + @objc(collectionView:shouldShowMenuForItemAtIndexPath:) + open func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath?) -> Bool { + // Note: IndexPath set optional due to https://github.com/badoo/Chatto/issues/310 + // Might be related: https://bugs.swift.org/browse/SR-2417 + guard let indexPath = indexPath else { return false } + return self.presenterForIndexPath(indexPath).shouldShowMenu() + } + + @objc(collectionView:canPerformAction:forItemAtIndexPath:withSender:) + open func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath?, withSender sender: Any?) -> Bool { + // Note: IndexPath set optional due to https://github.com/badoo/Chatto/issues/247. SR-2417 might be related + // Might be related: https://bugs.swift.org/browse/SR-2417 + guard let indexPath = indexPath else { return false } + return self.presenterForIndexPath(indexPath).canPerformMenuControllerAction(action) + } + + @objc(collectionView:performAction:forItemAtIndexPath:withSender:) + open func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) { + self.presenterForIndexPath(indexPath).performMenuControllerAction(action) + } + + func presenterForIndexPath(_ indexPath: IndexPath) -> ChatItemPresenterProtocol { + return self.presenterForIndex(indexPath.item, chatItemCompanionCollection: self.chatItemCompanionCollection) + } + + func presenterForIndex(_ index: Int, chatItemCompanionCollection items: ChatItemCompanionCollection) -> ChatItemPresenterProtocol { + guard index < items.count else { + // This can happen from didEndDisplayingCell if we reloaded with less messages + return DummyChatItemPresenter() + } + return items[index].presenter + } + + public func createPresenterForChatItem(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol { + assert(self.presenterFactory != nil, "Presenter factory is not initialized") + return self.presenterFactory.createChatItemPresenter(chatItem) + } + + public func confugureCollectionViewWithPresenters() { + assert(self.presenterFactory == nil, "Presenter factory is already initialized") + guard let collectionView = self.collectionView else { + assertionFailure("CollectionView is not initialized") + return + } + self.presenterFactory = self.createPresenterFactory() + self.presenterFactory.configure(withCollectionView: collectionView ) + } + + public func decorationAttributesForIndexPath(_ indexPath: IndexPath) -> ChatItemDecorationAttributesProtocol? { + return self.chatItemCompanionCollection[indexPath.item].decorationAttributes + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift new file mode 100644 index 00000000..96453ad3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift @@ -0,0 +1,205 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public enum CellVerticalEdge { + case top + case bottom +} + +extension CGFloat { + static let bma_epsilon: CGFloat = 0.001 +} + +extension BaseChatViewController { + + private static var nextDidEndScrollingAnimationHandlersKey: Int = 0 + private var nextDidEndScrollingAnimationHandlers: [() -> Void] { + get { objc_getAssociatedObject(self, &Self.nextDidEndScrollingAnimationHandlersKey) as? [() -> Void] ?? [] } + set { objc_setAssociatedObject(self, &Self.nextDidEndScrollingAnimationHandlersKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + public func isScrolledAtBottom() -> Bool { + guard let collectionView = self.collectionView else { return true } + guard collectionView.numberOfSections > 0 && collectionView.numberOfItems(inSection: 0) > 0 else { return true } + let sectionIndex = collectionView.numberOfSections - 1 + let itemIndex = collectionView.numberOfItems(inSection: sectionIndex) - 1 + let lastIndexPath = IndexPath(item: itemIndex, section: sectionIndex) + return self.isIndexPathVisible(lastIndexPath, atEdge: .bottom) + } + + public func isScrolledAtTop() -> Bool { + guard let collectionView = self.collectionView else { return true } + guard collectionView.numberOfSections > 0 && collectionView.numberOfItems(inSection: 0) > 0 else { return true } + let firstIndexPath = IndexPath(item: 0, section: 0) + return self.isIndexPathVisible(firstIndexPath, atEdge: .top) + } + + public func isCloseToBottom() -> Bool { + guard let collectionView = self.collectionView else { return true } + guard collectionView.contentSize.height > 0 else { return true } + return (self.visibleRect().maxY / collectionView.contentSize.height) > (1 - self.constants.autoloadingFractionalThreshold) + } + + public func isCloseToTop() -> Bool { + guard let collectionView = self.collectionView else { return true } + guard collectionView.contentSize.height > 0 else { return true } + return (self.visibleRect().minY / collectionView.contentSize.height) < self.constants.autoloadingFractionalThreshold + } + + public func isIndexPathVisible(_ indexPath: IndexPath, atEdge edge: CellVerticalEdge) -> Bool { + guard let collectionView = self.collectionView else { return true } + guard let attributes = collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath) else { return false } + let visibleRect = self.visibleRect() + let intersection = visibleRect.intersection(attributes.frame) + if edge == .top { + return abs(intersection.minY - attributes.frame.minY) < CGFloat.bma_epsilon + } else { + return abs(intersection.maxY - attributes.frame.maxY) < CGFloat.bma_epsilon + } + } + + public func visibleRect() -> CGRect { + guard let collectionView = self.collectionView else { return CGRect.zero } + let contentInset = collectionView.contentInset + let collectionViewBounds = collectionView.bounds + let contentSize = collectionView.collectionViewLayout.collectionViewContentSize + return CGRect(x: CGFloat(0), y: collectionView.contentOffset.y + contentInset.top, width: collectionViewBounds.width, height: min(contentSize.height, collectionViewBounds.height - contentInset.top - contentInset.bottom)) + } + + @objc + open func scrollToBottom(animated: Bool) { + guard let collectionView = self.collectionView else { return } + // Cancel current scrolling + collectionView.setContentOffset(collectionView.contentOffset, animated: false) + + // Note that we don't rely on collectionView's contentSize. This is because it won't be valid after performBatchUpdates or reloadData + // After reload data, collectionViewLayout.collectionViewContentSize won't be even valid, so you may want to refresh the layout manually + let offsetY = max(-collectionView.contentInset.top, collectionView.collectionViewLayout.collectionViewContentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom) + + // Don't use setContentOffset(:animated). If animated, contentOffset property will be updated along with the animation for each frame update + // If a message is inserted while scrolling is happening (as in very fast typing), we want to take the "final" content offset (not the "real time" one) to check if we should scroll to bottom again + if animated { + UIView.animate(withDuration: self.constants.updatesAnimationDuration, animations: { () -> Void in + collectionView.contentOffset = CGPoint(x: 0, y: offsetY) + }) + } else { + collectionView.contentOffset = CGPoint(x: 0, y: offsetY) + } + } + + public func scrollToPreservePosition(oldRefRect: CGRect?, newRefRect: CGRect?) { + guard let collectionView = self.collectionView else { return } + guard let oldRefRect = oldRefRect, let newRefRect = newRefRect else { + return + } + let diffY = newRefRect.minY - oldRefRect.minY + collectionView.contentOffset = CGPoint(x: 0, y: collectionView.contentOffset.y + diffY) + } + + public func scrollToItem(withId itemId: String, + position: UICollectionView.ScrollPosition = .centeredVertically, + animated: Bool = false, + spotlight: Bool = false) { + guard let collectionView = self.collectionView else { return } + guard let itemIndex = self.chatItemCompanionCollection.indexOf(itemId) else { return } + + let indexPath = IndexPath(item: itemIndex, section: 0) + guard let rect = self.rectAtIndexPath(indexPath) else { return } + + if animated { + let pageHeight = collectionView.bounds.height + let twoPagesHeight = pageHeight * 2 + let isScrollingUp = rect.minY < collectionView.contentOffset.y + + if isScrollingUp { + let isNeedToScrollUpMoreThenTwoPages = rect.minY < collectionView.contentOffset.y - twoPagesHeight + if isNeedToScrollUpMoreThenTwoPages { + let lastPageOriginY = collectionView.contentSize.height - pageHeight + var preScrollRect = rect + preScrollRect.origin.y = min(lastPageOriginY, rect.minY + pageHeight) + collectionView.scrollRectToVisible(preScrollRect, animated: false) + } + } else { + let isNeedToScrollDownMoreThenTwoPages = rect.minY > collectionView.contentOffset.y + twoPagesHeight + if isNeedToScrollDownMoreThenTwoPages { + var preScrollRect = rect + preScrollRect.origin.y = max(0, rect.minY - pageHeight) + collectionView.scrollRectToVisible(preScrollRect, animated: false) + } + } + } + + if spotlight { + guard let presenter = self.chatItemCompanionCollection[itemId]?.presenter else { return } + let contentOffsetWillBeChanged = !collectionView.indexPathsForVisibleItems.contains(indexPath) + if contentOffsetWillBeChanged { + self.nextDidEndScrollingAnimationHandlers.append { [weak presenter] in + presenter?.spotlight() + } + } else { + presenter.spotlight() + } + } + + collectionView.scrollToItem(at: indexPath, at: position, animated: animated) + } + + open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { + for handler in self.nextDidEndScrollingAnimationHandlers { + handler() + } + self.nextDidEndScrollingAnimationHandlers = [] + } + + open func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let collectionView = self.collectionView else { return } + if collectionView.isDragging { + self.autoLoadMoreContentIfNeeded() + } + self.scrollViewEventsHandler?.onScrollViewDidScroll(scrollView) + } + + open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + self.scrollViewEventsHandler?.onScrollViewDidEndDragging(scrollView, decelerate) + } + + open func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { + self.autoLoadMoreContentIfNeeded() + } + + open func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {} + open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {} + + public func autoLoadMoreContentIfNeeded() { + guard self.autoLoadingEnabled, let dataSource = self.chatDataSource else { return } + + if self.isCloseToTop() && dataSource.hasMorePrevious { + dataSource.loadPrevious() + } else if self.isCloseToBottom() && dataSource.hasMoreNext { + dataSource.loadNext() + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift new file mode 100644 index 00000000..e94221ce --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift @@ -0,0 +1,600 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public protocol KeyboardEventsHandling: AnyObject { + func onKeyboardStateDidChange(_ height: CGFloat, _ status: KeyboardStatus) +} + +public protocol ScrollViewEventsHandling: AnyObject { + func onScrollViewDidScroll(_ scrollView: UIScrollView) + func onScrollViewDidEndDragging(_ scrollView: UIScrollView, _ decelerate: Bool) +} + +public protocol ReplyActionHandler: AnyObject { + func handleReply(for: ChatItemProtocol) +} + +open class BaseChatViewController: UIViewController, + UICollectionViewDataSource, + UICollectionViewDelegate, + ChatDataSourceDelegateProtocol, + InputPositionControlling, + ReplyIndicatorRevealerDelegate { + + open weak var keyboardEventsHandler: KeyboardEventsHandling? + open weak var scrollViewEventsHandler: ScrollViewEventsHandling? + open var replyActionHandler: ReplyActionHandler? + open var replyFeedbackGenerator: ReplyFeedbackGeneratorProtocol? = BaseChatViewController.makeReplyFeedbackGenerator() + + open var layoutConfiguration: ChatLayoutConfigurationProtocol = ChatLayoutConfiguration.defaultConfiguration { + didSet { + self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) + } + } + + public struct Constants { + public var updatesAnimationDuration: TimeInterval = 0.33 + public var preferredMaxMessageCount: Int? = 500 // If not nil, will ask data source to reduce number of messages when limit is reached. @see ChatDataSourceDelegateProtocol + public var preferredMaxMessageCountAdjustment: Int = 400 // When the above happens, will ask to adjust with this value. It may be wise for this to be smaller to reduce number of adjustments + public var autoloadingFractionalThreshold: CGFloat = 0.05 // in [0, 1] + } + + public var constants = Constants() + + public struct UpdatesConfig { + + // Allows another performBatchUpdates to be called before completion of a previous one (not recommended). + // Changing this value after viewDidLoad is not supported + public var fastUpdates = true + + // If receiving data source updates too fast, while an update it's being processed, only the last one will be executed + public var coalesceUpdates = true + } + + public var updatesConfig = UpdatesConfig() + + open var customPresentersConfigurationPoint = false // If true then confugureCollectionViewWithPresenters() will not be called in viewDidLoad() method and has to be called manually + + public private(set) var collectionView: UICollectionView? + public final internal(set) var chatItemCompanionCollection = ChatItemCompanionCollection(items: []) + private var _chatDataSource: ChatDataSourceProtocol? + public final var chatDataSource: ChatDataSourceProtocol? { + get { + return _chatDataSource + } + set { + self.setChatDataSource(newValue, triggeringUpdateType: .normal) + } + } + + // If set to false messages will start appearing on top and goes down + // If true then messages will start from bottom and goes up. + public var placeMessagesFromBottom = false { + didSet { + self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) + } + } + + // If set to false user is responsible to make sure that view provided in loadView() implements BaseChatViewContollerViewProtocol. + // Must be set before loadView is called. + public var substitutesMainViewAutomatically = true + + // Custom update on setting the data source. if triggeringUpdateType is nil it won't enqueue any update (you should do it later manually) + public final func setChatDataSource(_ dataSource: ChatDataSourceProtocol?, triggeringUpdateType updateType: UpdateType?) { + self._chatDataSource = dataSource + self._chatDataSource?.delegate = self + if let updateType = updateType { + self.enqueueModelUpdate(updateType: updateType) + } + } + + deinit { + self.collectionView?.delegate = nil + self.collectionView?.dataSource = nil + } + + open override func loadView() { // swiftlint:disable:this prohibited_super_call + if substitutesMainViewAutomatically { + self.view = BaseChatViewControllerView() // http://stackoverflow.com/questions/24596031/uiviewcontroller-with-inputaccessoryview-is-not-deallocated + self.view.backgroundColor = UIColor.white + } else { + super.loadView() + } + + } + + override open func viewDidLoad() { + super.viewDidLoad() + self.addCollectionView() + self.addInputBarContainer() + self.addInputView() + self.addInputContentContainer() + self.setupKeyboardTracker() + self.setupTapGestureRecognizer() + } + + private func setupTapGestureRecognizer() { + self.collectionView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(BaseChatViewController.userDidTapOnCollectionView))) + } + + public var endsEditingWhenTappingOnChatBackground = true + @objc + open func userDidTapOnCollectionView() { + if self.endsEditingWhenTappingOnChatBackground { + self.view.endEditing(true) + } + } + + open override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.keyboardTracker.startTracking() + } + + open override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + self.keyboardTracker?.stopTracking() + } + + private func addCollectionView() { + let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: self.createCollectionViewLayout()) + collectionView.contentInset = self.layoutConfiguration.contentInsets + collectionView.scrollIndicatorInsets = self.layoutConfiguration.scrollIndicatorInsets + collectionView.alwaysBounceVertical = true + collectionView.backgroundColor = UIColor.clear + collectionView.keyboardDismissMode = .interactive + collectionView.showsVerticalScrollIndicator = true + collectionView.showsHorizontalScrollIndicator = false + collectionView.allowsSelection = false + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.autoresizingMask = [] + self.view.addSubview(collectionView) + NSLayoutConstraint.activate([ + self.view.topAnchor.constraint(equalTo: collectionView.topAnchor), + self.view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) + ]) + + let leadingAnchor: NSLayoutXAxisAnchor + let trailingAnchor: NSLayoutXAxisAnchor + if #available(iOS 11.0, *) { + let guide = self.view.safeAreaLayoutGuide + leadingAnchor = guide.leadingAnchor + trailingAnchor = guide.trailingAnchor + } else { + leadingAnchor = self.view.leadingAnchor + trailingAnchor = self.view.trailingAnchor + } + + NSLayoutConstraint.activate([ + collectionView.leadingAnchor.constraint(equalTo: leadingAnchor), + collectionView.trailingAnchor.constraint(equalTo: trailingAnchor) + ]) + + collectionView.dataSource = self + collectionView.delegate = self + collectionView.chatto_setContentInsetAdjustment(enabled: false, in: self) + collectionView.chatto_setAutomaticallyAdjustsScrollIndicatorInsets(false) + collectionView.chatto_setIsPrefetchingEnabled(false) + + self.cellPanGestureHandler = CellPanGestureHandler(collectionView: collectionView) + self.cellPanGestureHandler.replyDelegate = self + self.cellPanGestureHandler.config = self.cellPanGestureHandlerConfig + self.collectionView = collectionView + + if !self.customPresentersConfigurationPoint { + self.confugureCollectionViewWithPresenters() + } + } + + var unfinishedBatchUpdatesCount: Int = 0 + var onAllBatchUpdatesFinished: (() -> Void)? + + var inputContainerBottomConstraint: NSLayoutConstraint! + private func addInputBarContainer() { + self.inputBarContainer = UIView(frame: CGRect.zero) + self.inputBarContainer.autoresizingMask = UIView.AutoresizingMask() + self.inputBarContainer.translatesAutoresizingMaskIntoConstraints = false + self.inputBarContainer.backgroundColor = .white + self.view.addSubview(self.inputBarContainer) + NSLayoutConstraint.activate([ + self.inputBarContainer.topAnchor.constraint(greaterThanOrEqualTo: self.topLayoutGuide.bottomAnchor) + ]) + let leadingAnchor: NSLayoutXAxisAnchor + let trailingAnchor: NSLayoutXAxisAnchor + if #available(iOS 11.0, *) { + let guide = self.view.safeAreaLayoutGuide + leadingAnchor = guide.leadingAnchor + trailingAnchor = guide.trailingAnchor + } else { + leadingAnchor = self.view.leadingAnchor + trailingAnchor = self.view.trailingAnchor + } + + NSLayoutConstraint.activate([ + self.inputBarContainer.leadingAnchor.constraint(equalTo: leadingAnchor), + self.inputBarContainer.trailingAnchor.constraint(equalTo: trailingAnchor) + ]) + self.inputContainerBottomConstraint = self.view.bottomAnchor.constraint(equalTo: self.inputBarContainer.bottomAnchor) + self.view.addConstraint(self.inputContainerBottomConstraint) + } + + private func addInputView() { + let inputView = self.createChatInputView() + self.inputBarContainer.addSubview(inputView) + NSLayoutConstraint.activate([ + self.inputBarContainer.topAnchor.constraint(equalTo: inputView.topAnchor), + self.inputBarContainer.bottomAnchor.constraint(equalTo: inputView.bottomAnchor), + self.inputBarContainer.leadingAnchor.constraint(equalTo: inputView.leadingAnchor), + self.inputBarContainer.trailingAnchor.constraint(equalTo: inputView.trailingAnchor) + ]) + } + + private func addInputContentContainer() { + self.inputContentContainer = UIView(frame: CGRect.zero) + self.inputContentContainer.autoresizingMask = UIView.AutoresizingMask() + self.inputContentContainer.translatesAutoresizingMaskIntoConstraints = false + self.inputContentContainer.backgroundColor = .white + self.view.addSubview(self.inputContentContainer) + NSLayoutConstraint.activate([ + self.view.bottomAnchor.constraint(equalTo: self.inputContentContainer.bottomAnchor), + self.view.leadingAnchor.constraint(equalTo: self.inputContentContainer.leadingAnchor), + self.view.trailingAnchor.constraint(equalTo: self.inputContentContainer.trailingAnchor), + self.inputContentContainer.topAnchor.constraint(equalTo: self.inputBarContainer.bottomAnchor) + ]) + } + + private func updateInputContainerBottomBaseOffset() { + if #available(iOS 11.0, *) { + let offset = self.bottomLayoutGuide.length + if self.inputContainerBottomBaseOffset != offset { + self.inputContainerBottomBaseOffset = offset + } + } else { + // If we have been pushed on nav controller and hidesBottomBarWhenPushed = true, then ignore bottomLayoutMargin + // because it has incorrect value when we actually have a bottom bar (tabbar) + // Also if instance of BaseChatViewController is added as childViewController to another view controller, we had to check all this stuf on parent instance instead of self + // UPD: Fixed in iOS 11.0 + let navigatedController: UIViewController + if let parent = self.parent, !(parent is UINavigationController || parent is UITabBarController) { + navigatedController = parent + } else { + navigatedController = self + } + + if navigatedController.hidesBottomBarWhenPushed && (navigationController?.viewControllers.count ?? 0) > 1 && navigationController?.viewControllers.last == navigatedController { + self.inputContainerBottomBaseOffset = 0 + } else { + self.inputContainerBottomBaseOffset = self.bottomLayoutGuide.length + } + } + } + + private var inputContainerBottomBaseOffset: CGFloat = 0 { + didSet { self.updateInputContainerBottomConstraint() } + } + + private var inputContainerBottomAdditionalOffset: CGFloat = 0 { + didSet { self.updateInputContainerBottomConstraint() } + } + + private func updateInputContainerBottomConstraint() { + self.inputContainerBottomConstraint.constant = max(self.inputContainerBottomBaseOffset, self.inputContainerBottomAdditionalOffset) + self.view.setNeedsLayout() + } + + var isAdjustingInputContainer: Bool = false + + open func setupKeyboardTracker() { + let heightBlock = { [weak self] (bottomMargin: CGFloat, keyboardStatus: KeyboardStatus) in + guard let sSelf = self else { return } + if let keyboardObservingDelegate = sSelf.keyboardEventsHandler { + keyboardObservingDelegate.onKeyboardStateDidChange(bottomMargin, keyboardStatus) + } else { + sSelf.changeInputContentBottomMarginTo(bottomMargin) + } + } + self.keyboardTracker = KeyboardTracker(viewController: self, inputBarContainer: self.inputBarContainer, heightBlock: heightBlock, notificationCenter: self.notificationCenter) + + (self.view as? BaseChatViewControllerViewProtocol)?.bmaInputAccessoryView = self.keyboardTracker?.trackingView + } + + var notificationCenter = NotificationCenter.default + var keyboardTracker: KeyboardTracker! + + public private(set) var isFirstLayout: Bool = true + override open func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + self.adjustCollectionViewInsets(shouldUpdateContentOffset: true) + self.keyboardTracker.adjustTrackingViewSizeIfNeeded() + + if self.isFirstLayout { + self.updateQueue.start() + self.isFirstLayout = false + } + + self.updateInputContainerBottomBaseOffset() + } + + public var allContentFits: Bool { + guard let collectionView = self.collectionView else { return false } + let inputHeightWithKeyboard = self.view.bounds.height - self.inputBarContainer.frame.minY + let insetTop = self.topLayoutGuide.length + self.layoutConfiguration.contentInsets.top + let insetBottom = self.layoutConfiguration.contentInsets.bottom + inputHeightWithKeyboard + let availableHeight = collectionView.bounds.height - (insetTop + insetBottom) + let contentSize = collectionView.collectionViewLayout.collectionViewContentSize + return availableHeight >= contentSize.height + } + + private var previousBoundsUsedForInsetsAdjustment: CGRect? + func adjustCollectionViewInsets(shouldUpdateContentOffset: Bool) { + guard let collectionView = self.collectionView else { return } + let isInteracting = collectionView.panGestureRecognizer.numberOfTouches > 0 + let isBouncingAtTop = isInteracting && collectionView.contentOffset.y < -collectionView.contentInset.top + if !self.placeMessagesFromBottom && isBouncingAtTop { return } + + let inputHeightWithKeyboard = self.view.bounds.height - self.inputBarContainer.frame.minY + let newInsetBottom = self.layoutConfiguration.contentInsets.bottom + inputHeightWithKeyboard + let insetBottomDiff = newInsetBottom - collectionView.contentInset.bottom + var newInsetTop = self.topLayoutGuide.length + self.layoutConfiguration.contentInsets.top + let contentSize = collectionView.collectionViewLayout.collectionViewContentSize + + let needToPlaceMessagesAtBottom = self.placeMessagesFromBottom && self.allContentFits + if needToPlaceMessagesAtBottom { + let realContentHeight = contentSize.height + newInsetTop + newInsetBottom + newInsetTop += collectionView.bounds.height - realContentHeight + } + + let insetTopDiff = newInsetTop - collectionView.contentInset.top + let needToUpdateContentInset = self.placeMessagesFromBottom && (insetTopDiff != 0 || insetBottomDiff != 0) + + let prevContentOffsetY = collectionView.contentOffset.y + + let boundsHeightDiff: CGFloat = { + guard shouldUpdateContentOffset, let lastUsedBounds = self.previousBoundsUsedForInsetsAdjustment else { + return 0 + } + let diff = lastUsedBounds.height - collectionView.bounds.height + // When collectionView is scrolled to bottom and height increases, + // collectionView adjusts its contentOffset automatically + let isScrolledToBottom = contentSize.height <= collectionView.bounds.maxY - collectionView.contentInset.bottom + return isScrolledToBottom ? max(0, diff) : diff + }() + self.previousBoundsUsedForInsetsAdjustment = collectionView.bounds + + let newContentOffsetY: CGFloat = { + let minOffset = -newInsetTop + let maxOffset = contentSize.height - (collectionView.bounds.height - newInsetBottom) + let targetOffset = prevContentOffsetY + insetBottomDiff + boundsHeightDiff + return max(min(maxOffset, targetOffset), minOffset) + }() + + collectionView.contentInset = { + var currentInsets = collectionView.contentInset + currentInsets.bottom = newInsetBottom + currentInsets.top = newInsetTop + return currentInsets + }() + + collectionView.chatto_setVerticalScrollIndicatorInsets({ + var currentInsets = collectionView.scrollIndicatorInsets + currentInsets.bottom = self.layoutConfiguration.scrollIndicatorInsets.bottom + inputHeightWithKeyboard + currentInsets.top = self.topLayoutGuide.length + self.layoutConfiguration.scrollIndicatorInsets.top + return currentInsets + }()) + + guard shouldUpdateContentOffset else { return } + + let inputIsAtBottom = self.view.bounds.maxY - self.inputBarContainer.frame.maxY <= 0 + if isInteracting && (needToPlaceMessagesAtBottom || needToUpdateContentInset) { + collectionView.contentOffset.y = prevContentOffsetY + } else if self.allContentFits { + collectionView.contentOffset.y = -collectionView.contentInset.top + } else if !isInteracting || inputIsAtBottom { + collectionView.contentOffset.y = newContentOffsetY + } + } + + func rectAtIndexPath(_ indexPath: IndexPath?) -> CGRect? { + guard let collectionView = self.collectionView else { return nil } + guard let indexPath = indexPath else { return nil } + + return collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath)?.frame + } + + var autoLoadingEnabled: Bool = false + var cellPanGestureHandler: CellPanGestureHandler! + public private(set) var inputBarContainer: UIView! + public private(set) var inputContentContainer: UIView! + public internal(set) var presenterFactory: ChatItemPresenterFactoryProtocol! + let presentersByCell = NSMapTable(keyOptions: .weakMemory, valueOptions: .weakMemory) + var visibleCells: [IndexPath: UICollectionViewCell] = [:] // @see visibleCellsAreValid(changes:) + + public internal(set) var updateQueue: SerialTaskQueueProtocol = SerialTaskQueue() + + /** + - You can use a decorator to: + - Provide the ChatCollectionViewLayout with margins between messages + - Provide to your pressenters additional attributes to help them configure their cells (for instance if a bubble should show a tail) + - You can also add new items (for instance time markers or failed cells) + */ + public var chatItemsDecorator: ChatItemsDecoratorProtocol? + + open func createCollectionViewLayout() -> UICollectionViewLayout { + let layout = ChatCollectionViewLayout() + layout.delegate = self + return layout + } + + var layoutModel = ChatCollectionViewLayoutModel.createModel(0, itemsLayoutData: []) + + // MARK: Subclass overrides + + open func createPresenterFactory() -> ChatItemPresenterFactoryProtocol { + // Default implementation + return ChatItemPresenterFactory(presenterBuildersByType: self.createPresenterBuilders()) + } + + open func createPresenterBuilders() -> [ChatItemType: [ChatItemPresenterBuilderProtocol]] { + assert(false, "Override in subclass") + return [ChatItemType: [ChatItemPresenterBuilderProtocol]]() + } + + open func createChatInputView() -> UIView { + assert(false, "Override in subclass") + return UIView() + } + + /** + When paginating up we need to change the scroll position as the content is pushed down. + We take distance to top from beforeUpdate indexPath and then we make afterUpdate indexPath to appear at the same distance + */ + open func referenceIndexPathsToRestoreScrollPositionOnUpdate(itemsBeforeUpdate: ChatItemCompanionCollection, changes: CollectionChanges) -> (beforeUpdate: IndexPath?, afterUpdate: IndexPath?) { + let firstItemMoved = changes.movedIndexPaths.first + return (firstItemMoved?.indexPathOld as IndexPath?, firstItemMoved?.indexPathNew as IndexPath?) + } + + // MARK: ReplyIndicatorRevealerDelegate + + open func didPassThreshold(at: IndexPath) { + self.replyFeedbackGenerator?.generateFeedback() + } + + open func didFinishReplyGesture(at indexPath: IndexPath) { + let item = self.chatItemCompanionCollection[indexPath.item].chatItem + self.replyActionHandler?.handleReply(for: item) + } + + open func didCancelReplyGesture(at: IndexPath) {} + + public final var cellPanGestureHandlerConfig: CellPanGestureHandlerConfig = .defaultConfig() { + didSet { + self.cellPanGestureHandler?.config = self.cellPanGestureHandlerConfig + } + } + + private static func makeReplyFeedbackGenerator() -> ReplyFeedbackGeneratorProtocol? { + if #available(iOS 10, *) { + return ReplyFeedbackGenerator() + } + return nil + } + + // MARK: ChatDataSourceDelegateProtocol + + open func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol, updateType: UpdateType) { + self.enqueueModelUpdate(updateType: updateType) + } + + open func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol) { + self.enqueueModelUpdate(updateType: .normal) + } + + public var keyboardStatus: KeyboardStatus { + return self.keyboardTracker.keyboardStatus + } + + public var maximumInputSize: CGSize { + return self.view.bounds.size + } + + open var inputContentBottomMargin: CGFloat { + return self.inputContainerBottomConstraint.constant + } + + open func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool = false, callback: (() -> Void)? = nil) { + self.changeInputContentBottomMarginTo(newValue, animated: animated, duration: CATransaction.animationDuration(), callback: callback) + } + + open func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool = false, duration: CFTimeInterval, initialSpringVelocity: CGFloat = 0.0, callback: (() -> Void)? = nil) { + guard self.inputContainerBottomConstraint.constant != newValue else { callback?(); return } + if animated { + self.isAdjustingInputContainer = true + self.inputContainerBottomAdditionalOffset = newValue + CATransaction.begin() + UIView.animate( + withDuration: duration, + delay: 0.0, + usingSpringWithDamping: 1.0, + initialSpringVelocity: initialSpringVelocity, + options: .curveLinear, + animations: { self.view.layoutIfNeeded() }, + completion: { _ in }) + CATransaction.setCompletionBlock(callback) // this callback is guaranteed to be called + CATransaction.commit() + self.isAdjustingInputContainer = false + } else { + self.changeInputContentBottomMarginWithoutAnimationTo(newValue, callback: callback) + } + } + + open func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool = false, duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, callback: (() -> Void)? = nil) { + guard self.inputContainerBottomConstraint.constant != newValue else { callback?(); return } + if animated { + self.isAdjustingInputContainer = true + CATransaction.begin() + CATransaction.setAnimationTimingFunction(timingFunction) + self.inputContainerBottomAdditionalOffset = newValue + UIView.animate( + withDuration: duration, + animations: { self.view.layoutIfNeeded() }, + completion: { _ in } + ) + CATransaction.setCompletionBlock(callback) // this callback is guaranteed to be called + CATransaction.commit() + self.isAdjustingInputContainer = false + } else { + self.changeInputContentBottomMarginWithoutAnimationTo(newValue, callback: callback) + } + } + + private func changeInputContentBottomMarginWithoutAnimationTo(_ newValue: CGFloat, callback: (() -> Void)?) { + self.isAdjustingInputContainer = true + self.inputContainerBottomAdditionalOffset = newValue + self.view.layoutIfNeeded() + callback?() + self.isAdjustingInputContainer = false + } +} + +extension BaseChatViewController { // Rotation + + open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + guard isViewLoaded else { return } + guard let collectionView = self.collectionView else { return } + let shouldScrollToBottom = self.isScrolledAtBottom() + let referenceIndexPath = collectionView.indexPathsForVisibleItems.first + let oldRect = self.rectAtIndexPath(referenceIndexPath) + coordinator.animate(alongsideTransition: { (_) -> Void in + if shouldScrollToBottom { + self.scrollToBottom(animated: false) + } else { + let newRect = self.rectAtIndexPath(referenceIndexPath) + self.scrollToPreservePosition(oldRefRect: oldRect, newRefRect: newRect) + } + }, completion: nil) + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift new file mode 100644 index 00000000..11817c74 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift @@ -0,0 +1,49 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public protocol ChatLayoutConfigurationProtocol { + var contentInsets: UIEdgeInsets { get } + var scrollIndicatorInsets: UIEdgeInsets { get } +} + +public struct ChatLayoutConfiguration: ChatLayoutConfigurationProtocol { + public let contentInsets: UIEdgeInsets + public let scrollIndicatorInsets: UIEdgeInsets + + public init(contentInsets: UIEdgeInsets, scrollIndicatorInsets: UIEdgeInsets) { + self.contentInsets = contentInsets + self.scrollIndicatorInsets = scrollIndicatorInsets + } +} + +extension ChatLayoutConfiguration { + static var defaultConfiguration: ChatLayoutConfiguration { + let contentInsets = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) + let scrollIndicatorInsets = UIEdgeInsets.zero + return ChatLayoutConfiguration(contentInsets: contentInsets, + scrollIndicatorInsets: scrollIndicatorInsets) + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift new file mode 100644 index 00000000..6afe97f2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift @@ -0,0 +1,45 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +// If you wish to use your custom view instead of BaseChatViewControllerView, you must implement this protocol. +public protocol BaseChatViewControllerViewProtocol: class { + var bmaInputAccessoryView: UIView? { get set } +} + +// http://stackoverflow.com/questions/24596031/uiviewcontroller-with-inputaccessoryview-is-not-deallocated +final class BaseChatViewControllerView: UIView, BaseChatViewControllerViewProtocol { + + var bmaInputAccessoryView: UIView? + + override var inputAccessoryView: UIView? { + get { + return self.bmaInputAccessoryView + } + set { + self.bmaInputAccessoryView = newValue + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift new file mode 100644 index 00000000..6c07f7e3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift @@ -0,0 +1,200 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public protocol CellRevealing { + var allowRevealing: Bool { get } +} + +public protocol AccessoryViewRevealable: CellRevealing { + func revealAccessoryView(withOffset offset: CGFloat, animated: Bool) + func preferredOffsetToRevealAccessoryView() -> CGFloat? // This allows to sync size in case cells have different sizes for the accessory view. Nil -> no restriction +} + +public protocol ReplyIndicatorRevealable: CellRevealing { + func canShowReply() -> Bool + func revealReplyIndicator(withOffset offset: CGFloat, animated: Bool) -> Bool +} + +public protocol ReplyIndicatorRevealerDelegate: AnyObject { + func didPassThreshold(at: IndexPath) + func didFinishReplyGesture(at: IndexPath) + func didCancelReplyGesture(at: IndexPath) +} + +public struct CellPanGestureHandlerConfig { + public let angleThresholdInRads: CGFloat + public let threshold: CGFloat + public let accessoryViewTranslationMultiplier: CGFloat + public let replyIndicatorTranslationMultiplier: CGFloat + public var allowReplyRevealing: Bool = false + public var allowTimestampRevealing: Bool = true + + public static func defaultConfig() -> CellPanGestureHandlerConfig { + .init( + angleThresholdInRads: 0.0872665, // ~5 degrees + threshold: 30, + accessoryViewTranslationMultiplier: 1/2, + replyIndicatorTranslationMultiplier: 2/3 + ) + } + + func transformAccessoryViewTranslation(_ translation: CGFloat) -> CGFloat { + (translation - self.threshold) * self.accessoryViewTranslationMultiplier + } + + func transformReplyIndicatorTranslation(_ translation: CGFloat) -> CGFloat { + (translation - self.threshold) * self.replyIndicatorTranslationMultiplier + } +} + +final class CellPanGestureHandler: NSObject, UIGestureRecognizerDelegate { + + private let panRecognizer: UIPanGestureRecognizer = UIPanGestureRecognizer() + private let collectionView: UICollectionView + + init(collectionView: UICollectionView) { + self.collectionView = collectionView + super.init() + self.collectionView.addGestureRecognizer(self.panRecognizer) + self.panRecognizer.addTarget(self, action: #selector(CellPanGestureHandler.handlePan(_:))) + self.panRecognizer.delegate = self + } + + deinit { + self.panRecognizer.delegate = nil + self.collectionView.removeGestureRecognizer(self.panRecognizer) + } + + var isEnabled: Bool = true { + didSet { + self.panRecognizer.isEnabled = self.isEnabled + } + } + + var config = CellPanGestureHandlerConfig.defaultConfig() + + public weak var replyDelegate: ReplyIndicatorRevealerDelegate? + + @objc + private func handlePan(_ panRecognizer: UIPanGestureRecognizer) { + switch panRecognizer.state { + case .began: + break + case .changed: + let translation = panRecognizer.translation(in: self.collectionView) + if translation.x < 0 { + guard self.config.allowTimestampRevealing else { return } + self.revealAccessoryView(atOffset: self.config.transformAccessoryViewTranslation(-translation.x)) + } else { + guard let indexPath = self.collectionView.indexPathForItem(at: panRecognizer.location(in: self.collectionView)), + let cell = self.collectionView.cellForItem(at: indexPath) as? ReplyIndicatorRevealable, + cell.allowRevealing, + self.config.allowReplyRevealing, + cell.canShowReply() else { return } + + if self.replyIndexPath == nil, translation.x > self.config.threshold { + self.replyIndexPath = indexPath + self.collectionView.isScrollEnabled = false + } + self.revealReplyIndicator(atOffset: self.config.transformReplyIndicatorTranslation(translation.x)) + } + case .ended: + self.revealAccessoryView(atOffset: 0) + self.finishRevealingReply() + + case .failed, .cancelled: + self.revealAccessoryView(atOffset: 0) + self.cancelRevealingReply() + default: + break + } + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer != self.panRecognizer { + return true + } + + let translation = self.panRecognizer.translation(in: self.collectionView) + let x = abs(translation.x), y = abs(translation.y) + let angleRads = atan2(y, x) + return angleRads <= self.config.angleThresholdInRads + } + + private func revealAccessoryView(atOffset offset: CGFloat) { + // Find max offset (cells can have slighlty different timestamp size ( 3.00 am vs 11.37 pm ) + let cells: [AccessoryViewRevealable] = self.collectionView.visibleCells.compactMap({ $0 as? AccessoryViewRevealable }) + let offset = min(offset, cells.reduce(0) { (current, cell) -> CGFloat in + return max(current, cell.preferredOffsetToRevealAccessoryView() ?? 0) + }) + + for cell in self.collectionView.visibleCells { + if let cell = cell as? AccessoryViewRevealable, cell.allowRevealing { + cell.revealAccessoryView(withOffset: offset, animated: offset == 0) + } + } + } + + private var replyIndexPath: IndexPath? + private var overReplyThreshold = false + + private func revealReplyIndicator(atOffset offset: CGFloat) { + guard let indexPath = self.replyIndexPath, + let cell = self.collectionView.cellForItem(at: indexPath) as? ReplyIndicatorRevealable else { return } + let maxOffsetReached = cell.revealReplyIndicator(withOffset: offset, animated: offset == 0) + if maxOffsetReached != overReplyThreshold { + self.replyDelegate?.didPassThreshold(at: indexPath) + self.overReplyThreshold = maxOffsetReached + } + } + + private func finishRevealingReply() { + defer { self.cleanUpRevealingReply() } + guard let indexPath = self.replyIndexPath else { return } + if self.overReplyThreshold { + self.replyDelegate?.didFinishReplyGesture(at: indexPath) + } else { + self.replyDelegate?.didCancelReplyGesture(at: indexPath) + } + } + + private func cancelRevealingReply() { + defer { self.cleanUpRevealingReply() } + guard let indexPath = self.replyIndexPath else { return } + self.replyDelegate?.didCancelReplyGesture(at: indexPath) + } + + private func cleanUpRevealingReply() { + self.overReplyThreshold = false + self.revealReplyIndicator(atOffset: 0) + self.collectionView.isScrollEnabled = true + self.replyIndexPath = nil + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift new file mode 100644 index 00000000..45535684 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift @@ -0,0 +1,170 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public protocol ChatCollectionViewLayoutDelegate: class { + func chatCollectionViewLayoutModel() -> ChatCollectionViewLayoutModel +} + +public struct ChatCollectionViewLayoutModel { + let contentSize: CGSize + let layoutAttributes: [UICollectionViewLayoutAttributes] + let layoutAttributesBySectionAndItem: [[UICollectionViewLayoutAttributes]] + let calculatedForWidth: CGFloat + + public static func createModel(_ collectionViewWidth: CGFloat, itemsLayoutData: [(height: CGFloat, bottomMargin: CGFloat)]) -> ChatCollectionViewLayoutModel { + var layoutAttributes = [UICollectionViewLayoutAttributes]() + var layoutAttributesBySectionAndItem = [[UICollectionViewLayoutAttributes]]() + layoutAttributesBySectionAndItem.append([UICollectionViewLayoutAttributes]()) + + var verticalOffset: CGFloat = 0 + for (index, layoutData) in itemsLayoutData.enumerated() { + let indexPath = IndexPath(item: index, section: 0) + let (height, bottomMargin) = layoutData + let itemSize = CGSize(width: collectionViewWidth, height: height) + let frame = CGRect(origin: CGPoint(x: 0, y: verticalOffset), size: itemSize) + let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) + attributes.frame = frame + layoutAttributes.append(attributes) + layoutAttributesBySectionAndItem[0].append(attributes) + verticalOffset += itemSize.height + verticalOffset += bottomMargin + } + + return ChatCollectionViewLayoutModel( + contentSize: CGSize(width: collectionViewWidth, height: verticalOffset), + layoutAttributes: layoutAttributes, + layoutAttributesBySectionAndItem: layoutAttributesBySectionAndItem, + calculatedForWidth: collectionViewWidth + ) + } + + public static func createEmptyModel() -> ChatCollectionViewLayoutModel { + return ChatCollectionViewLayoutModel( + contentSize: .zero, + layoutAttributes: [], + layoutAttributesBySectionAndItem: [], + calculatedForWidth: 0 + ) + } +} + +open class ChatCollectionViewLayout: UICollectionViewLayout { + var layoutModel: ChatCollectionViewLayoutModel! + public weak var delegate: ChatCollectionViewLayoutDelegate? + + // Optimization: after reloadData we'll get invalidateLayout, but prepareLayout will be delayed until next run loop. + // Client may need to force prepareLayout after reloadData, but we don't want to compute layout again in the next run loop. + private var layoutNeedsUpdate = true + open override func invalidateLayout() { + super.invalidateLayout() + self.layoutNeedsUpdate = true + } + + open override func prepare() { + super.prepare() + guard self.layoutNeedsUpdate else { return } + guard let delegate = self.delegate else { + self.layoutModel = ChatCollectionViewLayoutModel.createEmptyModel() + return + } + var oldLayoutModel = self.layoutModel + self.layoutModel = delegate.chatCollectionViewLayoutModel() + self.layoutNeedsUpdate = false + DispatchQueue.global(qos: .default).async { () -> Void in + // Dealloc of layout with 5000 items take 25 ms on tests on iPhone 4s + // This moves dealloc out of main thread + if oldLayoutModel != nil { + // Use nil check above to remove compiler warning: Variable 'oldLayoutModel' was written to, but never read + oldLayoutModel = nil + } + } + } + + open override var collectionViewContentSize: CGSize { + return self.layoutModel?.contentSize ?? .zero + } + + open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + var attributesArray = [UICollectionViewLayoutAttributes]() + + // Find any cell that sits within the query rect. + guard let firstMatchIndex = self.layoutModel.layoutAttributes.binarySearch(predicate: { attribute in + if attribute.frame.intersects(rect) { + return .orderedSame + } + if attribute.frame.minY > rect.maxY { + return .orderedDescending + } + return .orderedAscending + }) else { return attributesArray } + + // Starting from the match, loop up and down through the array until all the attributes + // have been added within the query rect. + for attributes in self.layoutModel.layoutAttributes[..= rect.minY else { break } + attributesArray.append(attributes) + } + + for attributes in self.layoutModel.layoutAttributes[firstMatchIndex...] { + guard attributes.frame.minY <= rect.maxY else { break } + attributesArray.append(attributes) + } + + return attributesArray + } + + open override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + if indexPath.section < self.layoutModel.layoutAttributesBySectionAndItem.count && indexPath.item < self.layoutModel.layoutAttributesBySectionAndItem[indexPath.section].count { + return self.layoutModel.layoutAttributesBySectionAndItem[indexPath.section][indexPath.item] + } + assert(false, "Unexpected indexPath requested:\(indexPath)") + return nil + } + + open override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { + return self.layoutModel.calculatedForWidth != newBounds.width + } +} + +private extension Array { + + func binarySearch(predicate: (Element) -> ComparisonResult) -> Index? { + var lowerBound = startIndex + var upperBound = endIndex + + while lowerBound < upperBound { + let midIndex = lowerBound + (upperBound - lowerBound) / 2 + if predicate(self[midIndex]) == .orderedSame { + return midIndex + } else if predicate(self[midIndex]) == .orderedAscending { + lowerBound = midIndex + 1 + } else { + upperBound = midIndex + } + } + return nil + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift new file mode 100644 index 00000000..74e4a2b4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift @@ -0,0 +1,49 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 UpdateType: CaseIterable { + case normal + case firstLoad + case pagination + case reload + case messageCountReduction +} + +public protocol ChatDataSourceDelegateProtocol: class { + func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol) + func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol, updateType: UpdateType) +} + +public protocol ChatDataSourceProtocol: class { + var hasMoreNext: Bool { get } + var hasMorePrevious: Bool { get } + var chatItems: [ChatItemProtocol] { get } + var delegate: ChatDataSourceDelegateProtocol? { get set } + + func loadNext() // Should trigger chatDataSourceDidUpdate with UpdateType.Pagination + func loadPrevious() // Should trigger chatDataSourceDidUpdate with UpdateType.Pagination + func adjustNumberOfMessages(preferredMaxCount: Int?, focusPosition: Double, completion:(_ didAdjust: Bool) -> Void) // If you want, implement message count contention for performance, otherwise just call completion(false) +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift new file mode 100644 index 00000000..ca288fbe --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift @@ -0,0 +1,54 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public protocol ChatItemPresenterFactoryProtocol { + func createChatItemPresenter(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol + func configure(withCollectionView collectionView: UICollectionView) +} + +final class ChatItemPresenterFactory: ChatItemPresenterFactoryProtocol { + var presenterBuildersByType = [ChatItemType: [ChatItemPresenterBuilderProtocol]]() + + init(presenterBuildersByType: [ChatItemType: [ChatItemPresenterBuilderProtocol]]) { + self.presenterBuildersByType = presenterBuildersByType + } + + func createChatItemPresenter(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol { + for builder in self.presenterBuildersByType[chatItem.type] ?? [] { + if builder.canHandleChatItem(chatItem) { + return builder.createPresenterWithChatItem(chatItem) + } + } + return DummyChatItemPresenter() + } + + func configure(withCollectionView collectionView: UICollectionView) { + for presenterBuilder in self.presenterBuildersByType.flatMap({ $0.1 }) { + presenterBuilder.presenterType.registerCells(collectionView) + } + DummyChatItemPresenter.registerCells(collectionView) + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift new file mode 100644 index 00000000..30ee6c41 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift @@ -0,0 +1,123 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 protocol UniqueIdentificable { + var uid: String { get } +} + +public struct CollectionChangeMove: Equatable, Hashable { + public let indexPathOld: IndexPath + public let indexPathNew: IndexPath + public init(indexPathOld: IndexPath, indexPathNew: IndexPath) { + self.indexPathOld = indexPathOld + self.indexPathNew = indexPathNew + } +} + +public func == (lhs: CollectionChangeMove, rhs: CollectionChangeMove) -> Bool { + return lhs.indexPathOld == rhs.indexPathOld && lhs.indexPathNew == rhs.indexPathNew +} + +public struct CollectionChanges { + public let insertedIndexPaths: Set + public let deletedIndexPaths: Set + public let movedIndexPaths: [CollectionChangeMove] + + init(insertedIndexPaths: Set, deletedIndexPaths: Set, movedIndexPaths: [CollectionChangeMove]) { + self.insertedIndexPaths = insertedIndexPaths + self.deletedIndexPaths = deletedIndexPaths + self.movedIndexPaths = movedIndexPaths + } +} + +func generateChanges(oldCollection: [T], newCollection: [T]) -> CollectionChanges { + func indexed(_ values: [T]) -> [T: Int] { + var map: [T: Int] = .init(minimumCapacity: values.count) + for (index, value) in values.enumerated() { + map[value] = index + } + return map + } + + let oldIndexedCollection = indexed(oldCollection) + let newIndexedCollection = indexed(newCollection) + var deletedIndexPaths = Set() + var insertedIndexPaths = Set() + var movedIndexPaths = [CollectionChangeMove]() + + // Deletetions + for element in oldCollection { + let isDeleted = newIndexedCollection[element] == nil + if isDeleted { + deletedIndexPaths.insert(IndexPath(item: oldIndexedCollection[element]!, section: 0)) + } + } + + // Insertions and movements + for element in newCollection { + let newIndex = newIndexedCollection[element]! + let newIndexPath = IndexPath(item: newIndex, section: 0) + if let oldIndex = oldIndexedCollection[element] { + if oldIndex != newIndex { + let move = CollectionChangeMove( + indexPathOld: IndexPath(item: oldIndex, section: 0), + indexPathNew: newIndexPath + ) + movedIndexPaths.append(move) + } + } else { + // It's new + insertedIndexPaths.insert(newIndexPath) + } + } + + return CollectionChanges(insertedIndexPaths: insertedIndexPaths, + deletedIndexPaths: deletedIndexPaths, + movedIndexPaths: movedIndexPaths) +} + +func updated(collection: [IndexPath: T], withChanges changes: CollectionChanges) -> [IndexPath: T] { + var result = collection + + changes.deletedIndexPaths.forEach { (indexPath) in + result[indexPath] = nil + } + + var movedDestinations = Set() + changes.movedIndexPaths.forEach { (move) in + result[move.indexPathNew] = collection[move.indexPathOld] + movedDestinations.insert(move.indexPathNew) + if !movedDestinations.contains(move.indexPathOld) { + result[move.indexPathOld] = nil + } + } + + changes.insertedIndexPaths.forEach { (indexPath) in + result[indexPath] = nil + } + + return result +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift new file mode 100644 index 00000000..85c0628a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift @@ -0,0 +1,277 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public enum KeyboardStatus { + case hiding + case hidden + case showing + case shown +} + +public typealias KeyboardHeightBlock = (_ height: CGFloat, _ status: KeyboardStatus) -> Void + +class KeyboardTracker { + private(set) var keyboardStatus: KeyboardStatus = .hidden + private let view: UIView + var trackingView: UIView { + return self.keyboardTrackerView + } + private lazy var keyboardTrackerView: KeyboardTrackingView = { + let trackingView = KeyboardTrackingView() + trackingView.positionChangedCallback = { [weak self] in + guard let sSelf = self else { return } + if !sSelf.isPerformingForcedLayout { + sSelf.layoutInputAtTrackingViewIfNeeded() + } + } + return trackingView + }() + + var isTracking = false + var inputBarContainer: UIView + private var notificationCenter: NotificationCenter + + private var heightBlock: KeyboardHeightBlock + + init(viewController: UIViewController, inputBarContainer: UIView, heightBlock: @escaping KeyboardHeightBlock, notificationCenter: NotificationCenter) { + self.view = viewController.view + self.heightBlock = heightBlock + self.inputBarContainer = inputBarContainer + self.notificationCenter = notificationCenter + self.notificationCenter.addObserver( + self, + selector: #selector(KeyboardTracker.keyboardWillShow(_:)), + name: UIResponder.keyboardWillShowNotification, + object: nil + ) + self.notificationCenter.addObserver( + self, + selector: #selector(KeyboardTracker.keyboardDidShow(_:)), + name: UIResponder.keyboardDidShowNotification, + object: nil + ) + self.notificationCenter.addObserver( + self, + selector: #selector(KeyboardTracker.keyboardWillHide(_:)), + name: UIResponder.keyboardWillHideNotification, + object: nil + ) + self.notificationCenter.addObserver( + self, + selector: #selector(KeyboardTracker.keyboardDidHide(_:)), + name: UIResponder.keyboardDidHideNotification, + object: nil + ) + self.notificationCenter.addObserver( + self, + selector: #selector(KeyboardTracker.keyboardWillChangeFrame(_:)), + name: UIResponder.keyboardWillChangeFrameNotification, + object: nil + ) + } + + deinit { + self.notificationCenter.removeObserver(self) + } + + func startTracking() { + self.isTracking = true + } + + func stopTracking() { + self.isTracking = false + } + + @objc + private func keyboardWillShow(_ notification: Notification) { + guard self.isTracking else { return } + guard !self.isPerformingForcedLayout else { return } + let bottomConstraint = self.bottomConstraintFromNotification(notification) + guard bottomConstraint > 0 else { return } // Some keyboards may report initial willShow/DidShow notifications with invalid positions + self.keyboardStatus = .showing + self.layoutInputContainer(withBottomConstraint: bottomConstraint) + } + + @objc + private func keyboardDidShow(_ notification: Notification) { + guard self.isTracking else { return } + guard !self.isPerformingForcedLayout else { return } + + let bottomConstraint = self.bottomConstraintFromNotification(notification) + guard bottomConstraint > 0 else { return } // Some keyboards may report initial willShow/DidShow notifications with invalid positions + self.keyboardStatus = .shown + self.layoutInputContainer(withBottomConstraint: bottomConstraint) + self.adjustTrackingViewSizeIfNeeded() + } + + @objc + private func keyboardWillChangeFrame(_ notification: Notification) { + guard self.isTracking else { return } + let bottomConstraint = self.bottomConstraintFromNotification(notification) + if bottomConstraint == 0 { + self.keyboardStatus = .hiding + self.layoutInputAtBottom() + } + } + + @objc + private func keyboardWillHide(_ notification: Notification) { + guard self.isTracking else { return } + self.keyboardStatus = .hiding + self.layoutInputAtBottom() + } + + @objc + private func keyboardDidHide(_ notification: Notification) { + guard self.isTracking else { return } + self.keyboardStatus = .hidden + self.layoutInputAtBottom() + } + + private func bottomConstraintFromNotification(_ notification: Notification) -> CGFloat { + guard let rect = ((notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return 0 } + guard rect.height > 0 else { return 0 } + let rectInView = self.view.convert(rect, from: nil) + guard rectInView.maxY >=~ self.view.bounds.height else { return 0 } // Undocked keyboard + return max(0, self.view.bounds.height - rectInView.minY - self.keyboardTrackerView.intrinsicContentSize.height) + } + + private func bottomConstraintFromTrackingView() -> CGFloat { + guard self.keyboardTrackerView.superview != nil else { return 0 } + let trackingViewRect = self.view.convert(self.keyboardTrackerView.bounds, from: self.keyboardTrackerView) + return max(0, self.view.bounds.height - trackingViewRect.maxY) + } + + func adjustTrackingViewSizeIfNeeded() { + guard self.isTracking && self.keyboardStatus == .shown else { return } + self.adjustTrackingViewSize() + } + + private func adjustTrackingViewSize() { + let inputContainerHeight = self.inputBarContainer.bounds.height + if self.keyboardTrackerView.preferredSize.height != inputContainerHeight { + self.keyboardTrackerView.preferredSize.height = inputContainerHeight + self.isPerformingForcedLayout = true + + // Sometimes, the autolayout system doesn't finish the layout inside of the input bar container at this point. + // If it happens, then the input bar may have a height different than an input bar container. + // We need to ensure that their heights are the same; otherwise, it would lead to incorrect calculations that in turn affects lastKnownKeyboardHeight. + // Tracking view adjustment changes a keyboard height and triggers an update of lastKnownKeyboardHeight. + self.inputBarContainer.layoutIfNeeded() + self.keyboardTrackerView.window?.layoutIfNeeded() + + self.isPerformingForcedLayout = false + } + } + + private func layoutInputAtBottom() { + self.keyboardTrackerView.bounds.size.height = 0 + self.layoutInputContainer(withBottomConstraint: 0) + } + + var isPerformingForcedLayout: Bool = false + func layoutInputAtTrackingViewIfNeeded() { + guard self.isTracking && self.keyboardStatus == .shown else { return } + self.layoutInputContainer(withBottomConstraint: self.bottomConstraintFromTrackingView()) + } + + private func layoutInputContainer(withBottomConstraint constraint: CGFloat) { + self.isPerformingForcedLayout = true + self.heightBlock(constraint, self.keyboardStatus) + self.isPerformingForcedLayout = false + } +} + +private class KeyboardTrackingView: UIView { + + var positionChangedCallback: (() -> Void)? + var observedView: UIView? + + deinit { + if let observedView = self.observedView { + observedView.removeObserver(self, forKeyPath: "frame") + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.commonInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.commonInit() + } + + func commonInit() { + self.autoresizingMask = .flexibleHeight + self.isUserInteractionEnabled = false + self.backgroundColor = UIColor.clear + self.isHidden = true + } + + var preferredSize: CGSize = .zero { + didSet { + if oldValue != self.preferredSize { + self.invalidateIntrinsicContentSize() + self.window?.setNeedsLayout() + } + } + } + + override var intrinsicContentSize: CGSize { + return self.preferredSize + } + + override func didMoveToSuperview() { + if let observedView = self.observedView { + observedView.removeObserver(self, forKeyPath: "center") + self.observedView = nil + } + + if let newSuperview = self.superview { + newSuperview.addObserver(self, forKeyPath: "center", options: [.new, .old], context: nil) + self.observedView = newSuperview + } + + super.didMoveToSuperview() + } + + override func observeValue(forKeyPath keyPath: String?, + of object: Any?, + change: [NSKeyValueChangeKey: Any]?, + context: UnsafeMutableRawPointer?) { + guard let object = object as? UIView, let superview = self.superview else { return } + if object === superview { + guard let sChange = change else { return } + let oldCenter = (sChange[NSKeyValueChangeKey.oldKey] as! NSValue).cgPointValue + let newCenter = (sChange[NSKeyValueChangeKey.newKey] as! NSValue).cgPointValue + if oldCenter != newCenter { + self.positionChangedCallback?() + } + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift new file mode 100644 index 00000000..73b09336 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift @@ -0,0 +1,38 @@ +// +// The MIT License (MIT) +// +// Copyright (c) 2015-present Badoo Trading Limited. +// +// 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 + +public protocol ReplyFeedbackGeneratorProtocol { + func generateFeedback() +} + +@available(iOS 10, *) +struct ReplyFeedbackGenerator: ReplyFeedbackGeneratorProtocol { + + private let impactFeedbackGenerator = UIImpactFeedbackGenerator() + + func generateFeedback() { + self.impactFeedbackGenerator.impactOccurred() + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift new file mode 100644 index 00000000..6d01868e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift @@ -0,0 +1,39 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 + +public protocol InputPositionControlling: AnyObject { + + var keyboardStatus: KeyboardStatus { get } + + var inputBarContainer: UIView! { get } + var maximumInputSize: CGSize { get } + + var inputContentContainer: UIView! { get } + var inputContentBottomMargin: CGFloat { get } + + func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool, callback: (() -> Void)?) + func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool, duration: CFTimeInterval, initialSpringVelocity: CGFloat, callback: (() -> Void)?) + func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool, duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, callback: (() -> Void)?) +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift new file mode 100644 index 00000000..623f6b19 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift @@ -0,0 +1,79 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 ChatItemCompanionCollection: Collection { + + private let items: [ChatItemCompanion] + private let itemIndexesById: [String: Int] // Maping to the position in the array instead the item itself for better performance + + public init(items: [ChatItemCompanion]) { + var dictionary = [String: Int](minimumCapacity: items.count) + for (index, item) in items.enumerated() { + dictionary[item.uid] = index + dictionary[item.chatItem.uid] = index + } + self.items = items + self.itemIndexesById = dictionary + } + + public func indexOf(_ uid: String) -> Int? { + return self.itemIndexesById[uid] + } + + public subscript(index: Int) -> ChatItemCompanion { + return self.items[index] + } + + public subscript(uid: String) -> ChatItemCompanion? { + if let index = self.indexOf(uid) { + return self.items[index] + } + return nil + } + + public func makeIterator() -> IndexingIterator<[ChatItemCompanion]> { + return self.items.makeIterator() + } + + public func index(_ i: Int, offsetBy n: Int) -> Int { + return self.items.index(i, offsetBy: n) + } + + public func index(_ i: Int, offsetBy n: Int, limitedBy limit: Int) -> Int? { + return self.items.index(i, offsetBy: n, limitedBy: limit) + } + + public func index(after i: Int) -> Int { + return self.items.index(after: i) + } + + public var startIndex: Int { + return 0 + } + + public var endIndex: Int { + return self.items.count + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift new file mode 100644 index 00000000..d9f728c9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift @@ -0,0 +1,80 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 typealias TaskClosure = (_ completion: @escaping () -> Void) -> Void + +public protocol SerialTaskQueueProtocol { + func addTask(_ task: @escaping TaskClosure) + func start() + func stop() + func flushQueue() + var isEmpty: Bool { get } + var isStopped: Bool { get } +} + +public final class SerialTaskQueue: SerialTaskQueueProtocol { + public private(set) var isBusy = false + public private(set) var isStopped = true + + private var tasksQueue = [TaskClosure]() + + public init() {} + + public func addTask(_ task: @escaping TaskClosure) { + self.tasksQueue.append(task) + self.maybeExecuteNextTask() + } + + public func start() { + self.isStopped = false + self.maybeExecuteNextTask() + } + + public func stop() { + self.isStopped = true + } + + public func flushQueue() { + self.tasksQueue.removeAll() + } + + public var isEmpty: Bool { + return self.tasksQueue.isEmpty + } + + private func maybeExecuteNextTask() { + if !self.isStopped && !self.isBusy { + if !self.isEmpty { + let firstTask = self.tasksQueue.removeFirst() + self.isBusy = true + firstTask({ [weak self] () -> Void in + self?.isBusy = false + self?.maybeExecuteNextTask() + }) + } + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift new file mode 100644 index 00000000..e0bdaec7 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift @@ -0,0 +1,69 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015-present Badoo Trading Limited. + + 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 let scale = UIScreen.main.scale + +infix operator >=~ +func >=~ (lhs: CGFloat, rhs: CGFloat) -> Bool { + return round(lhs * scale) >= round(rhs * scale) +} + +extension UIScrollView { + func chatto_setContentInsetAdjustment(enabled: Bool, in viewController: UIViewController) { + #if swift(>=3.2) + if #available(iOS 11.0, *) { + self.contentInsetAdjustmentBehavior = enabled ? .always : .never + } else { + viewController.automaticallyAdjustsScrollViewInsets = enabled + } + #else + viewController.automaticallyAdjustsScrollViewInsets = enabled + #endif + } + + func chatto_setAutomaticallyAdjustsScrollIndicatorInsets(_ adjusts: Bool) { + if #available(iOS 13.0, *) { + self.automaticallyAdjustsScrollIndicatorInsets = adjusts + } + } + + func chatto_setVerticalScrollIndicatorInsets(_ insets: UIEdgeInsets) { + if #available(iOS 11.1, *) { + self.verticalScrollIndicatorInsets = insets + } else { + self.scrollIndicatorInsets = insets + } + } +} + +extension UICollectionView { + func chatto_setIsPrefetchingEnabled(_ isPrefetchingEnabled: Bool) { + if #available(iOS 10.0, *) { + self.isPrefetchingEnabled = isPrefetchingEnabled + } + } +} diff --git a/test/fixtures/cocoapods/Pods/Chatto/LICENSE b/test/fixtures/cocoapods/Pods/Chatto/LICENSE new file mode 100644 index 00000000..2bef5f8e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Badoo Development + +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. + diff --git a/test/fixtures/cocoapods/Pods/Chatto/README.md b/test/fixtures/cocoapods/Pods/Chatto/README.md new file mode 100644 index 00000000..11178631 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Chatto/README.md @@ -0,0 +1,92 @@ +# Chatto [![Build Status](https://travis-ci.org/badoo/Chatto.svg?branch=master)](https://travis-ci.org/badoo/Chatto) [![codecov.io](https://codecov.io/github/badoo/Chatto/coverage.svg?branch=master)](https://codecov.io/github/badoo/Chatto?branch=master) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Chatto.svg)](https://img.shields.io/cocoapods/v/Chatto.svg) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + + +`Chatto` is a Swift lightweight framework to build chat applications. It's been designed to be extensible and performant. Along with `Chatto` there is `ChattoAdditions`, a companion framework which includes cells for messages and an extensible input component. You can find more details about how it was implemented in our [blog](https://techblog.badoo.com/blog/2015/12/04/how-we-made-chatto/). See them in action! +
+ + + + +
+ +## Features +- Calculation of collection view changes and layout in background +- Supports pagination in both directions and autoloading +- Message count contention for fast pagination and rotation with thousands of messsages +- Accessory view revealing by swiping from right +- Interactive keyboard dismissal +- Text bubbles +- Photo bubbles +- Extensible input bar + +## How to use + +Check the [wiki!](https://github.com/badoo/Chatto/wiki) + +## How to contribute + +If you **just have a question**, please reach us in [our gitter room](https://gitter.im/chatto-framework/community) + +If you'd like to file a bug report, suggest changes or submit a pull request, please [check our contribution guide](.github/CONTRIBUTING.md) + +## How to install +### CocoaPods + +1. Make sure `use_frameworks!` is added to your `Podfile`. + +2. Include the following in your `Podfile`: + ``` + # Swift 5 + pod 'Chatto', '= 4.1.0' + pod 'ChattoAdditions', '= 4.1.0' # if you want to use the cells or the input component + ``` + ``` + # Swift 4.2 + pod 'Chatto', '= 3.4.0' + pod 'ChattoAdditions', '= 3.4.0' # if you want to use the cells or the input component + ``` + ``` + # Swift 4 + pod 'Chatto', '= 3.3.1' + pod 'ChattoAdditions', '= 3.3.1' # if you want to use the cells or the input component + ``` + ``` + # Swift 3 + pod 'Chatto', '= 3.2.0' + pod 'ChattoAdditions', '= 3.2.0' # if you want to use the cells or the input component + ``` + ``` + # Swift 2.x + pod 'Chatto', '= 2.1.0' + pod 'ChattoAdditions', '= 2.1.0' # if you want to use the cells or the input component + ``` +If you like living on the bleeding edge, you can use the `master` branch with: + ``` + pod 'Chatto', :git => 'https://github.com/badoo/Chatto', :branch => 'master' + pod 'ChattoAdditions', :git => 'https://github.com/badoo/Chatto', :branch => 'master' + ``` +3. Run `pod install` + +### Carthage + +If you’re using [Carthage](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos), simply add Chatto to your Cartfile: +``` +# Swift 5 +github "badoo/Chatto" +``` +``` +# Swift 2.x +github "badoo/Chatto" "swift-2" +``` + +### Manually + +1. Clone, add as a submodule or [download.](https://github.com/badoo/Chatto/archive/master.zip) +2. Drag and drop `Chatto` and/or `ChattoAdditions` project to your workspace +3. Add `Chatto` and/or `ChattoAdditions` to Embedded binaries + +## License +Source code is distributed under MIT license. + +## Blog +Read more on our [tech blog](https://medium.com/bumble-tech) or explore our other [open source projects](https://github.com/badoo) diff --git a/test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json b/test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json new file mode 100644 index 00000000..2c8800f2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json @@ -0,0 +1,26 @@ +{ + "name": "Chatto", + "version": "4.1.0", + "summary": "Chat framework in Swift", + "description": "Lightweight chat framework to build Chat apps", + "homepage": "https://github.com/badoo/Chatto", + "license": { + "type": "MIT" + }, + "platforms": { + "ios": "9.0" + }, + "authors": { + "Diego Sanchez": "diego.sanchezr@gmail.com", + "Anton Schukin": "a.p.schukin@gmail.com" + }, + "source": { + "git": "https://github.com/badoo/Chatto.git", + "tag": "4.1.0" + }, + "source_files": "Chatto/Source/**/*.{h,m,swift}", + "public_header_files": "Chatto/Source/**/*.h", + "requires_arc": true, + "swift_versions": "5.3", + "swift_version": "5.3" +} diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE b/test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/README.md b/test/fixtures/cocoapods/Pods/MDFInternationalization/README.md new file mode 100644 index 00000000..ec8f118d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/README.md @@ -0,0 +1,74 @@ +# MDFInternationalization + +MDFInternationalization assists in internationalizing your iOS app or components' user interface. + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/material-foundation/material-internationalization-ios/blob/develop/LICENSE) +[![GitHub release](https://img.shields.io/github/release/material-foundation/material-internationalization-ios.svg)](https://github.com/material-foundation/material-internationalization-ios/releases) +[![Build Status](https://travis-ci.org/material-foundation/material-internationalization-ios.svg?branch=stable)](https://travis-ci.org/material-foundation/material-internationalization-ios) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/MDFInternationalization.svg)](https://img.shields.io/cocoapods/v/MDFInternationalization.svg) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +## Right-to-Left calculations for CGRects and UIEdgeInsets + +A UIView is positioned within its superview in terms of a frame (CGRect) consisting of an +origin and a size. When a device is set to a language that is written from Right-to-Left (RTL), +we often want to mirror the interface around the vertical axis. This library contains +functions to assist in modifying frames and edge insets for RTL. + +``` obj-c +// To flip a subview's frame horizontally, pass in subview.frame and the width of its parent. +CGRect originalFrame = childView.frame; +CGRect flippedFrame = MDFRectFlippedHorizontally(originalFrame, CGRectGetWidth(self.bounds)); +childView.frame = flippedFrame; +``` + +## Mirroring Images + +A category on UIImage backports iOS 10's `[UIImage imageWithHorizontallyFlippedOrientation]` to +earlier versions of iOS. + +``` obj-c +// To mirror on image, invoke mdf_imageWithHorizontallyFlippedOrientation. +UIImage *mirroredImage = [originalImage mdf_imageWithHorizontallyFlippedOrientation]; +``` + +## Adding semantic context + +A category on UIView backports iOS 9's `-[UIView semanticContentAttribute]` and iOS 10's +`-[UIView effectiveUserInterfaceLayoutDirection]` to earlier versions of iOS. + +``` obj-c +// To set a semantic content attribute, set the mdf_semanticContentAttribute property. +lockedLTRView.mdf_semanticContentAttribute = UISemanticContentAttributeForceLeftToRight; + +// mdf_semanticContentAttribute is used to calculate the mdf_effectiveUserInterfaceLayoutDirection +if (customView.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { + // Update customView's layout to be in RTL mode. +} +``` + +## Embedding Bi-directional strings + +A category on NSString offers a simple API to wrap strings in Unicode markers so that LTR +and RTL text can co-exist in the same string. + +``` obj-c +// To embed an RTL string in an existing LTR string we should wrap it in Unicode directionality +// markers to maintain preoper rendering. + +// The name of a restaurant is in Arabic (RTL), but the rest of string is in Latin (LTR). +NSString *wrappedRestaurantName = + [restaurantName mdf_stringWithStereoReset:NSLocaleLanguageDirectionRightToLeft + context:NSLocaleLanguageDirectionLeftToRight]; + +NSString *reservationString = [NSString stringWithFormat:@"%@ : %ld", wrappedRestaurantName, attendees]; +``` + +## Usage + +See Examples/Flags for a detailed example of how to use the functionality provided by this library. + + +## License + +MDFInternationalization is licensed under the [Apache License Version 2.0](LICENSE). diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h new file mode 100644 index 00000000..f9ab12ef --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h @@ -0,0 +1,29 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MDFRTL.h" // IWYU pragma: export +#import "NSLocale+MaterialRTL.h" // IWYU pragma: export +#import "NSString+MaterialBidi.h" // IWYU pragma: export +#import "UIImage+MaterialRTL.h" // IWYU pragma: export +#import "UIView+MaterialRTL.h" // IWYU pragma: export + +//! Project version number for MDFInternationalization. +FOUNDATION_EXPORT double MDFInternationalizationVersionNumber; + +//! Project version string for MDFInternationalization. +FOUNDATION_EXPORT const unsigned char MDFInternationalizationVersionString[]; diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h new file mode 100644 index 00000000..595dc949 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h @@ -0,0 +1,105 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +/** + Leading autoresizing mask based on layoutDirection. 'Leading' is 'Left' in + UIUserInterfaceLayoutDirectionLeftToRight, 'Right' otherwise. + + @param layoutDirection The layout direction to consider when computing the autoresizing mask. + @return The leading margin part of an autoresizing mask. + */ +FOUNDATION_EXPORT UIViewAutoresizing MDFLeadingMarginAutoresizingMaskForLayoutDirection( + UIUserInterfaceLayoutDirection layoutDirection); + +/** + Trailing autoresizing masks based on layoutDirection. 'Trailing' is 'Right' in + UIUserInterfaceLayoutDirectionLeftToRight, 'Left' otherwise. + + @param layoutDirection The layout direction to consider to compute the autoresizing mask. + @return The trailing margin part of an autoresizing mask. + */ +FOUNDATION_EXPORT UIViewAutoresizing MDFTrailingMarginAutoresizingMaskForLayoutDirection( + UIUserInterfaceLayoutDirection layoutDirection); + +/** + The frame to use when actually laying out a view in its superview. + + A view is conceptually positioned within its superview in terms of leading/trailing. When it's time + to actually lay out (i.e. setting frames), you position the frame as you usually would, but if you + are in the opposite layout direction you call this function to return a rect that has been flipped + around the vertical axis. + + @note Example: Flipping the frame of a subview 50pts wide at 10pts from the leading edge of a + bounding view. + + CGRect frame = CGRectMake(10, originY, 50, height); + CGFloat containerWidth = CGRectGetWidth(self.bounds); + if (layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { + frame = MDFRectFlippedHorizontally(frame, containerWidth); + } + + In LTR, frame is { { 10, originY }, { 50, height } } in a 100 wide bounding view. + +----------------------------------------100----------------------------------------+ + | | + | 10 +--------------------50--------------------+ | + | | | | + | +------------------------------------------+ | + | | + +----------------------------------------100----------------------------------------+ + + In RTL, frame is { { 40, originY }, { 50, height } }. + +----------------------------------------100----------------------------------------+ + | | + | 40 +--------------------50--------------------+ | + | | | | + | +------------------------------------------+ | + | | + +----------------------------------------100----------------------------------------+ + + @param frame The frame to convert. + @param containerWidth The superview's bounds's width. + @return The frame mirrored around the vertical axis. + */ +FOUNDATION_EXPORT CGRect MDFRectFlippedHorizontally(CGRect frame, CGFloat containerWidth); + + +/** + Creates a UIEdgeInsets instance with its left and right values exchanged. + + @param insets The insets we are intending to flip horizontally. + @return Insets with the right and left values exchanged. + */ +FOUNDATION_EXPORT UIEdgeInsets MDFInsetsFlippedHorizontally(UIEdgeInsets insets); + +/** + Creates a UIEdgeInsets instance from the parameters while obeying layoutDirection. + + If layoutDirection is UIUserInterfaceLayoutDirectionLeftToRight, then the left inset is leading and + the right inset is trailing, otherwise they are reversed. + + @param top The top inset. + @param leading The leading inset. + @param bottom The bottom inset. + @param trailing The trailing inset. + @return Insets in terms of left/right, already internationalized based on the layout direction. + */ +FOUNDATION_EXPORT UIEdgeInsets MDFInsetsMakeWithLayoutDirection(CGFloat top, + CGFloat leading, + CGFloat bottom, + CGFloat trailing, + UIUserInterfaceLayoutDirection layoutDirection); diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m new file mode 100644 index 00000000..e5df8824 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m @@ -0,0 +1,73 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDFRTL.h" + +UIViewAutoresizing MDFLeadingMarginAutoresizingMaskForLayoutDirection( + UIUserInterfaceLayoutDirection layoutDirection) { + switch (layoutDirection) { + case UIUserInterfaceLayoutDirectionLeftToRight: + return UIViewAutoresizingFlexibleLeftMargin; + case UIUserInterfaceLayoutDirectionRightToLeft: + return UIViewAutoresizingFlexibleRightMargin; + } + NSCAssert(NO, @"Invalid enumeration value %i.", (int)layoutDirection); + return UIViewAutoresizingFlexibleLeftMargin; +} + +UIViewAutoresizing MDFTrailingMarginAutoresizingMaskForLayoutDirection( + UIUserInterfaceLayoutDirection layoutDirection) { + switch (layoutDirection) { + case UIUserInterfaceLayoutDirectionLeftToRight: + return UIViewAutoresizingFlexibleRightMargin; + case UIUserInterfaceLayoutDirectionRightToLeft: + return UIViewAutoresizingFlexibleLeftMargin; + } + NSCAssert(NO, @"Invalid enumeration value %i.", (int)layoutDirection); + return UIViewAutoresizingFlexibleRightMargin; +} + +CGRect MDFRectFlippedHorizontally(CGRect frame, CGFloat containerWidth) { + CGRect flippedRect = CGRectStandardize(frame); + CGFloat leadingInset = CGRectGetMinX(flippedRect); + CGFloat width = CGRectGetWidth(flippedRect); + flippedRect.origin.x = containerWidth - leadingInset - width; + + return flippedRect; +} + +UIEdgeInsets MDFInsetsFlippedHorizontally(UIEdgeInsets insets) { + UIEdgeInsets flippedInsets = insets; + flippedInsets.left = insets.right; + flippedInsets.right = insets.left; + + return flippedInsets; +} + +UIEdgeInsets MDFInsetsMakeWithLayoutDirection(CGFloat top, + CGFloat leading, + CGFloat bottom, + CGFloat trailing, + UIUserInterfaceLayoutDirection layoutDirection) { + switch (layoutDirection) { + case UIUserInterfaceLayoutDirectionLeftToRight: + return UIEdgeInsetsMake(top, leading, bottom, trailing); + case UIUserInterfaceLayoutDirectionRightToLeft: + return UIEdgeInsetsMake(top, trailing, bottom, leading); + } + NSCAssert(NO, @"Invalid enumeration value %i.", (int)layoutDirection); + return UIEdgeInsetsMake(top, leading, bottom, trailing); +} diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h new file mode 100644 index 00000000..f73c95ab --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h @@ -0,0 +1,36 @@ +/* + Copyright 2018-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +@interface NSLocale (MaterialRTL) + +/** + Is the direction of the current locale's default language Left-To-Right? + + @return YES if the language is LTR, NO if the language is any other direction. + */ ++ (BOOL)mdf_isDefaultLanguageLTR; + +/** + Is the direction of the current locale's default language Right-To-Left? + + @return YES if the language is RTL, NO if the language is any other direction. + */ ++ (BOOL)mdf_isDefaultLanguageRTL; + +@end + diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m new file mode 100644 index 00000000..21256d2d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m @@ -0,0 +1,38 @@ +/* + Copyright 2018-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "NSLocale+MaterialRTL.h" + +@implementation NSLocale (MaterialRTL) + ++ (BOOL)mdf_isDefaultLanguageLTR { + NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; + NSLocaleLanguageDirection characterDirection = + [NSLocale characterDirectionForLanguage:languageCode]; + BOOL localeLanguageDirectionIsLTR = (characterDirection == NSLocaleLanguageDirectionLeftToRight); + return localeLanguageDirectionIsLTR; +} + ++ (BOOL)mdf_isDefaultLanguageRTL { + NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; + NSLocaleLanguageDirection characterDirection = + [NSLocale characterDirectionForLanguage:languageCode]; + BOOL localeLanguageDirectionIsRTL = (characterDirection == NSLocaleLanguageDirectionRightToLeft); + return localeLanguageDirectionIsRTL; +} + +@end + diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h new file mode 100644 index 00000000..5aeec86a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h @@ -0,0 +1,84 @@ +/* + Copyright 2018-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +@interface NSString (MaterialBidi) + +/** + Uses CFStringTokenizerCopyBestStringLanguage to determine string's language direction. + If the language direction is unknown or vertical returns left-to-right. + + As CFStringTokenizerCopyBestStringLanguage is Apple's API, its result may change if + Apple improves or modifies the implementation. + + @return the direction of the string + */ +- (NSLocaleLanguageDirection)mdf_calculatedLanguageDirection; + +/** + Initializes a copy of the string tagged with the given language direction. This + formatting adds the appropriate Unicode embedding characters at the beginning and end of the + string. + + Only NSLocaleLanguageDirectionLeftToRight and NSLocaleLanguageDirectionRightToLeft + language directions are supported. Other values of NSLocalLanguageDirection will + return a copy of self. + + Returns a string wrapped with Unicode bidi formatting characters by inserting these characters + around the string: + RLE+|string|+PDF for RTL text, or LRE+|string|+PDF for LTR text. + + @returns the new string. + */ +- (nonnull NSString *)mdf_stringWithBidiEmbedding:(NSLocaleLanguageDirection)languageDirection; + +/** + Returns a copy of the string explicitly tagged with a language direction. + + Uses mdf_calculatedLanguageDirection to determine string's language direction then invokes + mdf_stringWithBidiEmbedding:. + + @return the new string. + */ +- (nonnull NSString *)mdf_stringWithBidiEmbedding; + +/** + This method will wrap the string in embedding (LRE/RLE and PDF) characters, based on the string + direction and additionally wrapping the string in marks (LRM and RLM) if the string's direction + is different from the context direction. + + |direction| can be NSLocaleLanguageDirectionLeftToRight, NSLocaleLanguageDirectionRightToLeft, or + NSLocaleLanguageDirectionUnknown. If NSLocaleLanguageDirectionUnknown, the direction of the string + will be calculated with mdf_calculatedLanguageDirection. + + |contextDirection| must be specified and cannot be unknown. Only + NSLocaleLanguageDirectionLeftToRight and NSLocaleLanguageDirectionRightToLeft language directions + are supported. + + @returns the new string. + */ +- (nonnull NSString *)mdf_stringWithStereoReset:(NSLocaleLanguageDirection)direction + context:(NSLocaleLanguageDirection)contextDirection; + +/** + Returns a new string in which all occurrences of Unicode bidirectional format markers are removed. + + @returns the new string. + */ +- (nonnull NSString *)mdf_stringWithBidiMarkersStripped; + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m new file mode 100644 index 00000000..239baa97 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m @@ -0,0 +1,165 @@ +/* + Copyright 2018-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "NSString+MaterialBidi.h" + +#import + +@implementation NSString (MaterialBidi) + +// https://www.w3.org/International/questions/qa-bidi-unicode-controls +//TODO: Reach out to AAA about the utility of the Isolate markers +// ??? Do we want Embedding or Isolate markers? w3 recommends isolate? +// Add reference : Unicode® Standard Annex #9 UNICODE BIDIRECTIONAL ALGORITHM +// go/android-bidiformatter +// http://unicode.org/reports/tr9/ + +// Mark influences the directionality of neutral characters when the context is opposite of the +// neutral chatacter's desired directionality. +static NSString *kMDFLTRMark = @"\u200e"; // left-to-right mark +static NSString *kMDFRTLMark = @"\u200f"; // right-to-left mark + +// Embedding indicates a text segment is embedded in a larger context with the opposite +// directionality. +static NSString *kMDFLTREmbedding = @"\u202a"; // left-to-right embedding +static NSString *kMDFRTLEmbedding = @"\u202b"; // right-to-left embedding + +// Override reverses the directionality of strongly LTR or RTL characters +static NSString *kMDFLTROverride = @"\u202d"; // left-to-right override +static NSString *kMDFRTLOverride = @"\u202e"; // right-to-left override + +// Pop is used to denote the end of an embedding or override text segment +static NSString *kMDFPopFormatting = @"\u202c"; // pop directional formatting + +// Version 6.3.0 Bidi algorithm additions +// The following only work on iOS 10+ + +// Isolate indicates that the text segment has an internal directionality with no effect on +// surrounding characters. +static NSString *kMDFLTRIsolate = @"\u2066"; // left-to-right isolate +static NSString *kMDFRTLIsolate = @"\u2067"; // right-to-left isolate +static NSString *kMDFFirstStrongIsolate = @"\u2068"; // first strong isolate + +// Pop Isolate is used to denote the end of an isolate text segment +static NSString *kMDFPopIsolate = @"\u2069"; // pop directional isolate + + +- (NSLocaleLanguageDirection)mdf_calculatedLanguageDirection { + // Attempt to determine language of string. + NSLocaleLanguageDirection languageDirection = NSLocaleLanguageDirectionUnknown; + + // Pass string into CoreFoundation's language identifier + CFStringRef text = (__bridge CFStringRef)self; + CFRange range = CFRangeMake(0, (CFIndex)[self length]); + NSString *languageCode = + (NSString *)CFBridgingRelease(CFStringTokenizerCopyBestStringLanguage(text, range)); + if (languageCode) { + // If we identified a language, explicitly set the string direction based on that + languageDirection = [NSLocale characterDirectionForLanguage:languageCode]; + } + + // If the result is not LTR or RTL, fallback to LTR + // ??? Should I be defaulting to NSLocale.NSLocaleLanguageCode.characterDiretion? + if (languageDirection != NSLocaleLanguageDirectionLeftToRight && + languageDirection != NSLocaleLanguageDirectionRightToLeft) { + languageDirection = NSLocaleLanguageDirectionLeftToRight; + } + + return languageDirection; +} + +- (NSString *)mdf_stringWithBidiEmbedding { + NSLocaleLanguageDirection languageDirection = [self mdf_calculatedLanguageDirection]; + + return [self mdf_stringWithBidiEmbedding:languageDirection]; +} + +- (NSString *)mdf_stringWithBidiEmbedding:(NSLocaleLanguageDirection)languageDirection { + if (languageDirection == NSLocaleLanguageDirectionRightToLeft) { + return [NSString stringWithFormat:@"%@%@%@", kMDFRTLEmbedding, self, kMDFPopFormatting]; + } else if (languageDirection == NSLocaleLanguageDirectionLeftToRight) { + return [NSString stringWithFormat:@"%@%@%@", kMDFLTREmbedding, self, kMDFPopFormatting]; + } else { + // Return a copy original string if an unsupported direction is passed in. + return [self copy]; + } +} + +- (nonnull NSString *)mdf_stringWithStereoReset:(NSLocaleLanguageDirection)direction + context:(NSLocaleLanguageDirection)contextDirection { +#if DEBUG + // Disable in release, as a pre-caution in case not everyone defines NS_BLOCK_ASSERTION. + NSCAssert((contextDirection != NSLocaleLanguageDirectionLeftToRight || + contextDirection != NSLocaleLanguageDirectionRightToLeft), + @"contextStringDirection must be passed in and set to either" + "NSLocaleLanguageDirectionLeftToRight or NSLocaleLanguageDirectionRightToLeft."); + + NSCAssert((direction != NSLocaleLanguageDirectionLeftToRight || + direction != NSLocaleLanguageDirectionRightToLeft || + direction != NSLocaleLanguageDirectionUnknown), + @"stringToBeInsertedDirection must be set to either NSLocaleLanguageDirectionUnknown," + "NSLocaleLanguageDirectionLeftToRight, or NSLocaleLanguageDirectionRightToLeft."); +#endif + + if (self.length == 0) { + return [self copy]; + } + + if (direction != NSLocaleLanguageDirectionLeftToRight && + direction != NSLocaleLanguageDirectionRightToLeft) { + direction = [self mdf_calculatedLanguageDirection]; + } + + NSString *bidiEmbeddedString = [self mdf_stringWithBidiEmbedding:direction]; + + NSString *bidiResetString; + if (direction != contextDirection) { + if (contextDirection == NSLocaleLanguageDirectionRightToLeft) { + bidiResetString = + [NSString stringWithFormat:@"%@%@%@", kMDFRTLMark, bidiEmbeddedString, kMDFRTLMark]; + } else { + bidiResetString = + [NSString stringWithFormat:@"%@%@%@", kMDFLTRMark, bidiEmbeddedString, kMDFLTRMark]; + } + } else { + bidiResetString = bidiEmbeddedString; + } + + return bidiResetString; +} + +- (NSString *)mdf_stringWithBidiMarkersStripped { + NSString *strippedString = self; + NSArray *directionalMarkers = @[ kMDFLTRMark, + kMDFRTLMark, + kMDFRTLEmbedding, + kMDFLTREmbedding, + kMDFRTLOverride, + kMDFLTROverride, + kMDFPopFormatting, + kMDFLTRIsolate, + kMDFRTLIsolate, + kMDFFirstStrongIsolate, + kMDFPopIsolate + ]; + for (NSString *markerString in directionalMarkers) { + strippedString = + [strippedString stringByReplacingOccurrencesOfString:markerString withString:@""]; + } + return strippedString; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h new file mode 100644 index 00000000..36a1e171 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h @@ -0,0 +1,33 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +/** + Backporting of iOS 10's [UIImage imageWithHorizontallyFlippedOrientation]. + */ + +@interface UIImage (MaterialRTL) + +/** + On iOS 10 and above, calls [UIImage imageWithHorizontallyFlippedOrientation]. + Otherwise manually flips the image and returns the result. + + @return A horizontally mirrored version of this image. + */ +- (nonnull UIImage *)mdf_imageWithHorizontallyFlippedOrientation; + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m new file mode 100644 index 00000000..b75d7343 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m @@ -0,0 +1,168 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "UIImage+MaterialRTL.h" + +#import +#import + +/** Returns the horizontally flipped version of the given UIImageOrientation. */ +static UIImageOrientation MDFRTLMirroredOrientation(UIImageOrientation sourceOrientation) { + switch (sourceOrientation) { + case UIImageOrientationUp: + return UIImageOrientationUpMirrored; + case UIImageOrientationDown: + return UIImageOrientationDownMirrored; + case UIImageOrientationLeft: + return UIImageOrientationLeftMirrored; + case UIImageOrientationRight: + return UIImageOrientationRightMirrored; + case UIImageOrientationUpMirrored: + return UIImageOrientationUp; + case UIImageOrientationDownMirrored: + return UIImageOrientationDown; + case UIImageOrientationLeftMirrored: + return UIImageOrientationLeft; + case UIImageOrientationRightMirrored: + return UIImageOrientationRight; + } + NSCAssert(NO, @"Invalid enumeration value %i.", (int)sourceOrientation); + return UIImageOrientationUpMirrored; +} + +/** + Returns a copy of the image actually flipped. The orientation and scale are consumed, while the + rendering mode is ported to the new image. + */ +static UIImage *MDFRTLFlippedImage(UIImage *image) { + CGSize size = image.size; + CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height); + + UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetShouldAntialias(context, true); + CGContextSetInterpolationQuality(context, kCGInterpolationHigh); + + // Note: UIKit's and CoreGraphics coordinates systems are flipped vertically (UIKit's Y axis goes + // down, while CoreGraphics' goes up). + switch (image.imageOrientation) { + case UIImageOrientationUp: + CGContextScaleCTM(context, -1, -1); + CGContextTranslateCTM(context, -rect.size.width, -rect.size.height); + break; + case UIImageOrientationDown: + // Orientation down is equivalent to a 180º rotation. The difference in coordinates systems is + // thus sufficient and nothing needs to be down to flip the image. + break; + case UIImageOrientationLeft: + CGContextRotateCTM(context, -(CGFloat)M_PI_2); + CGContextTranslateCTM(context, -rect.size.width, 0); + break; + case UIImageOrientationRight: + CGContextRotateCTM(context, (CGFloat)M_PI_2); + CGContextTranslateCTM(context, 0, -rect.size.width); + break; + case UIImageOrientationUpMirrored: + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -rect.size.height); + break; + case UIImageOrientationDownMirrored: + CGContextScaleCTM(context, -1, 1); + CGContextTranslateCTM(context, -rect.size.width, 0); + break; + case UIImageOrientationLeftMirrored: + CGContextRotateCTM(context, -(CGFloat)M_PI_2); + CGContextTranslateCTM(context, -rect.size.width, 0); + CGContextScaleCTM(context, -1, 1); + CGContextTranslateCTM(context, -rect.size.width, 0); + break; + case UIImageOrientationRightMirrored: + CGContextRotateCTM(context, (CGFloat)M_PI_2); + CGContextTranslateCTM(context, 0, -rect.size.width); + CGContextScaleCTM(context, -1, 1); + CGContextTranslateCTM(context, -rect.size.width, 0); + break; + default: + NSCAssert(NO, @"Invalid enumeration value %i.", (int)image.imageOrientation); + } + + // If the UIImage is not backed by a CGImage, create one from the CIImage + if (image.CGImage) { + CGContextDrawImage(context, rect, image.CGImage); + } else if (image.CIImage) { + CIImage *coreImage = image.CIImage; + CIContext *coreImageContext = [CIContext context]; + CGImageRef coreGraphicsImage = + [coreImageContext createCGImage:coreImage fromRect:coreImage.extent]; + if (coreGraphicsImage) { + CGContextDrawImage(context, rect, coreGraphicsImage); + CFRelease(coreGraphicsImage); + coreGraphicsImage = NULL; + } + } else { + NSCAssert(NO, @"Unable to flip image without a CGImage or CIImage backing store"); + } + + UIImage *drawnImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + // Port the rendering mode. + UIImage *flippedImage = [drawnImage imageWithRenderingMode:image.renderingMode]; + return flippedImage; +} + +@implementation UIImage (MaterialRTL) + +- (UIImage *)mdf_imageWithHorizontallyFlippedOrientation { + // On iOS 10 and above, UIImage supports the imageWithHorizontallyFlippedOrientation method. + // Otherwise, we manually manipulate the image. + if ([self respondsToSelector:@selector(imageWithHorizontallyFlippedOrientation)]) { + //TODO: (#22) Replace with @availability when we adopt Xcode 9 as our minimum supported version. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + return [self imageWithHorizontallyFlippedOrientation]; +#pragma clang diagnostic pop + } else { + UIImage *mirroredImage; + UIImageOrientation mirroredOrientation = MDFRTLMirroredOrientation(self.imageOrientation); + if (self.CGImage) { + CGImageRef _Nonnull image = (CGImageRef _Nonnull)self.CGImage; + mirroredImage = [[self class] imageWithCGImage:image + scale:self.scale + orientation:mirroredOrientation]; + } else if (self.CIImage) { + CIImage * _Nonnull image = (CIImage * _Nonnull)self.CIImage; + mirroredImage = [[self class] imageWithCIImage:image + scale:self.scale + orientation:mirroredOrientation]; + } + + // If we were unsuccessful, manually flip the image using a Core Graphics context + if (!mirroredImage) { + mirroredImage = MDFRTLFlippedImage(self); + } + + // On iOS9- [UIImage imageWithCGImage:scale:orientation:] loses the rendering mode. + // Restore it if the new renderingMode does not match the current renderingMode. + if (mirroredImage.renderingMode != self.renderingMode) { + mirroredImage = [mirroredImage imageWithRenderingMode:self.renderingMode]; + } + + return mirroredImage; + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h new file mode 100644 index 00000000..fbf11555 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h @@ -0,0 +1,91 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +/** + Complete backporting of iOS 9's `-[UIView semanticContentAttribute]` and + `+[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:]`, and iOS 10's + `-[UIView effectiveUserInterfaceLayoutDirection]` and + `+[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:relativeToLayoutDirection:]`. + */ + + + +@interface UIView (MaterialRTL) + +// UISemanticContentAttribute was added in iOS SDK 9.0 but is available on devices running earlier +// version of iOS. We ignore the partial-availability warning that gets thrown on our use of this +// symbol. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + +/** + A semantic description of the view's contents, used to determine whether the view should be flipped + when switching between left-to-right and right-to-left layouts. + + @note Default: + - iOS 8 and below: UISemanticContentAttributeUnspecified. + - iOS 9 and above: same as -[UIView semanticContentAttribute] + */ +@property(nonatomic, setter=mdf_setSemanticContentAttribute:) + UISemanticContentAttribute mdf_semanticContentAttribute; + +/** + The user interface layout direction appropriate for arranging the immediate content of this view. + + Always consult the mdf_effectiveUserInterfaceLayoutDirection of the view whose immediate content is + being arranged or drawn. Do not assume that the value propagates through the view's subtree. + + @note + - iOS 9 and below: same as +[UIView mdf_userInterfaceLayoutDirectionForSemanticContentAttribute:] + - iOS 10 and above: same as -[UIView effectiveUserInterfaceLayoutDirection] + */ +@property(nonatomic, readonly) + UIUserInterfaceLayoutDirection mdf_effectiveUserInterfaceLayoutDirection; + +/** + Returns the layout direction implied by the provided semantic content attribute relative to the + application-wide layout direction (as returned by + UIApplication.sharedApplication.userInterfaceLayoutDirection). However, if it's being called from + an iOS 8 extension, it will return left-to-right every time. + + @param semanticContentAttribute The semantic content attribute. + @return The layout direction. + */ ++ (UIUserInterfaceLayoutDirection)mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: + (UISemanticContentAttribute)semanticContentAttribute; + +/** + Returns the layout direction implied by the provided semantic content attribute relative to the + provided layout direction. For example, when provided a layout direction of + RightToLeft and a semantic content attribute of Playback, this method returns LeftToRight. Layout + and drawing code can use this method to determine how to arrange elements, but might find it easier + to query the container view's mdf_effectiveUserInterfaceLayoutDirection property instead. + + @param semanticContentAttribute The semantic content attribute. + @param layoutDirection The layout direction to consider. + @return The implied layout direction. + */ ++ (UIUserInterfaceLayoutDirection) + mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: + (UISemanticContentAttribute)semanticContentAttribute + relativeToLayoutDirection: + (UIUserInterfaceLayoutDirection)layoutDirection; + +#pragma clang diagnostic pop + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m new file mode 100644 index 00000000..95126f78 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m @@ -0,0 +1,167 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "UIView+MaterialRTL.h" + +#import + +#define MDF_BASE_SDK_EQUAL_OR_ABOVE(x) \ + (defined(__IPHONE_##x) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_##x)) + +// UISemanticContentAttribute was added in iOS SDK 9.0 but is available on devices running earlier +// version of iOS. We ignore the partial-availability warning that gets thrown on our use of this +// symbol. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + +static inline UIUserInterfaceLayoutDirection + MDFUserInterfaceLayoutDirectionForSemanticContentAttributeRelativeToLayoutDirection( + UISemanticContentAttribute semanticContentAttribute, + UIUserInterfaceLayoutDirection userInterfaceLayoutDirection) { + switch (semanticContentAttribute) { + case UISemanticContentAttributeUnspecified: + return userInterfaceLayoutDirection; + case UISemanticContentAttributePlayback: + case UISemanticContentAttributeSpatial: + case UISemanticContentAttributeForceLeftToRight: + return UIUserInterfaceLayoutDirectionLeftToRight; + case UISemanticContentAttributeForceRightToLeft: + return UIUserInterfaceLayoutDirectionRightToLeft; + } + NSCAssert(NO, @"Invalid enumeration value %i.", (int)semanticContentAttribute); + return userInterfaceLayoutDirection; +} + +@interface UIView (MaterialRTLPrivate) + +// On iOS 9 and above, mdf_semanticContentAttribute is backed by UIKit's semanticContentAttribute. +// On iOS 8 and below, mdf_semanticContentAttribute is backed by an associated object. +@property(nonatomic, setter=mdf_setAssociatedSemanticContentAttribute:) + UISemanticContentAttribute mdf_associatedSemanticContentAttribute; + +@end + +@implementation UIView (MaterialRTL) + +- (UISemanticContentAttribute)mdf_semanticContentAttribute { +#if MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) + if ([self respondsToSelector:@selector(semanticContentAttribute)]) { + return self.semanticContentAttribute; + } else +#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) + { + return self.mdf_associatedSemanticContentAttribute; + } +} + +- (void)mdf_setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute { +#if MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) + if ([self respondsToSelector:@selector(semanticContentAttribute)]) { + self.semanticContentAttribute = semanticContentAttribute; + } else +#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) + { + self.mdf_associatedSemanticContentAttribute = semanticContentAttribute; + } + + // Invalidate the layout. + [self setNeedsLayout]; +} + +- (UIUserInterfaceLayoutDirection)mdf_effectiveUserInterfaceLayoutDirection { +#if MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) + if ([self respondsToSelector:@selector(effectiveUserInterfaceLayoutDirection)]) { + return self.effectiveUserInterfaceLayoutDirection; + } else { + return [UIView mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: + self.mdf_semanticContentAttribute]; + } +#else + return [UIView mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: + self.mdf_semanticContentAttribute]; +#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) +} + ++ (UIUserInterfaceLayoutDirection)mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: + (UISemanticContentAttribute)attribute { +#if MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) + if ([self + respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute:)]) { + return [self userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; + } else +#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) + { + // If we are running in the context of an app, we query [UIApplication sharedApplication]. + // Otherwise use a default of Left-to-Right. + UIUserInterfaceLayoutDirection applicationLayoutDirection = + UIUserInterfaceLayoutDirectionLeftToRight; + NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; + // Can I use kAppBundleIdentifier ? + if ([bundlePath hasSuffix:@".app"]) { + // We can't call sharedApplication directly or an error gets thrown for app extensions. + UIApplication *application = + [[UIApplication class] performSelector:@selector(sharedApplication)]; + applicationLayoutDirection = application.userInterfaceLayoutDirection; + } + return [self + mdf_userInterfaceLayoutDirectionForSemanticContentAttribute:attribute + relativeToLayoutDirection:applicationLayoutDirection]; + } +} + ++ (UIUserInterfaceLayoutDirection) + mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: + (UISemanticContentAttribute)semanticContentAttribute + relativeToLayoutDirection: + (UIUserInterfaceLayoutDirection)layoutDirection { +#if MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) + if ([self + respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute: + relativeToLayoutDirection:)]) { + return [self userInterfaceLayoutDirectionForSemanticContentAttribute:semanticContentAttribute + relativeToLayoutDirection:layoutDirection]; + } else { + return MDFUserInterfaceLayoutDirectionForSemanticContentAttributeRelativeToLayoutDirection( + semanticContentAttribute, layoutDirection); + } +#else + return MDFUserInterfaceLayoutDirectionForSemanticContentAttributeRelativeToLayoutDirection( + semanticContentAttribute, layoutDirection); +#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) +} + +@end + +@implementation UIView (MaterialRTLPrivate) + +- (UISemanticContentAttribute)mdf_associatedSemanticContentAttribute { + NSNumber *semanticContentAttributeNumber = + objc_getAssociatedObject(self, @selector(mdf_semanticContentAttribute)); + if (semanticContentAttributeNumber != nil) { + return [semanticContentAttributeNumber integerValue]; + } + return UISemanticContentAttributeUnspecified; +} + +- (void)mdf_setAssociatedSemanticContentAttribute: + (UISemanticContentAttribute)semanticContentAttribute { + objc_setAssociatedObject(self, @selector(mdf_semanticContentAttribute), + @(semanticContentAttribute), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma clang diagnostic pop diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md new file mode 100644 index 00000000..92102e79 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md @@ -0,0 +1,101 @@ +MDFTextAccessibility assists in selecting text colors that will meet the +[W3C standards for accessibility](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html). + +[![Build Status](https://travis-ci.org/material-foundation/material-text-accessibility-ios.svg?branch=develop)](https://travis-ci.org/material-foundation/material-text-accessibility-ios) +[![Code Coverage](http://codecov.io/github/material-foundation/material-text-accessibility-ios/coverage.svg?branch=develop)](http://codecov.io/github/material-foundation/material-text-accessibility-ios?branch=develop) + +## Accessibility of text + +Apps created for the widest range of users should ensure that users can read +text presented over any background. While the legibility of text depends on many +things, a great start is to provide a sufficiently large *contrast ratio* +between foreground text colors and their background colors. The contrast ratio +of two colors is a measurement of how much the brightness of two colors differ. +For example, white on dark grey might have a contrast ratio of 9:1, while white +on medium grey might only have a contrast ratio of 4:1. In general, larger +contrast ratios are better and will ensure the widest range of users can easily +read the text in your app. + +The [W3C](https://www.w3.org)'s +[Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG/#visual-audio-contrast) +contains two recommendations for text contrast ratios: + +1. Minimum contrast: text should have a contrast ratio of at least 4.5:1, + except for "large" text, which can have a contrast ratio of 3:1. +2. Enhanced contrast: text should have a contrast ratio of at least 7:1, except + for large text, which can have a contrast ratio of 4.5:1. + +"Large" text is nominally defined as at least 18pt in a normal font face or at +least 14pt in a bold font face. For more information (including some important +exceptions), see the +[Guidelines](https://www.w3.org/TR/WCAG/#visual-audio-contrast). + +## Computing contrast ratios + +Computing acceptable contrast ratios involves the foreground color, the +background color, the text size, and the transparency of the foreground color, +if any. MDFTextAccessibility provides convenient access to colors that will +automatically give acceptable contrast ratios. + +For methods that return a UIColor, the color along with its alpha is guaranteed +to provide a contrast ratio meeting the minimum ratios recommended by the W3C. +The returned alpha may be greater than the requested alpha to ensure acceptable +contrast ratios, that is, the returned color may be more opaque than requested +to ensure that the text remains legible. + +## Legible text on images + +Displaying text legibly on arbitrary images is difficult because the image +content can conflict with the text. Images with smooth gradients or blurred +regions are likely to result in more legible text; images with many details and +high contrast are less likely to result in legible text. + +MDFTextAccessibility provides methods that attempt to select a good color for +displaying text on a particular image, but the quality of the results will +depend on the contents of the image. If the content of the image is not known +(for example, when images provided by the user), then consider using a +semi-transparent shim between the image and the text to increase contrast. + +## Usage + +### Basic usage + +The simplest usage is to select between black and white text depending on the +background color, irrespective of the font: + +```objective-c +label.textColor = [MDFTextAccessibility textColorOnBackgroundColor:label.backgroundColor + textAlpha:1 + font:nil]; +``` + +Many design standards for user interfaces use text colors that are not fully +opaque. However, transparent text can reduce legibility, so you can request a +color that is as close as possible to a particular alpha value while still being +legible: + +```objective-c +label.textColor = [MDFTextAccessibility textColorOnBackgroundColor:label.backgroundColor + targetTextAlpha:0.85 + font:nil]; +``` + +Since the W3C recommends different contrast ratios for "normal" and "large" +text, including the font can result in a text color closer to your target alpha +when appropriate: + +```objective-c +label.textColor = [MDFTextAccessibility textColorOnBackgroundColor:label.backgroundColor + textAlpha:0.85 + font:label.font]; +``` + +### Advanced usage + +For more advanced usage, such as selecting from a set of colors other than white +and black, see MDFTextAccessibility's +`textColorFromChoices:onBackgroundColor:options:`. + +## License + +MDFTextAccessiblity is licensed under the [Apache License Version 2.0](LICENSE). diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h new file mode 100644 index 00000000..ea88e561 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h @@ -0,0 +1,17 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDFTextAccessibility.h" diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h new file mode 100644 index 00000000..34a13d1b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h @@ -0,0 +1,199 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +/** Options for selecting text colors that provide acceptable contrast ratios. */ +typedef NS_OPTIONS(NSUInteger, MDFTextAccessibilityOptions) { + /** No options. */ + MDFTextAccessibilityOptionsNone = 0, + + /** Font size is at least 14pt bold or 18pt normal. */ + MDFTextAccessibilityOptionsLargeFont = 1 << 0, + + /** Do not modify alpha values to find good colors. */ + MDFTextAccessibilityOptionsPreserveAlpha = 1 << 1, + + /** Prefer darker colors to lighter colors. */ + MDFTextAccessibilityOptionsPreferDarker = 1 << 2, + + /** Prefer lighter colors to darker colors. */ + MDFTextAccessibilityOptionsPreferLighter = 1 << 3, + + /** Use enhanced contrast ratios (level AAA) instead of minimum ratios (level AA). */ + MDFTextAccessibilityOptionsEnhancedContrast = 1 << 4, +}; + +/** + MDFTextAccessiblity provides methods to compute accessible text colors. + */ +@interface MDFTextAccessibility : NSObject + +/** + An optionally transparent text color suitable for displaying on a background color with a + particular font. + + The color returned will be white or black with an alpha value of targetTextAlpha, unless the + contrast ratio is insufficient, in which case the alpha is increased (made more opaque). + + If the passed font is nil, then a conservative guess is used. + + @param backgroundColor The opaque background color the text will be displayed on. + @param targetTextAlpha The target alpha of the text. + @param font The font the text will use or nil. + @return A color with acceptable contrast ratio for displaying text on |color|. + */ ++ (nonnull UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor + targetTextAlpha:(CGFloat)targetTextAlpha + font:(nullable UIFont *)font; + +/** + An optionally transparent text color suitable for displaying on a background image with a + particular font. + + The color returned will be white or black with an alpha value of targetTextAlpha, unless the + contrast ratio is insufficient, in which case the alpha is increased (made more opaque). + + If the passed font is nil, then a conservative guess is used. + + The content of the background image is simply averaged to make an average color, which is then used + as if it were the background color of the text. Depending on the contents of the image, this + approximation may or may not result in legible text. + + The supplied image region will be intersected with the image's bounds. If the resulting region is + null or empty then this method returns nil. + + @param backgroundImage The opaque background image the text will be displayed on. + @param region The region in which the text will be displayed. Can be conservatively large. + @param targetTextAlpha The target alpha of the text. + @param font The font to be used to display the text. Can be nil. + @return A color with acceptable contrast ratio, or nil if the region is out of bounds of the image. + */ ++ (nullable UIColor *)textColorOnBackgroundImage:(nonnull UIImage *)backgroundImage + inRegion:(CGRect)region + targetTextAlpha:(CGFloat)targetTextAlpha + font:(nullable UIFont *)font; + +#pragma mark Advanced methods + +/** + An optionally transparent text color suitable for displaying text on a given opaque background + color. + + This method calls textColorFromChoices:onColor:options: with the choices [white, black], both + with their alpha set to targetTextAlpha. + + If MDFTextAccessibilityOptionsPreserveAlpha is included in the options, then the algorithm will not + modify the alpha values, which may result in no color being returned at all. + + @param backgroundColor The opaque background color the text will be displayed on. + @param targetTextAlpha The target alpha of the text. + @param options A combination of MDFTextAccessibilityOptions values. + @return A color with acceptable contrast ratio or nil if no such color exists. + */ ++ (nullable UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor + targetTextAlpha:(CGFloat)targetTextAlpha + options:(MDFTextAccessibilityOptions)options; + +/** + A color selected from a set which is suitable for displaying text on a given opaque background + color. + + Since the minimum ratio for "large" text is less stringent, set + the MDFTextAccessibilityOptionsLargeFont bit if the font size will be greater than or + equal to 18pt normal or 14pt bold. If in doubt or if the text size can vary, be conservative and do + not specify MDFTextAccessibilityOptionsLargeFont in the options. + + By default, the first acceptable color in |choices| will be returned. If + MDFTextAccessibilityOptionsPreferLighter is set, then the lightest acceptable color will be + returned, and if MDFTextAccessibilityOptionsPreferDarker is set, then the darkest acceptable color + will be returned. This allows for a standard set of text colors to be used in different situations. + + By default, the algorithm will attempt to modify the alpha value of colors in |choices| instead + of switching to an alternate color, under the assumption that text with slightly different + alpha values is less noticible than, for example, black text where white text is usually used. + If MDFTextAccessibilityOptionsPreserveAlpha is included in the options, then the algorithm will not + modify the alpha values, which may result in no color being returned at all. + + @param choices An array of text color UIColor objects with optional alpha values. + @param backgroundColor The opaque background color the text will be displayed on. + @param options A combination of MDFTextAccessibilityOptions values. + @return A color with acceptable contrast ratio or nil if no such color exists. + */ ++ (nullable UIColor *)textColorFromChoices:(nonnull NSArray *)choices + onBackgroundColor:(nonnull UIColor *)backgroundColor + options:(MDFTextAccessibilityOptions)options; + +/** + The minimum alpha that text can have and still have an acceptable contrast ratio. Depending on + color combinations, the minimum useable alpha can vary. + + Since the minimum ratio for "large" text is less stringent, set + the MDFTextAccessibilityOptionsLargeFont bit if the font size will be greater than or + equal to 18pt normal or 14pt bold. If in doubt or if the text size can vary, be conservative and do + not specify MDFTextAccessibilityOptionsLargeFont in the options. + + @note There are some color combinations (white on white) for which an acceptable alpha value + doesn't exist. + + @param textColor The text color (alpha is ignored). + @param backgroundColor The opaque background color the text will be displayed on. + @param options A combination of MDFTextAccessibilityOptions values. + @return The minimum alpha value to use in this situation, or -1 if there is no such alpha. + */ ++ (CGFloat)minAlphaOfTextColor:(nonnull UIColor *)textColor + onBackgroundColor:(nonnull UIColor *)backgroundColor + options:(MDFTextAccessibilityOptions)options; + +/** + The contrast ratio of a text color when displayed on an opaque background color. + + @param textColor A text color with optional transparency. + @param backgroundColor The opaque background color the text will be displayed on. + @return The contrast ratio of the text color on the background color. + */ ++ (CGFloat)contrastRatioForTextColor:(nonnull UIColor *)textColor + onBackgroundColor:(nonnull UIColor *)backgroundColor; + +/** + Whether a text color passes accessibility standards when displayed on an opaque background color. + + MDFTextAccessibilityOptionsLargeFont and MDFTextAccessibilityOptionsEnhancedContrast are relevant + options for this method. + + @param textColor A text color with optional transparency. + @param backgroundColor The opaque background color the text will be displayed on. + @param options A combination of MDFTextAccessibilityOptions values. + @return YES if the text color would meet accessibility standards. + */ ++ (BOOL)textColor:(nonnull UIColor *)textColor + passesOnBackgroundColor:(nonnull UIColor *)backgroundColor + options:(MDFTextAccessibilityOptions)options; + +/** + Whether a particular font would be considered "large" for the purposes of calculating + contrast ratios. + + Large fonts are defined as greater than 18pt normal or 14pt bold. If the passed font is nil, then + this method returns NO. + For more see: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html + + @param font The font to examine, or nil. + @return YES if the font is non-nil and is considered "large". + */ ++ (BOOL)isLargeForContrastRatios:(nullable UIFont *)font; + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m new file mode 100644 index 00000000..38cf89b4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m @@ -0,0 +1,188 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDFTextAccessibility.h" + +#import "MDFColorCalculations.h" +#import "MDFImageCalculations.h" +#import "NSArray+MDFUtils.h" + +static const CGFloat kMinContrastRatioNormalText = 4.5f; +static const CGFloat kMinContrastRatioLargeText = 3.0f; +static const CGFloat kMinContrastRatioNormalTextEnhanced = 7.0f; +static const CGFloat kMinContrastRatioLargeTextEnhanced = 4.5f; + +@implementation MDFTextAccessibility + ++ (nonnull UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor + targetTextAlpha:(CGFloat)targetTextAlpha + font:(nullable UIFont *)font { + MDFTextAccessibilityOptions options = 0; + if ([self isLargeForContrastRatios:font]) { + options |= MDFTextAccessibilityOptionsLargeFont; + } + return [self textColorOnBackgroundColor:backgroundColor + targetTextAlpha:targetTextAlpha + options:options]; +} + ++ (nullable UIColor *)textColorOnBackgroundImage:(nonnull UIImage *)backgroundImage + inRegion:(CGRect)region + targetTextAlpha:(CGFloat)targetTextAlpha + font:(nullable UIFont *)font { + UIColor *backgroundColor = MDFAverageColorOfOpaqueImage(backgroundImage, region); + if (!backgroundColor) { + return nil; + } + + return [self textColorOnBackgroundColor:backgroundColor + targetTextAlpha:targetTextAlpha + font:font]; +} + ++ (nullable UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor + targetTextAlpha:(CGFloat)targetTextAlpha + options:(MDFTextAccessibilityOptions)options { + NSArray *colors = @[ + [UIColor colorWithWhite:1 alpha:targetTextAlpha], [UIColor colorWithWhite:0 + alpha:targetTextAlpha] + ]; + UIColor *textColor = [self textColorFromChoices:colors + onBackgroundColor:backgroundColor + options:options]; + return textColor; +} + ++ (nullable UIColor *)textColorFromChoices:(nonnull NSArray *)choices + onBackgroundColor:(nonnull UIColor *)backgroundColor + options:(MDFTextAccessibilityOptions)options { + [choices enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + NSAssert([obj isKindOfClass:[UIColor class]], @"Choices must be UIColors."); + }]; + + // Sort by luminance if requested. + if ((options & MDFTextAccessibilityOptionsPreferLighter) || + (options & MDFTextAccessibilityOptionsPreferDarker)) { + NSArray *luminances = [choices mdf_arrayByMappingObjects:^id(id object) { + return @([self luminanceOfColor:object]); + }]; + + BOOL inverse = (options & MDFTextAccessibilityOptionsPreferDarker) ? YES : NO; + choices = [luminances mdf_sortArray:choices + usingComparator:^NSComparisonResult(id obj1, id obj2) { + float first = inverse ? [obj1 floatValue] : [obj2 floatValue]; + float second = inverse ? [obj2 floatValue] : [obj1 floatValue]; + + if (first < second) { + return NSOrderedAscending; + } else if (first > second) { + return NSOrderedDescending; + } else { + return NSOrderedSame; + } + }]; + } + + // Search the array for a color that can be used, adjusting alpha values upwards if requested. + // The first acceptable color (adjusted or not) is returned. + BOOL adjustAlphas = (options & MDFTextAccessibilityOptionsPreserveAlpha) ? NO : YES; + for (UIColor *choice in choices) { + if ([self textColor:choice passesOnBackgroundColor:backgroundColor options:options]) { + return choice; + } + + if (!adjustAlphas) { + continue; + } + + CGFloat alpha = CGColorGetAlpha(choice.CGColor); + CGFloat minAlpha = [self minAlphaOfTextColor:choice + onBackgroundColor:backgroundColor + options:options]; + if (minAlpha > 0) { + if (alpha > minAlpha) { + NSAssert(NO, + @"Logic error: computed an acceptable minimum alpha (%f) that is *less* than the " + @"unacceptable current alpha (%f).", + minAlpha, alpha); + continue; + } + return [choice colorWithAlphaComponent:minAlpha]; + } + } + + return nil; +} + ++ (CGFloat)minAlphaOfTextColor:(nonnull UIColor *)textColor + onBackgroundColor:(nonnull UIColor *)backgroundColor + options:(MDFTextAccessibilityOptions)options { + CGFloat minContrastRatio = [self minContrastRatioForOptions:options]; + return MDFMinAlphaOfColorOnBackgroundColor(textColor, backgroundColor, minContrastRatio); +} + ++ (CGFloat)contrastRatioForTextColor:(UIColor *)textColor + onBackgroundColor:(UIColor *)backgroundColor { + CGFloat colorComponents[4]; + CGFloat backgroundColorComponents[4]; + MDFCopyRGBAComponents(textColor.CGColor, colorComponents); + MDFCopyRGBAComponents(backgroundColor.CGColor, backgroundColorComponents); + + NSAssert(backgroundColorComponents[3] == 1, + @"Background color %@ must be opaque for a valid contrast ratio calculation.", + backgroundColor); + backgroundColorComponents[3] = 1; + + return MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); +} + ++ (BOOL)textColor:(nonnull UIColor *)textColor + passesOnBackgroundColor:(nonnull UIColor *)backgroundColor + options:(MDFTextAccessibilityOptions)options { + CGFloat minContrastRatio = [self minContrastRatioForOptions:options]; + CGFloat ratio = [self contrastRatioForTextColor:textColor onBackgroundColor:backgroundColor]; + return ratio >= minContrastRatio ? YES : NO; +} + ++ (BOOL)isLargeForContrastRatios:(nullable UIFont *)font { + UIFontDescriptor *fontDescriptor = font.fontDescriptor; + BOOL isBold = + (fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) == UIFontDescriptorTraitBold; + return font.pointSize >= 18 || (isBold && font.pointSize >= 14); +} + +#pragma mark - Private methods + ++ (CGFloat)luminanceOfColor:(UIColor *)color { + CGFloat colorComponents[4]; + MDFCopyRGBAComponents(color.CGColor, colorComponents); + return MDFRelativeLuminanceOfRGBComponents(colorComponents); +} + ++ (CGFloat)minContrastRatioForOptions:(MDFTextAccessibilityOptions)options { + BOOL isLarge = + (options & MDFTextAccessibilityOptionsLargeFont) == MDFTextAccessibilityOptionsLargeFont; + BOOL isEnhanced = (options & MDFTextAccessibilityOptionsEnhancedContrast) == + MDFTextAccessibilityOptionsEnhancedContrast; + + if (isEnhanced) { + return isLarge ? kMinContrastRatioLargeTextEnhanced : kMinContrastRatioNormalTextEnhanced; + } else { + return isLarge ? kMinContrastRatioLargeText : kMinContrastRatioNormalText; + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h new file mode 100644 index 00000000..3e2f78ff --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h @@ -0,0 +1,70 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + Copies the RGBA components of color, expanding greyscale colors out to RGBA if necessary. + + @param color The color to extract the components from. + @param rgbaComponents A pointer to four CGFloat values. + */ +void MDFCopyRGBAComponents(CGColorRef color, CGFloat *rgbaComponents); + +/** + Returns the contrast ratio of a foreground color blended on top of a background color. + + @param foregroundColorComponents A pointer to four RGBA CGFloats of the foreground color. + @param backgroundColorComponents A pointer to four RGBA CGFloats of the background color. + @return The contrast ratio of the two colors after blending. + */ +CGFloat MDFContrastRatioOfRGBAComponents(const CGFloat *foregroundColorComponents, + const CGFloat *backgroundColorComponents); + +/** + Calculates the relative luminance of a sRGB color from its components (ignores alpha). + @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + + @param components A pointer to three RGB CGFloat values of the color. + @return The relative luminance of the color. + */ +CGFloat MDFRelativeLuminanceOfRGBComponents(const CGFloat *components); + +/** + Calculates the minimum alpha that a foreground color can have such that, when it's blended on top + of an opaque background color, the resulting contrast ratio is higher than a minimum contrast + ratio. + + If there is no such acceptable contrast ratio, then -1 is returned. This is the case when even a + completely opaque foreground color can't produce a high enough contrast ratio. For example, light + grey on white will have a low contrast ratio for any alpha value assigned to the light grey. + + @param color The foreground color (alpha ignored). + @param backgroundColor The background color (assumed to be opaque). + @param minContrastRatio The minimum allowable contrast ratio. + @return The minimum acceptable alpha of the foreground color, or -1 if no such alpha exists. + */ +CGFloat MDFMinAlphaOfColorOnBackgroundColor(UIColor *color, + UIColor *backgroundColor, + CGFloat minContrastRatio); + +#if defined __cplusplus +} // extern "C" +#endif diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m new file mode 100644 index 00000000..4e2699e9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m @@ -0,0 +1,184 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDFColorCalculations.h" + +/** + The number of iterations required to find the minimum acceptable alpha is + ceil(log2(1/kMinAlphaSearchPrecision)), or 7 for a precision of 0.01. If you adjust the precision + then also adjust the max iterations, which is used as a safety check. + */ +static const CGFloat kMinAlphaSearchPrecision = 0.01f; +static const NSUInteger kMinAlphaSearchMaxIterations = 10; + +/** Returns value raised to exponent. */ +static inline CGFloat MDFPow(CGFloat value, CGFloat exponent) { +#if CGFLOAT_IS_DOUBLE + return pow(value, exponent); +#else + return powf(value, exponent); +#endif +} + +/** + Calculate a linear RGB component from a sRGB component for calculating luminance. + @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + */ +static inline CGFloat LinearRGBComponent(CGFloat component) { + if (component <= 0.03928f) { + return component / 12.92f; + } else { + return MDFPow(((component + 0.055f) / 1.055f), 2.4f); + } +} + +/** + Blend a foreground color with alpha on a opaque background color. + @note The background color must be opaque for this to be valid. + @see http://en.wikipedia.org/wiki/Alpha_compositing + */ +static inline void BlendColorOnOpaqueBackground(const CGFloat *foregroundColor, + const CGFloat *backgroundColor, + CGFloat *blendedColor) { + for (int i = 0; i < 3; ++i) { + blendedColor[i] = + foregroundColor[i] * foregroundColor[3] + backgroundColor[i] * (1 - foregroundColor[3]); + } + blendedColor[3] = 1.0f; +} + +static inline CGFloat ContrastRatioOfRGBComponents(const CGFloat *firstComponents, + const CGFloat *secondComponents) { + CGFloat firstLuminance = MDFRelativeLuminanceOfRGBComponents(firstComponents); + CGFloat secondLuminance = MDFRelativeLuminanceOfRGBComponents(secondComponents); + + if (secondLuminance > firstLuminance) { + CGFloat temp = firstLuminance; + firstLuminance = secondLuminance; + secondLuminance = temp; + } + + return (firstLuminance + 0.05f) / (secondLuminance + 0.05f); +} + +CGFloat MDFContrastRatioOfRGBAComponents(const CGFloat *foregroundColorComponents, + const CGFloat *backgroundColorComponents) { + CGFloat blendedColor[4]; + BlendColorOnOpaqueBackground(foregroundColorComponents, backgroundColorComponents, blendedColor); + return ContrastRatioOfRGBComponents(blendedColor, backgroundColorComponents); +} + +void MDFCopyRGBAComponents(CGColorRef color, CGFloat *rgbaComponents) { + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(CGColorGetColorSpace(color)); + if (!(colorSpaceModel == kCGColorSpaceModelRGB || + colorSpaceModel == kCGColorSpaceModelMonochrome)) { + NSCAssert(NO, @"Can't compute luminance for non-RGB color space model %i.", colorSpaceModel); + for (int i = 0; i < 4; ++i) { + rgbaComponents[i] = 0; + } + return; + } + + size_t numComponents = CGColorGetNumberOfComponents(color); + const CGFloat *components = CGColorGetComponents(color); + switch (numComponents) { + // Greyscale + alpha + case 2: + for (int i = 0; i < 3; ++i) { + rgbaComponents[i] = components[0]; + } + rgbaComponents[3] = components[1]; + break; + + // RGB + alpha + case 4: + for (int i = 0; i < 4; ++i) { + rgbaComponents[i] = components[i]; + } + break; + + default: + NSCAssert(NO, @"Unexpected number of color components: %zu.", numComponents); + } +} + +CGFloat MDFRelativeLuminanceOfRGBComponents(const CGFloat *components) { + CGFloat linearRGB[3]; + for (int i = 0; i < 3; ++i) { + linearRGB[i] = LinearRGBComponent(components[i]); + } + return 0.2126f * linearRGB[0] + 0.7152f * linearRGB[1] + 0.0722f * linearRGB[2]; +} + +CGFloat MDFMinAlphaOfColorOnBackgroundColor(UIColor *color, UIColor *backgroundColor, + CGFloat minContrastRatio) { + CGFloat colorComponents[4]; + CGFloat backgroundColorComponents[4]; + MDFCopyRGBAComponents(color.CGColor, colorComponents); + MDFCopyRGBAComponents(backgroundColor.CGColor, backgroundColorComponents); + + NSCAssert(backgroundColorComponents[3] == 1, + @"Background color %@ must be opaque for a valid contrast ratio calculation.", + backgroundColor); + backgroundColorComponents[3] = 1; + + NSCAssert(minContrastRatio > 0, @"Invalid min contrast ratio %g.", (double)minContrastRatio); + CGFloat minAlpha = 0; + CGFloat maxAlpha = 1; + +#if DEBUG && !defined(NS_BLOCK_ASSERTIONS) + colorComponents[3] = minAlpha; + CGFloat minAlphaRatio = + MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); + NSCAssert(minAlphaRatio < minContrastRatio, @"Transparent color cannot be a valid color."); +#endif // !defined(NS_BLOCK_ASSERTIONS) + + // maxAlphaRatio is the best contrast ratio we can acheive by modifying the alpha of this color. + // If it's not good enough, then return now and inform the caller. + colorComponents[3] = maxAlpha; + CGFloat maxAlphaRatio = + MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); + if (maxAlphaRatio < minContrastRatio) { + return -1; + } + + // Classic binary search of a range. + NSUInteger numIterations = 0; + while (numIterations <= kMinAlphaSearchMaxIterations && + (maxAlpha - minAlpha) > kMinAlphaSearchPrecision) { + CGFloat testAlpha = (minAlpha + maxAlpha) / 2; + colorComponents[3] = testAlpha; + CGFloat testRatio = + MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); + + if (testRatio < minContrastRatio) { + minAlpha = testAlpha; + } else { + maxAlpha = testAlpha; + } + + ++numIterations; + } + + if (numIterations > kMinAlphaSearchMaxIterations) { + NSCAssert(NO, @"Too many iterations (%i) while finding min alpha of text color %@ on %@.", + (int)numIterations, color, backgroundColor); + return -1; + } + + // Conservatively return the max of the range of possible alphas, which is known to pass. + return maxAlpha; +} diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h new file mode 100644 index 00000000..7cdb664b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h @@ -0,0 +1,37 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + Return the average color of an image in a particular region. + + The region will be intersected with the image's bounds. If the resulting region is empty (or the + input region was null) then this function returns nil. + + @param image The image to examine. + @param region The region of the image to average, or CGRectInfinite for the entire image. + @return The average color, or nil if the region was invalid. + */ +UIColor *MDFAverageColorOfOpaqueImage(UIImage *image, CGRect region); + +#if defined __cplusplus +} // extern "C" +#endif diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m new file mode 100644 index 00000000..d5546148 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m @@ -0,0 +1,54 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDFImageCalculations.h" + +UIColor *MDFAverageColorOfOpaqueImage(UIImage *image, CGRect region) { + CGImageRef imageRef = image.CGImage; + CGImageRef cropped = CGImageCreateWithImageInRect(imageRef, region); + + // Empty/null regions will cause cropped to be nil. + if (!cropped) { + return nil; + } + + UIGraphicsBeginImageContext(CGSizeMake(1, 1)); + + uint8_t argb[4]; + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = + CGBitmapContextCreate(argb, // data + 1, // width + 1, // height + 8, // Bits per component + 4, // Bytes per row + colorspace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorspace); + CGContextSetInterpolationQuality(context, kCGInterpolationMedium); + CGContextSetBlendMode(context, kCGBlendModeCopy); + CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), cropped); + CGContextRelease(context); + CGImageRelease(cropped); + + CGFloat alpha = argb[0] / (CGFloat)255; + CGFloat scale = alpha > 0 ? 1 / (alpha * 255) : 0; + UIColor *color = [UIColor colorWithRed:scale * argb[1] + green:scale * argb[2] + blue:scale * argb[3] + alpha:alpha]; + UIGraphicsEndImageContext(); + return color; +} diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h new file mode 100644 index 00000000..57784b3b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h @@ -0,0 +1,53 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +typedef id (^MDFMappingFunction)(id object); + +/** Functional extensions to NSArray. */ +@interface NSArray (MDFUtils) + +/** + * Returns an array consisting of applying |function| to each element of self in order. + * + * @param function A block mapping an input element to an output element. + * @return An array of the same size as self containing elements mapped through the function. + */ +- (NSArray *)mdf_arrayByMappingObjects:(MDFMappingFunction)function; + +/** + * Returns a sorted version of |array| by using the passed comparator on self. + * + * @note The comparator acts of elements of self, @em not |array|. + * + * Example: + * @code + * NSArray *weights = @[ 100, 200, 50 ]; + * NSArray *dogs = @[ @"Bruno", @"Tiger", @"Spot" ]; + * NSComparator *ascending = ... NSString comparator ... + * NSArray *sortedDogs = [weights mdf_sortArray:dogs + * usingComparator:ascending]; + * // sortedDogs is @[ @"Spot", @"Bruno", @"Tiger" ]. + * @endcode + * + * @param array The array to sort. + * @param comparator A comparator acting on elements of self. + * @return A sorted copy of |array|. + */ +- (NSArray *)mdf_sortArray:(NSArray *)array usingComparator:(NSComparator)comparator; + +@end diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m new file mode 100644 index 00000000..9758611a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m @@ -0,0 +1,68 @@ +/* + Copyright 2016-present Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "NSArray+MDFUtils.h" + +@implementation NSArray (MDFUtils) + +- (NSArray *)mdf_arrayByMappingObjects:(MDFMappingFunction)function { + NSAssert(function, @"Mapping block must not be NULL."); + NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:[self count]]; + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [array addObject:function(obj)]; + }]; + return [array copy]; +} + +- (BOOL)mdf_anyObjectPassesTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + NSIndexSet *indices = [self indexesOfObjectsPassingTest:predicate]; + return [indices count] > 0; +} + +- (BOOL)mdf_allObjectsPassTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + NSIndexSet *indices = [self indexesOfObjectsPassingTest:predicate]; + return [indices count] == [self count]; +} + +- (NSArray *)mdf_sortArray:(NSArray *)array usingComparator:(NSComparator)comparator { + NSAssert(comparator, @"Comparator block must not be NULL."); + + NSUInteger numElements = [self count]; + NSAssert([array count] == numElements, @"Array %@ must have length %lu.", array, + (unsigned long)numElements); + + // Create a permutation array by sorting self with comparator. + NSMutableArray *permutation = [[NSMutableArray alloc] initWithCapacity:numElements]; + for (NSUInteger i = 0; i < numElements; ++i) { + [permutation addObject:@(i)]; + } + + NSArray *sortedPermutation = [permutation sortedArrayUsingComparator:^(id a, id b) { + NSUInteger firstIndex = [a unsignedIntegerValue]; + NSUInteger secondIndex = [b unsignedIntegerValue]; + return comparator(self[firstIndex], self[secondIndex]); + }]; + + // Permute array into order. + NSMutableArray *sorted = [[NSMutableArray alloc] initWithCapacity:numElements]; + for (NSUInteger i = 0; i < numElements; ++i) { + NSUInteger index = [sortedPermutation[i] unsignedIntegerValue]; + [sorted addObject:array[index]]; + } + return [sorted copy]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/Manifest.lock b/test/fixtures/cocoapods/Pods/Manifest.lock new file mode 100644 index 00000000..dd84759f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Manifest.lock @@ -0,0 +1,102 @@ +PODS: + - Alamofire (5.4.3) + - Chatto (4.1.0) + - lottie-ios (3.3.0) + - MaterialComponents/AnimationTiming (124.2.0) + - MaterialComponents/Availability (124.2.0) + - MaterialComponents/Buttons (124.2.0): + - MaterialComponents/Elevation + - MaterialComponents/Ink + - MaterialComponents/private/Math + - MaterialComponents/Ripple + - MaterialComponents/Shadow + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/ShapeLibrary + - MaterialComponents/Shapes + - MaterialComponents/Typography + - MDFInternationalization + - MDFTextAccessibility + - MaterialComponents/Cards (124.2.0): + - MaterialComponents/Elevation + - MaterialComponents/Ink + - MaterialComponents/private/Icons/ic_check_circle + - MaterialComponents/private/Math + - MaterialComponents/Ripple + - MaterialComponents/ShadowLayer + - MaterialComponents/Shapes + - MaterialComponents/Elevation (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Ink (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/private/Application (124.2.0) + - MaterialComponents/private/Color (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Icons/Base (124.2.0) + - MaterialComponents/private/Icons/ic_check_circle (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Math (124.2.0) + - MaterialComponents/Ripple (124.2.0): + - MaterialComponents/AnimationTiming + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Shadow (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/ShadowElevations (124.2.0) + - MaterialComponents/ShadowLayer (124.2.0): + - MaterialComponents/ShadowElevations + - MaterialComponents/ShapeLibrary (124.2.0): + - MaterialComponents/private/Math + - MaterialComponents/Shapes + - MaterialComponents/Shapes (124.2.0): + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/ShadowLayer + - MaterialComponents/Typography (124.2.0): + - MaterialComponents/private/Application + - MaterialComponents/private/Math + - MDFTextAccessibility + - MDFInternationalization (3.0.0) + - MDFTextAccessibility (2.0.1) + +DEPENDENCIES: + - Alamofire (= 5.4.3) + - Chatto (from `https://github.com/badoo/Chatto`, tag `4.1.0`) + - lottie-ios (= 3.3.0) + - MaterialComponents/Buttons (= 124.2.0) + - MaterialComponents/Cards (= 124.2.0) + +SPEC REPOS: + trunk: + - Alamofire + - lottie-ios + - MaterialComponents + - MDFInternationalization + - MDFTextAccessibility + +EXTERNAL SOURCES: + Chatto: + :git: https://github.com/badoo/Chatto + :tag: 4.1.0 + +CHECKOUT OPTIONS: + Chatto: + :git: https://github.com/badoo/Chatto + :tag: 4.1.0 + +SPEC CHECKSUMS: + Alamofire: e447a2774a40c996748296fa2c55112fdbbc42f9 + Chatto: dae34c377e0eca8bdcfe7a05a4d79d55eaccec3e + lottie-ios: 6ac74dcc09904798f59b18cb3075c089d76be9ae + MaterialComponents: 1a9b2d9d45b1601ae544de85089adc4c464306d4 + MDFInternationalization: d697c55307816222a55685c4ccb1044ffb030c12 + MDFTextAccessibility: f4bb4cc2194286651b59a215fdeaa0e05dc90ba5 + +PODFILE CHECKSUM: 5288d14d2586d983b7a18fae6fc10abda0f306a9 + +COCOAPODS: 1.11.2 diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE b/test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/README.md b/test/fixtures/cocoapods/Pods/MaterialComponents/README.md new file mode 100644 index 00000000..82c914f9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/README.md @@ -0,0 +1,71 @@ +# Material Components for iOS + + + +[![Code coverage](https://img.shields.io/codecov/c/github/material-components/material-components-ios/develop.svg)](https://codecov.io/gh/material-components/material-components-ios/branch/develop) + +Material Components for iOS (MDC-iOS) helps developers execute [Material Design](https://material.io). Developed by a core team of engineers and UX designers at Google, these components enable a reliable development workflow to build beautiful and functional iOS apps. Learn more about how Material Components for iOS supports design and usability best practices across platforms in the [Material Design Platform Adaptation guidelines](https://material.io/guidelines/platforms/platform-adaptation.html). + +Material Components for iOS are written in Objective-C and support Swift and Interface Builder. + +## Useful Links + +- [Documentation](https://material.io/components/ios/) (external site) +- [How To Use MDC-iOS](docs/) +- [All Components](components/) +- [Demo Apps](demos/) +- [Contributing](contributing/) +- [MDC-iOS on Stack Overflow](https://www.stackoverflow.com/questions/tagged/material-components+ios) (external site) +- [Material.io](https://material.io) (external site) +- [Material Design Guidelines](https://material.io/guidelines) (external site) +- [Checklist status spreadsheet](https://docs.google.com/spreadsheets/d/e/2PACX-1vRQLFMuo0Q3xsJp1_TdWvImtfdc8dU0lqX2DTct5pOPAEUIrN9OsuPquvv4aKRAwKK_KItpGs7c4Fok/pubhtml) + +## Trying out Material Components + +[CocoaPods](https://cocoapods.org/) is the easiest way to get started (if you're new to CocoaPods, +check out their [getting started documentation](https://guides.cocoapods.org/using/getting-started.html).) + +To install CocoaPods, run the following commands: + +```bash +sudo gem install cocoapods +``` + +Our [catalog](catalog/) showcases Material Components. You can use the `pod try` command from anywhere on your machine to try the components, even if you haven't checked out the repo yet: + +``` bash +pod try MaterialComponents +``` + +In case you have already checked out the repo, run the following command: + +``` bash +pod install --project-directory=catalog/ +``` + +The component implementations can be found in Xcode within `Pods > Development Pods > MaterialComponents`. + +## Requirements + +- Xcode 10 or higher +- Minimum iOS deployment target of 10.0 or higher +- CocoaPods 1.5 or higher + +## Attributions + +Material Components for iOS uses +[Material Design icons](https://github.com/google/material-design-icons), +copyright Google Inc. and licensed under +[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). + +Several components use +[MDFTextAccessibility](https://github.com/material-foundation/material-text-accessibility-ios), +copyright Google Inc. and licensed under +[Apache 2.0](https://github.com/material-foundation/material-text-accessibility-ios/blob/master/LICENSE) +without a NOTICE file. + +MDCCatalog uses the +[Roboto font](https://github.com/google/fonts/tree/master/apache/roboto), +copyright 2011 Google Inc. and licensed under +[Apache 2.0](https://github.com/google/fonts/blob/master/apache/roboto/LICENSE.txt) +without a NOTICE file. diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h new file mode 100644 index 00000000..d7b717a9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h @@ -0,0 +1,82 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + Material Design easing curve animation values. + + Use these easing curves to create smooth and consistent motion that conforms to Material Design. + */ +typedef NS_ENUM(NSUInteger, MDCAnimationTimingFunction) { + /** + This is the most frequently used interpolation curve for Material Design animations. This curve + is slow both at the beginning and end. It has similar characteristics to the system's EaseInOut. + This is known as Standard in the Material Design spec. + */ + MDCAnimationTimingFunctionStandard, + + /** + This curve should be used for motion when entering frame or when fading in from 0% opacity. This + curve is slow at the end. It has similar characteristics to the system's EaseOut. This is known + as Deceleration in the Material Design spec. + */ + MDCAnimationTimingFunctionDeceleration, + + /** + This curve should be used for motion when exiting frame or when fading out to 0% opacity. This + curve is slow at the beginning. It has similar characteristics to the system's EaseIn. This + is known as Acceleration in the Material Design spec. + */ + MDCAnimationTimingFunctionAcceleration, + + /** + This curve should be used for motion when elements quickly accelerate and decelerate. It is + used by exiting elements that may return to the screen at any time. The deceleration is + faster than the standard curve since it doesn't follow an exact path to the off-screen point. + */ + MDCAnimationTimingFunctionSharp, + + /** + Aliases for depreciated names + */ + MDCAnimationTimingFunctionEaseInOut = MDCAnimationTimingFunctionStandard, + MDCAnimationTimingFunctionEaseOut = MDCAnimationTimingFunctionDeceleration, + MDCAnimationTimingFunctionEaseIn = MDCAnimationTimingFunctionAcceleration, + + /** + Aliases for various specific timing curve recommendations. + */ + MDCAnimationTimingFunctionTranslate = MDCAnimationTimingFunctionStandard, + MDCAnimationTimingFunctionTranslateOnScreen = MDCAnimationTimingFunctionDeceleration, + MDCAnimationTimingFunctionTranslateOffScreen = MDCAnimationTimingFunctionAcceleration, + MDCAnimationTimingFunctionFadeIn = MDCAnimationTimingFunctionDeceleration, + MDCAnimationTimingFunctionFadeOut = MDCAnimationTimingFunctionAcceleration, +}; + +/** + Material Design animation curves. + */ +@interface CAMediaTimingFunction (MDCAnimationTiming) + +/** + Returns the corresponding CAMediaTimingFunction for the given curve specified by an enum. The most + common curve is MDCAnimationTimingFunctionEaseInOut. + + @param type A Material Design media timing function. + */ ++ (nullable CAMediaTimingFunction *)mdc_functionWithType:(MDCAnimationTimingFunction)type; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m new file mode 100644 index 00000000..b2f43eea --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m @@ -0,0 +1,37 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "CAMediaTimingFunction+MDCAnimationTiming.h" + +@implementation CAMediaTimingFunction (MDCAnimationTiming) + ++ (CAMediaTimingFunction *)mdc_functionWithType:(MDCAnimationTimingFunction)type { + switch (type) { + // clang-format off + case MDCAnimationTimingFunctionStandard: + return [[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:(float)0.2:1]; + case MDCAnimationTimingFunctionDeceleration: + return [[CAMediaTimingFunction alloc] initWithControlPoints:0:0:(float)0.2:1]; + case MDCAnimationTimingFunctionAcceleration: + return [[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:1:1]; + case MDCAnimationTimingFunctionSharp: + return [[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:(float)0.6:1]; + // clang-format on + } + NSAssert(NO, @"Invalid MDCAnimationTimingFunction value %i.", (int)type); + // Reasonable default to use in Release mode for garbage input. + return nil; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h new file mode 100644 index 00000000..fa2b8798 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h @@ -0,0 +1,16 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "CAMediaTimingFunction+MDCAnimationTiming.h" +#import "UIView+MDCTimingFunction.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h new file mode 100644 index 00000000..ce503185 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h @@ -0,0 +1,37 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +@interface UIView (MDCTimingFunction) + +/** + A convienence method for applying a timing function to animations. + + @param timingFunction A timing function for the easing curve animation. + @param duration The time the animation takes. + @param delay The time to wait before the animation begins. + @param options Configuration options for the timing function. + @param animations Animations to which the timing function will apply. + @param completion A completion block fired after the animations complete. + */ ++ (void)mdc_animateWithTimingFunction:(nullable CAMediaTimingFunction *)timingFunction + duration:(NSTimeInterval)duration + delay:(NSTimeInterval)delay + options:(UIViewAnimationOptions)options + animations:(void (^__nonnull)(void))animations + completion:(void (^__nullable)(BOOL finished))completion; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m new file mode 100644 index 00000000..bdb7ad64 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m @@ -0,0 +1,35 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIView+MDCTimingFunction.h" + +@implementation UIView (MDCTimingFunction) + ++ (void)mdc_animateWithTimingFunction:(CAMediaTimingFunction *)timingFunction + duration:(NSTimeInterval)duration + delay:(NSTimeInterval)delay + options:(UIViewAnimationOptions)options + animations:(void (^)(void))animations + completion:(void (^)(BOOL finished))completion { + [CATransaction begin]; + [CATransaction setAnimationTimingFunction:timingFunction]; + [UIView animateWithDuration:duration + delay:delay + options:options + animations:animations + completion:completion]; + [CATransaction commit]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h new file mode 100644 index 00000000..03b116d4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h @@ -0,0 +1,39 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights +// Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MDC_AVAILABILITY +#define MDC_AVAILABILITY + +#include + +/* + A header to be used in conjunction with Availability.h to conditionally + compile OS sensitive code. + + MDC_AVAILABLE_SDK_IOS(_ios) evaluates to true when the maximum target SDK is + greater than equal to the given value. This will only prevent code from + compiling when built with a maximum SDK version lower than the version + specified. A runtime check may still be necessary. Example: + + #if MDC_AVAILABLE_SDK_IOS(13_0) + // iOS 13 specific code. + #endif + */ + +#define MDC_AVAILABLE_SDK_IOS(_ios) \ + ((__IPHONE_##_ios != 0) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_##_ios)) + +#endif // MDC_AVAILABILITY diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h new file mode 100644 index 00000000..e1726c82 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h @@ -0,0 +1,16 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights +// Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCAvailability.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h new file mode 100644 index 00000000..2440c279 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h @@ -0,0 +1,453 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "MaterialElevation.h" +#import "MaterialInk.h" +#import "MaterialRipple.h" +#import "MaterialShadow.h" +#import "MaterialShadowElevations.h" +#import "MaterialShapes.h" + +/** + A Material flat, raised or floating button. + + All buttons display animated ink splashes when the user interacts with the button. + + The title color of the button set to have an accessible contrast ratio with the button's + background color. To ensure this works for flat buttons (with transparent background), the caller + is responsible for setting (and updating, if necessary) the button's underlyingColor property. + + All buttons set the exclusiveTouch property to YES by default, which prevents users from + simultaneously interacting with a button and other UI elements. + + @see https://material.io/go/design-buttons + */ +@interface MDCButton : UIButton + +/** The ripple style of the button. */ +@property(nonatomic, assign) MDCRippleStyle rippleStyle; + +/** + The color of the ripple. + + @note Defaults to a transparent black. + */ +@property(nonatomic, strong, null_resettable) UIColor *rippleColor; + +/** + The maximum radius the ripple can expand to. + + @note This property is ignored if @c rippleStyle is set to @c MDCRippleStyleBounded. + + @note Defaults to 0. + */ +@property(nonatomic, assign) CGFloat rippleMaximumRadius; + +/** + This property determines if an @c MDCButton should use the @c MDCInkView behavior or not. + + By setting this property to @c YES, @c MDCStatefulRippleView is used to provide the user visual + touch feedback, instead of the legacy @c MDCInkView. + + @note Defaults to @c NO. + */ +@property(nonatomic, assign) BOOL enableRippleBehavior; + +/** + The alpha value that will be applied when the button is disabled. Most clients can leave this as + the default value to get a semi-transparent button automatically. + */ +@property(nonatomic) CGFloat disabledAlpha; + +/** + If true, converts the button title to uppercase. Changing this property to NO will update the + current title string. + + Default is YES. + */ +@property(nonatomic, getter=isUppercaseTitle) BOOL uppercaseTitle UI_APPEARANCE_SELECTOR; + +/** + A Boolean value that determines whether the visible area is centered in the bounds of the view. + + If set to YES, the visible area is centered in the bounds of the view, which is often used to + configure invisible tappable area. If set to NO, the visible area fills its bounds. This property + doesn't affect the result of @c sizeThatFits:. + + The default value is @c NO. +*/ +@property(nonatomic, assign) BOOL centerVisibleArea; + +/** + The edges of this guide are constrained to equal the edges of the visible area + when @c centerVisibleArea is @c YES. + + @note If centerVisibleArea is @c NO then visibleAreaLayoutGuide is nil. +*/ +@property(nonatomic, readonly, strong, nullable) UILayoutGuide *visibleAreaLayoutGuide; + +/** + The default content edge insets of the button. They are set at initialization time. + */ +@property(nonatomic, readonly) UIEdgeInsets defaultContentEdgeInsets; + +/** + The offset (in points) of the button's inkView or rippleView (depending on which is being used - + see @c enableRippleBehavior) + + Default is CGSizeZero. + */ +@property(nonatomic) CGSize inkViewOffset; + +/** + The inset or outset margins for the rectangle surrounding the button’s ripple. + */ +@property(nonatomic, assign) UIEdgeInsets rippleEdgeInsets; + +/** + The minimum size of the button’s alignment rect. If either the height or width are non-positive + (negative or zero), they will be ignored and that axis will adjust to its content size. + + Defaults to CGSizeZero. + */ +@property(nonatomic, assign) CGSize minimumSize UI_APPEARANCE_SELECTOR; + +/** + The maximum size of the button’s alignment rect. If either the height or width are non-positive + (negative or zero), they will be ignored and that axis will adjust to its content size. Setting a + maximum size may result in image clipping or text truncation. + + Defaults to CGSizeZero. + */ +@property(nonatomic, assign) CGSize maximumSize UI_APPEARANCE_SELECTOR; + +/** + Setting this property to @c YES when the button's @c titleLabel is multi-line (i.e. when @c + numberOfLines is not equal to 1) will result in the button inferring what its size should be and + then setting both the @c minimumSize and @c maximumSize to that value. Setting this property back + to @c NO will result in @c maximumSize and @c minimumSize being reset to @c CGSizeZero. + + In both Manual Layout and Auto Layout environments the inferred height is a function of the width. + In an Auto Layout environment the width will depend on the constraints placed on the view. In a + Manual Layout environment the current width will be assumed to be the preferred width, so it is + important to make sure the button's width is set to an appropriate value before turning this flag + on. In an Auto Layout environment, the view will likely resize itself as needed when this flag is + turned on. In a Manual Layout environment, you will likely have to call @c -sizeToFit after turning + this flag on. + + Defaults to NO. + */ +@property(nonatomic, assign) BOOL inferMinimumAndMaximumSizeWhenMultiline; + +/** + The apparent background color as seen by the user, i.e. the color of the view behind the button. + + The underlying color hint is used by buttons to calculate accessible title text colors when in + states with transparent background colors. The hint is used whenever the button changes state such + that the background color changes, for example, setting the background color or disabling the + button. + + For flat buttons, this is the color of both the surrounding area and the button's background. + For raised and floating buttons, this is the color of view underneath the button. + + The default is nil. If left unset, buttons will likely have an incorrect appearance when + disabled. Additionally, flat buttons might have text colors with low accessibility. + */ +@property(nonatomic, strong, nullable) UIColor *underlyingColorHint; + +/* + Indicates whether the button should automatically update its font when the device’s + UIContentSizeCategory is changed. + + This property is modeled after the adjustsFontForContentSizeCategory property in the + UIContentSizeCategoryAdjusting protocol added by Apple in iOS 10.0. + + If set to YES, this button will base its text font on MDCFontTextStyleButton. + + Defaults value is NO. + */ +@property(nonatomic, readwrite, setter=mdc_setAdjustsFontForContentSizeCategory:) + BOOL mdc_adjustsFontForContentSizeCategory UI_APPEARANCE_SELECTOR; + +/** + Affects the fallback behavior for when a scaled font is not provided. + + If @c YES, the font size will adjust even if a scaled font has not been provided for + a given @c UIFont property on this component. + + If @c NO, the font size will only be adjusted if a scaled font has been provided. + + Default value is @c YES. + */ +@property(nonatomic, assign) BOOL adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable; + +/** + The shape generator used to define the button's shape. + + When the shapeGenerator is nil, MDCButton will use the default underlying layer with + its default settings. + + @note If a layer property is explicitly set after the shapeGenerator has been set, + it can lead to unexpected behavior. + + @note When @c centerVisibleArea is set to YES, this property can no longer be set. + + Default value for shapeGenerator is nil. + */ +@property(nullable, nonatomic, strong) id shapeGenerator; + +/** + A block that is invoked when the MDCButton receives a call to @c + traitCollectionDidChange:. The block is called after the call to the superclass. + */ +@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) + (MDCButton *_Nonnull button, UITraitCollection *_Nullable previousTraitCollection); + +/** + A color used as the button's @c backgroundColor for @c state. + + @param state The state. + @return The background color. + */ +- (nullable UIColor *)backgroundColorForState:(UIControlState)state; + +/** + A color used as the button's @c backgroundColor. + + If left unset or reset to nil for a given state, then a default blue color is used. + + @param backgroundColor The background color. + @param state The state. + */ +- (void)setBackgroundColor:(nullable UIColor *)backgroundColor + forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/* Convenience for `setBackgroundColor:backgroundColor forState:UIControlStateNormal`. */ +- (void)setBackgroundColor:(nullable UIColor *)backgroundColor; + +/** Sets the enabled state with optional animation. */ +- (void)setEnabled:(BOOL)enabled animated:(BOOL)animated; + +/** + Returns the elevation for a particular control state. + + The default values depend on the kind of button, for example, flat buttons in the + UIControlStateNormal state have zero elevation. + + @param state The control state to retrieve the elevation. + @return The elevation for the requested state. + */ +- (MDCShadowElevation)elevationForState:(UIControlState)state; + +/** + Sets the elevation for a particular control state. + + @param elevation The elevation to set. + @param state The state to set. + */ +- (void)setElevation:(MDCShadowElevation)elevation forState:(UIControlState)state; + +/** + A color used as the button's @c borderColor for @c state. + + @param state The state. + @return The border color. + */ +- (nullable UIColor *)borderColorForState:(UIControlState)state; + +/** + Sets the border color for a particular control state. Sets the @c borderColor of the layer. + + @param borderColor The border color to set. + @param state The state to set. + */ +- (void)setBorderColor:(nullable UIColor *)borderColor + forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + A color used as the button's imageView tint color @c imageTintColor for @c state. + + If no image tint color has been set for a given state, the returned value will fall back to the + value set for UIControlStateNormal. + + @param state The state. + @return The tint color. + */ +- (nullable UIColor *)imageTintColorForState:(UIControlState)state; + +/** + Sets the image view tint color for a particular control state. + + If left unset or reset to nil for a given state, it falls back to UIControlStateNormal setting. + + @param imageTintColor The imageView tint color to set. + @param state The state to set. + */ +- (void)setImageTintColor:(nullable UIColor *)imageTintColor forState:(UIControlState)state; + +/** + The value set for the button's @c borderWidth for @c state. + + @param state The state. + @return The border width. + */ +- (CGFloat)borderWidthForState:(UIControlState)state; + +/** + Sets the border width for a particular control state. Sets the @c borderWidth of the layer. + + @param borderWidth The border width to set. + @param state The state to set. + */ +- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Sets this button's layer's shadowColor for the specified control state. + + During initialization, the value for @c UIControlStateNormal is set to the value of this button's + layer's @c shadowColor. Providing a @c nil value for @c shadowColor will remove the shadow color + mapping for the specified state. + + If the color is not set for a specified state, the default behavior is to use the color associated + with @c UIControlStateNormal. If the color for @c UIControlStateNormal is not set, then @c nil will + be used. + + @param shadowColor The shadow color to use for the specified state. + @param state The state that uses the specified color. The possible values are described in + @c UIControlState. + */ +- (void)setShadowColor:(nullable UIColor *)shadowColor + forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + The color used as this button's layer's @c shadowColor for the specified control state. + + During initialization, the value for @c UIControlStateNormal is set to the value of this view's + layer's @c shadowColor. + + If the color is not set for a specified state, the default behavior is to use the color associated + with @c UIControlStateNormal. If the color for @c UIControlStateNormal has not been set, then + @c nil is returned. + + @param state The state that uses the shadow color. The possible values are described in + @c UIControlState. + @return The shadow color for the specified state. If no shadow color has been set for the + specific state, this method returns the shadow color associated with the + @c UIControlStateNormal state. + + @return The shadow color. + */ +- (nullable UIColor *)shadowColorForState:(UIControlState)state; + +#pragma mark - UIButton changes + +/** + From UIButton's documentation: "If you subclass UIButton, this method does not return an instance + of your subclass. If you want to create an instance of a specific subclass, you must alloc/init + the button directly." + */ ++ (nonnull instancetype)buttonWithType:(UIButtonType)buttonType NS_UNAVAILABLE; + +@end + +@interface MDCButton (ToBeDeprecated) + +/** + Enables the state-based font behavior of the receiver. + + If @c NO, then @c titleFont:forState: and @c setTitleFont:forState: have no effect. Defaults to + @c YES. + + @note This API will eventually be deprecated and removed. + */ +@property(nonatomic, assign) BOOL enableTitleFontForState; + +/** + The inset margins for the rectangle surrounding all of the button’s visual representation. + Use this property when you wish to have the touch target (frame) be larger than the + visible content. + + A positive value shrinks the visible area of the button. A negative value expands the visible area + of the button. + + The button uses this property to determine intrinsicContentSize and sizeThatFits:. + + @note This API will be deprecated and removed. Consider using @c centerVisibleArea. + + Default is UIEdgeInsetsZero. +*/ +@property(nonatomic, assign) UIEdgeInsets visibleAreaInsets; + +/** + The font used by the button's @c title. + + If left unset or reset to nil for a given state, then a default font is used. + + @param font The font. + @param state The state. + + @note This API will eventually be deprecated and removed. + */ +- (void)setTitleFont:(nullable UIFont *)font forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + The font used by the button's @c title for @c state. + + @param state The state. + @return The font. + + @note This API will eventually be deprecated and removed. + */ +- (nullable UIFont *)titleFontForState:(UIControlState)state; + +/** + If @c true, @c accessiblityTraits will always include @c UIAccessibilityTraitButton. + If @c false, @c accessibilityTraits will inherit its behavior from @c UIButton. + + @note Defaults to true. + @note This API is intended as a migration flag to restore @c UIButton behavior to @c MDCButton. In + a future version, this API will eventually be deprecated and then deleted. + */ +@property(nonatomic, assign) BOOL accessibilityTraitsIncludesButton; + +/** The ink style of the button. */ +@property(nonatomic, assign) MDCInkStyle inkStyle UI_APPEARANCE_SELECTOR; + +/** The ink color of the button. */ +@property(nonatomic, strong, null_resettable) UIColor *inkColor UI_APPEARANCE_SELECTOR; + +/* + Maximum radius of the button's ink. If the radius <= 0 then half the length of the diagonal of + self.bounds is used. This value is ignored if button's @c inkStyle is set to |MDCInkStyleBounded|. + */ +@property(nonatomic, assign) CGFloat inkMaxRippleRadius UI_APPEARANCE_SELECTOR; + +@end + +@interface MDCButton (Deprecated) + +/** + Insets to apply to the button’s hit area. + + Allows the button to detect touches outside of its bounds. A negative value indicates an + extension past the bounds. + + Default is UIEdgeInsetsZero. + */ +@property(nonatomic) UIEdgeInsets hitAreaInsets __deprecated_msg("Use centerVisibleArea instead."); + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m new file mode 100644 index 00000000..6bee2b3b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m @@ -0,0 +1,1452 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCButton.h" + +#import "private/MDCButton+Subclassing.h" +#import "MaterialElevation.h" +#import "MaterialInk.h" +#import "MaterialRipple.h" +#import "MaterialShadow.h" +#import "MaterialShadowElevations.h" +#import "MaterialShapeLibrary.h" +#import "MaterialShapes.h" +#import "MaterialTypography.h" +#import "MaterialMath.h" + +// TODO(ajsecord): Animate title color when animating between enabled/disabled states. +// Non-trivial: http://corecocoa.wordpress.com/2011/10/04/animatable-text-color-of-uilabel/ + +// Specified in Material Guidelines +// https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-touch-target-size +static const CGFloat MDCButtonMinimumTouchTargetHeight = 48; +static const CGFloat MDCButtonMinimumTouchTargetWidth = 48; +static const CGFloat MDCButtonDefaultCornerRadius = 2.0; +static const CGFloat kDefaultRippleAlpha = (CGFloat)0.12; + +static const NSTimeInterval MDCButtonAnimationDuration = 0.2; + +// https://material.io/go/design-buttons#buttons-main-buttons +static const CGFloat MDCButtonDisabledAlpha = (CGFloat)0.12; + +// Blue 500 from https://material.io/go/design-color-theming#color-color-palette . +static const uint32_t MDCButtonDefaultBackgroundColor = 0x191919; + +// KVO contexts +static char *const kKVOContextCornerRadius = "kKVOContextCornerRadius"; + +// Creates a UIColor from a 24-bit RGB color encoded as an integer. +static inline UIColor *MDCColorFromRGB(uint32_t rgbValue) { + return [UIColor colorWithRed:((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255 + green:((CGFloat)((rgbValue & 0x00FF00) >> 8)) / 255 + blue:((CGFloat)((rgbValue & 0x0000FF) >> 0)) / 255 + alpha:1]; +} + +// Expands size by provided edge insets. +static inline CGSize CGSizeExpandWithInsets(CGSize size, UIEdgeInsets edgeInsets) { + return CGSizeMake(size.width + edgeInsets.left + edgeInsets.right, + size.height + edgeInsets.top + edgeInsets.bottom); +} + +// Shrinks size by provided edge insets, and also standardized. +static inline CGSize CGSizeShrinkWithInsets(CGSize size, UIEdgeInsets edgeInsets) { + return CGSizeMake(MAX(0, size.width - (edgeInsets.left + edgeInsets.right)), + MAX(0, size.height - (edgeInsets.top + edgeInsets.bottom))); +} + +static NSAttributedString *UppercaseAttributedString(NSAttributedString *string) { + // Store the attributes. + NSMutableArray *attributes = [NSMutableArray array]; + [string enumerateAttributesInRange:NSMakeRange(0, [string length]) + options:0 + usingBlock:^(NSDictionary *attrs, NSRange range, __unused BOOL *stop) { + [attributes addObject:@{ + @"attrs" : attrs, + @"range" : [NSValue valueWithRange:range] + }]; + }]; + + // Make the string uppercase. + NSString *uppercaseString = [[string string] uppercaseStringWithLocale:[NSLocale currentLocale]]; + + // Apply the text and attributes to a mutable copy of the title attributed string. + NSMutableAttributedString *mutableString = [string mutableCopy]; + [mutableString replaceCharactersInRange:NSMakeRange(0, [string length]) + withString:uppercaseString]; + for (NSDictionary *attribute in attributes) { + [mutableString setAttributes:attribute[@"attrs"] range:[attribute[@"range"] rangeValue]]; + } + + return [mutableString copy]; +} + +@interface MDCButton () { + // For each UIControlState. + NSMutableDictionary *_userElevations; + NSMutableDictionary *_backgroundColors; + NSMutableDictionary *_borderColors; + NSMutableDictionary *_borderWidths; + NSMutableDictionary *_shadowColors; + NSMutableDictionary *_imageTintColors; + NSMutableDictionary *_fonts; + + CGFloat _enabledAlpha; + BOOL _hasCustomDisabledTitleColor; + BOOL _imageTintStatefulAPIEnabled; + + // Cached titles and accessibility labels. + NSMutableDictionary *_nontransformedTitles; + NSString *_accessibilityLabelExplicitValue; + + BOOL _mdc_adjustsFontForContentSizeCategory; + BOOL _cornerRadiusObserverAdded; + CGFloat _inkMaxRippleRadius; + + MDCShapeMediator *_shapedLayer; + CGFloat _currentElevation; +} +@property(nonatomic, strong, readonly, nonnull) MDCStatefulRippleView *rippleView; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +@property(nonatomic, strong) MDCInkView *inkView; +#pragma clang diagnostic pop +@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; +@property(nonatomic, assign) BOOL accessibilityTraitsIncludesButton; +@property(nonatomic, assign) BOOL enableTitleFontForState; +@property(nonatomic, assign) UIEdgeInsets visibleAreaInsets; +@property(nonatomic, strong) UIView *visibleAreaLayoutGuideView; +@property(nonatomic) UIEdgeInsets hitAreaInsets; +@property(nonatomic, assign) UIEdgeInsets currentVisibleAreaInsets; +@property(nonatomic, assign) CGSize lastRecordedIntrinsicContentSize; +@end + +@implementation MDCButton + +static BOOL gEnablePerformantShadow = NO; + +@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation; +@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock; +@synthesize visibleAreaInsets = _visibleAreaInsets; +@synthesize visibleAreaLayoutGuide = _visibleAreaLayoutGuide; +@synthesize shadowsCollection = _shadowsCollection; +@dynamic layer; + ++ (Class)layerClass { + if (gEnablePerformantShadow) { + return [super class]; + } else { + return [MDCShapedShadowLayer class]; + } +} + +- (instancetype)init { + return [self initWithFrame:CGRectZero]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + // Set up title label attributes. + // TODO(#2709): Have a single source of truth for fonts + // Migrate to [UIFont standardFont] when possible + self.titleLabel.font = [MDCTypography buttonFont]; + + [self commonMDCButtonInit]; + [self updateBackgroundColor]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCButtonInit]; + + if (self.titleLabel.font) { + _fonts = [@{} mutableCopy]; + _fonts[@(UIControlStateNormal)] = self.titleLabel.font; + } + + // Storyboards will set the backgroundColor via the UIView backgroundColor setter, so we have + // to write that in to our _backgroundColors dictionary. + _backgroundColors[@(UIControlStateNormal)] = gEnablePerformantShadow + ? _shapedLayer.shapedBackgroundColor + : self.layer.shapedBackgroundColor; + [self updateBackgroundColor]; + } + return self; +} + +- (void)commonMDCButtonInit { + // TODO(b/142861610): Default to `NO`, then remove once all internal usage is migrated. + _enableTitleFontForState = YES; + if (gEnablePerformantShadow) { + _shapedLayer = [[MDCShapeMediator alloc] initWithViewLayer:self.layer]; + } + _disabledAlpha = MDCButtonDisabledAlpha; + _enabledAlpha = self.alpha; + _uppercaseTitle = YES; + _userElevations = [NSMutableDictionary dictionary]; + _nontransformedTitles = [NSMutableDictionary dictionary]; + _borderColors = [NSMutableDictionary dictionary]; + _imageTintColors = [NSMutableDictionary dictionary]; + _borderWidths = [NSMutableDictionary dictionary]; + _fonts = [NSMutableDictionary dictionary]; + _accessibilityTraitsIncludesButton = YES; + _adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable = YES; + _mdc_overrideBaseElevation = -1; + _currentElevation = 0; + + if (!_backgroundColors) { + // _backgroundColors may have already been initialized by setting the backgroundColor setter. + _backgroundColors = [NSMutableDictionary dictionary]; + _backgroundColors[@(UIControlStateNormal)] = MDCColorFromRGB(MDCButtonDefaultBackgroundColor); + } + + // Disable default highlight state. + self.adjustsImageWhenHighlighted = NO; + +#if (!defined(TARGET_OS_TV) || TARGET_OS_TV == 0) + self.showsTouchWhenHighlighted = NO; +#endif + + self.layer.cornerRadius = MDCButtonDefaultCornerRadius; + if (gEnablePerformantShadow) { + self.layer.shadowColor = MDCShadowColor().CGColor; + } else { + self.layer.shadowColor = [UIColor blackColor].CGColor; + self.layer.elevation = [self elevationForState:self.state]; + } + + _shadowColors = [NSMutableDictionary dictionary]; + _shadowColors[@(UIControlStateNormal)] = [UIColor colorWithCGColor:self.layer.shadowColor]; + + // Set up ink layer. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + _inkView = [[MDCInkView alloc] initWithFrame:self.bounds]; +#pragma clang diagnostic pop + _inkView.usesLegacyInkRipple = NO; + [self insertSubview:_inkView belowSubview:self.imageView]; + // UIButton has a drag enter/exit boundary that is outside of the frame of the button itself. + // Because this is not exposed externally, we can't use -touchesMoved: to calculate when to + // change ink state. So instead we fall back on adding target/actions for these specific events. + [self addTarget:self + action:@selector(touchDragEnter:forEvent:) + forControlEvents:UIControlEventTouchDragEnter]; + [self addTarget:self + action:@selector(touchDragExit:forEvent:) + forControlEvents:UIControlEventTouchDragExit]; + +#if (!defined(TARGET_OS_TV) || TARGET_OS_TV == 0) + // Block users from activating multiple buttons simultaneously by default. + self.exclusiveTouch = YES; +#endif + + _inkView.inkColor = [UIColor colorWithWhite:1 alpha:(CGFloat)0.2]; + + _rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds]; + _rippleColor = [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha]; + _rippleView.rippleColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.12]; + + // Default content insets + // The default contentEdgeInsets are set here (instead of above, as they were previously) because + // of a UIButton bug introduced in the iOS 13 betas that is unresolved as of Xcode 11 beta 4 + // (b/136088498) wherein setting self.contentEdgeInsets before accessing self.imageView causes the + // imageView's bounds.origin to be set to { -(i.left + i.right), -(i.top + i.bottom) } + // This causes images created by using imageWithHorizontallyFlippedOrientation to not display. + // Images that have not been created this way seem to be fine. + // This behavior can also be seen in vanilla UIButtons by setting contentEdgeInsets to non-zero + // inset values and then setting an image created with imageWithHorizontallyFlippedOrientation. + self.contentEdgeInsets = [self defaultContentEdgeInsets]; + _minimumSize = CGSizeZero; + _maximumSize = CGSizeZero; + + // Uppercase all titles + if (_uppercaseTitle) { + [self updateTitleCase]; + } + +#ifdef __IPHONE_13_4 +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + if ([self respondsToSelector:@selector(pointerStyleProvider)]) { + __weak __typeof__(self) weakSelf = self; + UIButtonPointerStyleProvider buttonPointerStyleProvider = ^UIPointerStyle *( + UIButton *buttonToStyle, UIPointerEffect *proposedEffect, UIPointerShape *proposedShape) { + __typeof__(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + return [UIPointerStyle styleWithEffect:proposedEffect shape:proposedShape]; + } + CGPathRef boundingCGPath = [strongSelf boundingPath].CGPath; + UIBezierPath *boundingBezierPath = [UIBezierPath bezierPathWithCGPath:boundingCGPath]; + UIPointerShape *shape = [UIPointerShape shapeWithPath:boundingBezierPath]; + return [UIPointerStyle styleWithEffect:proposedEffect shape:shape]; + }; + self.pointerStyleProvider = buttonPointerStyleProvider; + // Setting the pointerStyleProvider to a non-nil value flips pointerInteractionEnabled to YES. + // To maintain parity with UIButton's default behavior, we want it to default to NO. + self.pointerInteractionEnabled = NO; + } + } +#endif // !TARGET_OS_TV +#endif // __IPHONE_13_4 +} + +- (void)dealloc { + [self removeTarget:self action:NULL forControlEvents:UIControlEventAllEvents]; + + if (_cornerRadiusObserverAdded) { + [self.layer removeObserver:self + forKeyPath:NSStringFromSelector(@selector(cornerRadius)) + context:kKVOContextCornerRadius]; + } +} + +- (void)setUnderlyingColorHint:(UIColor *)underlyingColorHint { + _underlyingColorHint = underlyingColorHint; + [self updateAlphaAndBackgroundColorAnimated:NO]; +} + +- (void)setAlpha:(CGFloat)alpha { + _enabledAlpha = alpha; + [super setAlpha:alpha]; +} + +- (void)setDisabledAlpha:(CGFloat)disabledAlpha { + _disabledAlpha = disabledAlpha; + [self updateAlphaAndBackgroundColorAnimated:NO]; +} + +#pragma mark - UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + [self updateShadowColor]; + [self updateBackgroundColor]; + [self updateBorderColor]; + + if (self.centerVisibleArea) { + UIEdgeInsets visibleAreaInsets = self.visibleAreaInsets; + if (!UIEdgeInsetsEqualToEdgeInsets(visibleAreaInsets, self.currentVisibleAreaInsets)) { + self.currentVisibleAreaInsets = visibleAreaInsets; + MDCRectangleShapeGenerator *shapeGenerator = + [self generateShapeWithCornerRadius:self.layer.cornerRadius + visibleAreaInsets:visibleAreaInsets]; + [self configureLayerWithShapeGenerator:shapeGenerator]; + if (self.visibleAreaLayoutGuideView) { + self.visibleAreaLayoutGuideView.frame = + UIEdgeInsetsInsetRect(self.bounds, visibleAreaInsets); + } + } + } + + if (gEnablePerformantShadow) { + if (_shapedLayer.shapeGenerator) { + [_shapedLayer layoutShapedSublayers]; + } + [self updateShadow]; + } else { + if (!self.layer.shapeGenerator) { + self.layer.shadowPath = [self boundingPath].CGPath; + } + } + + // Center unbounded ink view frame taking into account possible insets using contentRectForBounds. + if (_inkView.inkStyle == MDCInkStyleUnbounded && _inkView.usesLegacyInkRipple) { + CGRect contentRect = [self contentRectForBounds:self.bounds]; + CGPoint contentCenterPoint = + CGPointMake(CGRectGetMidX(contentRect), CGRectGetMidY(contentRect)); + CGPoint boundsCenterPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + + CGFloat offsetX = contentCenterPoint.x - boundsCenterPoint.x + self.inkViewOffset.width; + CGFloat offsetY = contentCenterPoint.y - boundsCenterPoint.y + self.inkViewOffset.height; + _inkView.frame = + CGRectMake(offsetX, offsetY, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); + } else { + CGRect bounds = CGRectStandardize(self.bounds); + bounds = CGRectOffset(bounds, self.inkViewOffset.width, self.inkViewOffset.height); + bounds = UIEdgeInsetsInsetRect(bounds, self.rippleEdgeInsets); + _inkView.frame = bounds; + self.rippleView.frame = bounds; + } + self.titleLabel.frame = MDCRectAlignToScale(self.titleLabel.frame, [UIScreen mainScreen].scale); + + if ([self shouldInferMinimumAndMaximumSize]) { + [self inferMinimumAndMaximumSize]; + } +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { + // If there are custom hitAreaInsets, use those + if (!UIEdgeInsetsEqualToEdgeInsets(self.hitAreaInsets, UIEdgeInsetsZero)) { + return CGRectContainsPoint( + UIEdgeInsetsInsetRect(CGRectStandardize(self.bounds), self.hitAreaInsets), point); + } + + // If the bounds are smaller than the minimum touch target, produce a warning once + CGFloat width = CGRectGetWidth(self.bounds); + CGFloat height = CGRectGetHeight(self.bounds); + if (width < MDCButtonMinimumTouchTargetWidth || height < MDCButtonMinimumTouchTargetHeight) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog( + @"Button touch target does not meet minimum size guidelines of (%0.f, %0.f). Button: %@, " + @"Touch Target: %@", + MDCButtonMinimumTouchTargetWidth, MDCButtonMinimumTouchTargetHeight, [self description], + NSStringFromCGSize(CGSizeMake(width, height))); + }); + } + return [super pointInside:point withEvent:event]; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview { + [super willMoveToSuperview:newSuperview]; + [self.inkView cancelAllAnimationsAnimated:NO]; + [self.rippleView cancelAllRipplesAnimated:NO completion:nil]; +} + +- (CGSize)sizeThatFits:(CGSize)size { + CGSize givenSizeWithInsets = CGSizeShrinkWithInsets(size, _visibleAreaInsets); + CGSize superSize = [super sizeThatFits:givenSizeWithInsets]; + + // TODO(b/171816831): revisit this in a future iOS version to verify reproducibility. + // Because of a UIKit bug in iOS 13 and 14 (current), buttons that have both an image and text + // will not return an appropriately large size from [super sizeThatFits:]. In this case, we need + // to expand the width. The number 1 was chosen somewhat arbitrarily, but based on some spot + // testing, adding the smallest amount of extra width possible seems to fix the issue. +#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + if (@available(iOS 13.0, *)) { + if (UIAccessibilityIsBoldTextEnabled() && [self imageForState:UIControlStateNormal] && + [self titleForState:UIControlStateNormal]) { + superSize.width += 1; + } + } +#endif + + if (self.minimumSize.height > 0) { + superSize.height = MAX(self.minimumSize.height, superSize.height); + } + if (self.maximumSize.height > 0) { + superSize.height = MIN(self.maximumSize.height, superSize.height); + } + if (self.minimumSize.width > 0) { + superSize.width = MAX(self.minimumSize.width, superSize.width); + } + if (self.maximumSize.width > 0) { + superSize.width = MIN(self.maximumSize.width, superSize.width); + } + + CGSize adjustedSize = CGSizeExpandWithInsets(superSize, _visibleAreaInsets); + return adjustedSize; +} + +- (CGSize)intrinsicContentSize { + CGSize size = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; + self.lastRecordedIntrinsicContentSize = size; + return size; +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + + if (self.traitCollectionDidChangeBlock) { + self.traitCollectionDidChangeBlock(self, previousTraitCollection); + } +} + +#pragma mark - UIResponder + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.enableRippleBehavior) { + [self.rippleView touchesBegan:touches withEvent:event]; + } + [super touchesBegan:touches withEvent:event]; + + if (!self.enableRippleBehavior) { + [self handleBeginTouches:touches]; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.enableRippleBehavior) { + [self.rippleView touchesMoved:touches withEvent:event]; + } + [super touchesMoved:touches withEvent:event]; + + // Drag events handled by -touchDragExit:forEvent: and -touchDragEnter:forEvent: +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.enableRippleBehavior) { + [self.rippleView touchesEnded:touches withEvent:event]; + } + [super touchesEnded:touches withEvent:event]; + + if (!self.enableRippleBehavior) { + CGPoint location = [self locationFromTouches:touches]; + [_inkView startTouchEndedAnimationAtPoint:location completion:nil]; + } +} + +// Note - in some cases, event may be nil (e.g. view removed from window). +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.enableRippleBehavior) { + [self.rippleView touchesCancelled:touches withEvent:event]; + } + [super touchesCancelled:touches withEvent:event]; + + if (!self.enableRippleBehavior) { + [self evaporateInkToPoint:[self locationFromTouches:touches]]; + } +} + +#pragma mark - UIControl methods + +- (void)setEnabled:(BOOL)enabled { + [self setEnabled:enabled animated:NO]; +} + +- (void)setEnabled:(BOOL)enabled animated:(BOOL)animated { + [super setEnabled:enabled]; + + [self updateAfterStateChange:animated]; +} + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + + self.rippleView.rippleHighlighted = highlighted; + [self updateAfterStateChange:NO]; +} + +- (void)setSelected:(BOOL)selected { + [super setSelected:selected]; + + self.rippleView.selected = selected; + [self updateAfterStateChange:NO]; +} + +- (void)updateAfterStateChange:(BOOL)animated { + [self updateAlphaAndBackgroundColorAnimated:animated]; + [self animateButtonToHeightForState:self.state]; + [self updateBorderColor]; + [self updateBorderWidth]; + [self updateShadowColor]; + [self updateTitleFont]; + [self updateImageTintColor]; +} + +#pragma mark - Title Uppercasing + +- (void)setUppercaseTitle:(BOOL)uppercaseTitle { + _uppercaseTitle = uppercaseTitle; + + [self updateTitleCase]; +} + +- (void)updateTitleCase { + // This calls setTitle or setAttributedTitle for every title value we have stored. In each + // respective setter the title is upcased if _uppercaseTitle is YES. + NSDictionary *nontransformedTitles = [_nontransformedTitles copy]; + for (NSNumber *key in nontransformedTitles.keyEnumerator) { + UIControlState state = key.unsignedIntegerValue; + NSString *title = nontransformedTitles[key]; + if ([title isKindOfClass:[NSAttributedString class]]) { + [self setAttributedTitle:(NSAttributedString *)title forState:state]; + } else if ([title isKindOfClass:[NSString class]]) { + [self setTitle:title forState:state]; + } + } +} + +- (void)updateShadowColor { + self.layer.shadowColor = [self shadowColorForState:self.state].CGColor; +} + +- (void)setShadowColor:(UIColor *)shadowColor forState:(UIControlState)state { + if (shadowColor) { + _shadowColors[@(state)] = shadowColor; + } else { + [_shadowColors removeObjectForKey:@(state)]; + } + + if (state == self.state) { + [self updateShadowColor]; + } +} + +- (UIColor *)shadowColorForState:(UIControlState)state { + UIColor *shadowColor = _shadowColors[@(state)]; + if (state != UIControlStateNormal && !shadowColor) { + shadowColor = _shadowColors[@(UIControlStateNormal)]; + } + return shadowColor; +} + +- (void)setTitle:(NSString *)title forState:(UIControlState)state { + // Intercept any setting of the title and store a copy in case the accessibilityLabel + // is requested and the original non-uppercased version needs to be returned. + if ([title length]) { + _nontransformedTitles[@(state)] = [title copy]; + } else { + [_nontransformedTitles removeObjectForKey:@(state)]; + } + + if (_uppercaseTitle) { + title = [title uppercaseStringWithLocale:[NSLocale currentLocale]]; + } + [super setTitle:title forState:state]; +} + +- (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state { + // Intercept any setting of the title and store a copy in case the accessibilityLabel + // is requested and the original non-uppercased version needs to be returned. + if ([title length]) { + _nontransformedTitles[@(state)] = [title copy]; + } else { + [_nontransformedTitles removeObjectForKey:@(state)]; + } + + if (_uppercaseTitle) { + title = UppercaseAttributedString(title); + } + [super setAttributedTitle:title forState:state]; +} + +#pragma mark - Accessibility + +- (void)setAccessibilityLabel:(NSString *)accessibilityLabel { + // Intercept any explicit setting of the accessibilityLabel so it can be returned + // later before the accessibiilityLabel is inferred from the setTitle:forState: + // argument values. + _accessibilityLabelExplicitValue = [accessibilityLabel copy]; + [super setAccessibilityLabel:accessibilityLabel]; +} + +- (NSString *)accessibilityLabel { + if (!_uppercaseTitle) { + return [super accessibilityLabel]; + } + + if ([_accessibilityLabelExplicitValue length]) { + return _accessibilityLabelExplicitValue; + } + + NSString *titleLabel; + id stateTitle = _nontransformedTitles[@(self.state)]; + if ([stateTitle isKindOfClass:[NSAttributedString class]]) { + titleLabel = [(NSAttributedString *)stateTitle string]; + } else if ([stateTitle isKindOfClass:[NSString class]]) { + titleLabel = stateTitle; + } + if ([titleLabel length]) { + return titleLabel; + } + + id normalTitle = _nontransformedTitles[@(UIControlStateNormal)]; + if ([normalTitle isKindOfClass:[NSAttributedString class]]) { + titleLabel = [(NSAttributedString *)normalTitle string]; + } else if ([normalTitle isKindOfClass:[NSString class]]) { + titleLabel = normalTitle; + } + if ([titleLabel length]) { + return titleLabel; + } + + NSString *label = [super accessibilityLabel]; + if ([label length]) { + return label; + } + + return nil; +} + +- (UIAccessibilityTraits)accessibilityTraits { + if (self.accessibilityTraitsIncludesButton) { + return [super accessibilityTraits] | UIAccessibilityTraitButton; + } + return [super accessibilityTraits]; +} + +#pragma mark - Ink + +- (MDCInkStyle)inkStyle { + return _inkView.inkStyle; +} + +- (void)setInkStyle:(MDCInkStyle)inkStyle { + _inkView.inkStyle = inkStyle; + self.rippleView.rippleStyle = + (inkStyle == MDCInkStyleUnbounded) ? MDCRippleStyleUnbounded : MDCRippleStyleBounded; +} + +- (void)setRippleStyle:(MDCRippleStyle)rippleStyle { + _rippleStyle = rippleStyle; + + self.rippleView.rippleStyle = rippleStyle; +} + +- (UIColor *)inkColor { + return _inkView.inkColor; +} + +- (void)setInkColor:(UIColor *)inkColor { + _inkView.inkColor = inkColor; + [self.rippleView setRippleColor:inkColor forState:MDCRippleStateHighlighted]; +} + +- (void)setRippleColor:(UIColor *)rippleColor { + _rippleColor = rippleColor ?: [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha]; + [self.rippleView setRippleColor:_rippleColor forState:MDCRippleStateHighlighted]; +} + +- (CGFloat)inkMaxRippleRadius { + return _inkMaxRippleRadius; +} + +- (void)setInkMaxRippleRadius:(CGFloat)inkMaxRippleRadius { + _inkMaxRippleRadius = inkMaxRippleRadius; + _inkView.maxRippleRadius = inkMaxRippleRadius; + self.rippleView.maximumRadius = inkMaxRippleRadius; +} + +- (void)setRippleMaximumRadius:(CGFloat)rippleMaximumRadius { + _rippleMaximumRadius = rippleMaximumRadius; + + self.rippleView.maximumRadius = rippleMaximumRadius; +} + +- (void)setInkViewOffset:(CGSize)inkViewOffset { + _inkViewOffset = inkViewOffset; + [self setNeedsLayout]; +} + +- (void)setRippleEdgeInsets:(UIEdgeInsets)rippleEdgeInsets { + _rippleEdgeInsets = rippleEdgeInsets; + + [self setNeedsLayout]; +} + +- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior { + _enableRippleBehavior = enableRippleBehavior; + + if (enableRippleBehavior) { + [self.inkView removeFromSuperview]; + [self insertSubview:self.rippleView belowSubview:self.imageView]; + } else { + [self.rippleView removeFromSuperview]; + [self insertSubview:self.inkView belowSubview:self.imageView]; + } +} + +#pragma mark - Shadows + +- (void)animateButtonToHeightForState:(UIControlState)state { + CGFloat newElevation = [self elevationForState:state]; + if (MDCCGFloatEqual(self.mdc_currentElevation, newElevation)) { + return; + } + _currentElevation = newElevation; + [CATransaction begin]; + [CATransaction setAnimationDuration:MDCButtonAnimationDuration]; + if (gEnablePerformantShadow) { + [self updateShadow]; + } else { + self.layer.elevation = newElevation; + } + [CATransaction commit]; + [self mdc_elevationDidChange]; +} + +#pragma mark - BackgroundColor + +- (void)setBackgroundColor:(nullable UIColor *)backgroundColor { + // Since setBackgroundColor can be called in the initializer we need to optionally build the dict. + if (!_backgroundColors) { + _backgroundColors = [NSMutableDictionary dictionary]; + } + _backgroundColors[@(UIControlStateNormal)] = backgroundColor; + [self updateBackgroundColor]; +} + +- (UIColor *)backgroundColor { + return gEnablePerformantShadow ? _shapedLayer.shapedBackgroundColor + : self.layer.shapedBackgroundColor; +} + +- (UIColor *)backgroundColorForState:(UIControlState)state { + // If the `.highlighted` flag is set, turn off the `.disabled` flag + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + state = state & ~UIControlStateDisabled; + } + + return _backgroundColors[@(state)] ?: _backgroundColors[@(UIControlStateNormal)]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state { + UIControlState storageState = state; + // If the `.highlighted` flag is set, turn off the `.disabled` flag + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + storageState = state & ~UIControlStateDisabled; + } + + // Only update the backing dictionary if: + // 1. The `state` argument is the same as the "storage" state, OR + // 2. There is already a value in the "storage" state. + if (storageState == state || _backgroundColors[@(storageState)] != nil) { + _backgroundColors[@(storageState)] = backgroundColor; + [self updateAlphaAndBackgroundColorAnimated:NO]; + } +} + +#pragma mark - Image Tint Color + +- (nullable UIColor *)imageTintColorForState:(UIControlState)state { + return _imageTintColors[@(state)] ?: _imageTintColors[@(UIControlStateNormal)]; +} + +- (void)setImageTintColor:(nullable UIColor *)imageTintColor forState:(UIControlState)state { + _imageTintColors[@(state)] = imageTintColor; + _imageTintStatefulAPIEnabled = YES; + [self updateImageTintColor]; +} + +- (void)updateImageTintColor { + if (!_imageTintStatefulAPIEnabled) { + return; + } + self.imageView.tintColor = [self imageTintColorForState:self.state]; +} + +#pragma mark - Elevations + +- (CGFloat)elevationForState:(UIControlState)state { + NSNumber *elevation = _userElevations[@(state)]; + if (state != UIControlStateNormal && (elevation == nil)) { + elevation = _userElevations[@(UIControlStateNormal)]; + } + if (elevation != nil) { + return (CGFloat)[elevation doubleValue]; + } + return 0; +} + +- (void)setElevation:(CGFloat)elevation forState:(UIControlState)state { + _userElevations[@(state)] = @(elevation); + MDCShadowElevation newElevation = [self elevationForState:self.state]; + // If no change to the current elevation, don't perform updates + if (MDCCGFloatEqual(newElevation, self.mdc_currentElevation)) { + return; + } + _currentElevation = newElevation; + if (gEnablePerformantShadow) { + [self updateShadow]; + } else { + self.layer.elevation = newElevation; + } + [self mdc_elevationDidChange]; + + // The elevation of the normal state controls whether this button is flat or not, and flat buttons + // have different background color requirements than raised buttons. + // TODO(ajsecord): Move to MDCFlatButton and update this comment. + if (state == UIControlStateNormal) { + [self updateAlphaAndBackgroundColorAnimated:NO]; + } +} + +- (void)updateShadow { + if (_shapedLayer.shapeGenerator == nil) { + MDCConfigureShadowForView(self, + [self.shadowsCollection shadowForElevation:self.mdc_currentElevation], + [self shadowColorForState:self.state] ?: MDCShadowColor()); + } else { + MDCConfigureShadowForViewWithPath( + self, [self.shadowsCollection shadowForElevation:self.mdc_currentElevation], + [self shadowColorForState:self.state] ?: MDCShadowColor(), self.layer.shadowPath); + } +} + +#pragma mark - Border Color + +- (UIColor *)borderColorForState:(UIControlState)state { + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + state = state & ~UIControlStateDisabled; + } + return _borderColors[@(state)] ?: _borderColors[@(UIControlStateNormal)]; +} + +- (void)setBorderColor:(UIColor *)borderColor forState:(UIControlState)state { + UIControlState storageState = state; + // If the `.highlighted` flag is set, turn off the `.disabled` flag + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + storageState = state & ~UIControlStateDisabled; + } + + // Only update the backing dictionary if: + // 1. The `state` argument is the same as the "storage" state, OR + // 2. There is already a value in the "storage" state. + if (storageState == state || _borderColors[@(storageState)] != nil) { + _borderColors[@(storageState)] = borderColor; + [self updateBorderColor]; + } +} + +#pragma mark - Border Width + +- (CGFloat)borderWidthForState:(UIControlState)state { + // If the `.highlighted` flag is set, turn off the `.disabled` flag + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + state = state & ~UIControlStateDisabled; + } + NSNumber *borderWidth = _borderWidths[@(state)]; + if (borderWidth != nil) { + return (CGFloat)borderWidth.doubleValue; + } + return (CGFloat)[_borderWidths[@(UIControlStateNormal)] doubleValue]; +} + +- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state { + UIControlState storageState = state; + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + storageState = state & ~UIControlStateDisabled; + } + // Only update the backing dictionary if: + // 1. The `state` argument is the same as the "storage" state, OR + // 2. There is already a value in the "storage" state. + if (storageState == state || _backgroundColors[@(storageState)] != nil) { + _borderWidths[@(state)] = @(borderWidth); + [self updateBorderWidth]; + } +} + +- (void)updateBorderWidth { + NSNumber *width = _borderWidths[@(self.state)]; + if ((width == nil) && self.state != UIControlStateNormal) { + // We fall back to UIControlStateNormal if there is no value for the current state. + width = _borderWidths[@(UIControlStateNormal)]; + } + if (gEnablePerformantShadow) { + _shapedLayer.shapedBorderWidth = (width != nil) ? (CGFloat)width.doubleValue : 0; + } else { + self.layer.shapedBorderWidth = (width != nil) ? (CGFloat)width.doubleValue : 0; + } +} + +#pragma mark - Title Font + +- (nullable UIFont *)titleFontForState:(UIControlState)state { + // If the `.highlighted` flag is set, turn off the `.disabled` flag + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + state = state & ~UIControlStateDisabled; + } + UIFont *font = _fonts[@(state)] ?: _fonts[@(UIControlStateNormal)]; + + if (!font) { + // TODO(#2709): Have a single source of truth for fonts + // Migrate to [UIFont standardFont] when possible + font = [MDCTypography buttonFont]; + } + + if (_mdc_adjustsFontForContentSizeCategory) { + // Dynamic type is enabled so apply scaling + if (font.mdc_scalingCurve) { + font = [font mdc_scaledFontForTraitEnvironment:self]; + } else { + if (self.adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable) { + font = [font mdc_fontSizedForMaterialTextStyle:MDCFontTextStyleButton + scaledForDynamicType:YES]; + } + } + } + return font; +} + +- (void)setTitleFont:(nullable UIFont *)font forState:(UIControlState)state { + UIControlState storageState = state; + // If the `.highlighted` flag is set, turn off the `.disabled` flag + if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { + storageState = state & ~UIControlStateDisabled; + } + + // Only update the backing dictionary if: + // 1. The `state` argument is the same as the "storage" state, OR + // 2. There is already a value in the "storage" state. + if (storageState == state || _fonts[@(storageState)] != nil) { + _fonts[@(storageState)] = font; + [self updateTitleFont]; + } +} + +#pragma mark - MaterialElevation + +- (CGFloat)mdc_currentElevation { + return _currentElevation; +} + +- (MDCShadowsCollection *)shadowsCollection { + if (!_shadowsCollection) { + _shadowsCollection = MDCShadowsCollectionDefault(); + } + return _shadowsCollection; +} + +#pragma mark - Private methods + +/** + The background color that a user would see for this button. If self.backgroundColor is not + transparent, then returns that. Otherwise, returns self.underlyingColorHint. + @note If self.underlyingColorHint is not set, then this method will return nil. + */ +- (UIColor *)effectiveBackgroundColor { + UIColor *backgroundColor = [self backgroundColorForState:self.state]; + if (![self isTransparentColor:backgroundColor]) { + return backgroundColor; + } else { + return self.underlyingColorHint; + } +} + +/** Returns YES if the color is not transparent and is a "dark" color. */ +- (BOOL)isDarkColor:(UIColor *)color { + // TODO: have a components/private/ColorCalculations/MDCColorCalculations.h|m + // return ![self isTransparentColor:color] && [QTMColorGroup luminanceOfColor:color] < 0.5; + return ![self isTransparentColor:color]; +} + +/** Returns YES if the color is transparent (including a nil color). */ +- (BOOL)isTransparentColor:(UIColor *)color { + return !color || [color isEqual:[UIColor clearColor]] || CGColorGetAlpha(color.CGColor) == 0; +} + +- (void)touchDragEnter:(__unused MDCButton *)button forEvent:(UIEvent *)event { + [self handleBeginTouches:event.allTouches]; +} + +- (void)touchDragExit:(__unused MDCButton *)button forEvent:(UIEvent *)event { + CGPoint location = [self locationFromTouches:event.allTouches]; + [self evaporateInkToPoint:location]; +} + +- (void)handleBeginTouches:(NSSet *)touches { + [_inkView startTouchBeganAnimationAtPoint:[self locationFromTouches:touches] completion:nil]; +} + +- (CGPoint)locationFromTouches:(NSSet *)touches { + UITouch *touch = [touches anyObject]; + return [touch locationInView:self]; +} + +- (void)evaporateInkToPoint:(CGPoint)toPoint { + [_inkView startTouchEndedAnimationAtPoint:toPoint completion:nil]; +} + +- (UIBezierPath *)boundingPath { + return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.layer.cornerRadius]; +} + +- (UIEdgeInsets)defaultContentEdgeInsets { + return UIEdgeInsetsMake(8, 16, 8, 16); +} + +- (BOOL)shouldHaveOpaqueBackground { + BOOL isFlatButton = MDCCGFloatIsExactlyZero([self elevationForState:UIControlStateNormal]); + return !isFlatButton; +} + +- (void)updateAlphaAndBackgroundColorAnimated:(BOOL)animated { + void (^animations)(void) = ^{ + // Set super to avoid overwriting _enabledAlpha + super.alpha = self.enabled ? self->_enabledAlpha : self.disabledAlpha; + [self updateBackgroundColor]; + }; + + if (animated) { + [UIView animateWithDuration:MDCButtonAnimationDuration animations:animations]; + } else { + animations(); + } +} + +- (void)updateBackgroundColor { + // When shapeGenerator is unset then self.layer.shapedBackgroundColor sets the layer's + // backgroundColor. Whereas when shapeGenerator is set the sublayer's fillColor is set. + if (gEnablePerformantShadow) { + _shapedLayer.shapedBackgroundColor = [self backgroundColorForState:self.state]; + } else { + self.layer.shapedBackgroundColor = [self backgroundColorForState:self.state]; + } + [self updateDisabledTitleColor]; +} + +- (void)updateDisabledTitleColor { + // We only want to automatically set a disabled title color if the user hasn't already provided a + // value. + if (_hasCustomDisabledTitleColor) { + return; + } + // Disabled buttons have very low opacity, so we full-opacity text color here to make the text + // readable. Also, even for non-flat buttons with opaque backgrounds, the correct background color + // to examine is the underlying color, since disabled buttons are so transparent. + BOOL darkBackground = [self isDarkColor:[self underlyingColorHint]]; + // We call super here to distinguish between automatic title color assignments and that of users. + [super setTitleColor:darkBackground ? [UIColor whiteColor] : [UIColor blackColor] + forState:UIControlStateDisabled]; +} + +- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { + [super setTitleColor:color forState:state]; + if (state == UIControlStateDisabled) { + _hasCustomDisabledTitleColor = color != nil; + if (!_hasCustomDisabledTitleColor) { + [self updateDisabledTitleColor]; + } + } +} + +- (void)updateBorderColor { + UIColor *color = _borderColors[@(self.state)]; + if (!color && self.state != UIControlStateNormal) { + // We fall back to UIControlStateNormal if there is no value for the current state. + color = _borderColors[@(UIControlStateNormal)]; + } + if (gEnablePerformantShadow) { + _shapedLayer.shapedBorderColor = color ?: NULL; + } else { + self.layer.shapedBorderColor = color ?: NULL; + } +} + +- (void)updateTitleFont { + if (!self.enableTitleFontForState) { + return; + } + + self.titleLabel.font = [self titleFontForState:self.state]; + + [self setNeedsLayout]; +} + +- (void)setShapeGenerator:(id)shapeGenerator { + if (!UIEdgeInsetsEqualToEdgeInsets(_visibleAreaInsets, UIEdgeInsetsZero) || + self.centerVisibleArea) { + // When visibleAreaInsets or centerVisibleArea is set, custom shapeGenerater is not allow + // to be set through setter. + return; + } + + [self configureLayerWithShapeGenerator:shapeGenerator]; +} + +- (void)configureLayerWithShapeGenerator:(id)shapeGenerator { + if (shapeGenerator) { + self.layer.shadowPath = nil; + } else { + if (gEnablePerformantShadow) { + MDCConfigureShadowForView( + self, [self.shadowsCollection shadowForElevation:self.mdc_currentElevation], + [self shadowColorForState:self.state] ?: MDCShadowColor()); + } else { + self.layer.shadowPath = [self boundingPath].CGPath; + } + } + + if (gEnablePerformantShadow) { + _shapedLayer.shapeGenerator = shapeGenerator; + } else { + self.layer.shapeGenerator = shapeGenerator; + } + // The imageView is added very early in the lifecycle of a UIButton, therefore we need to move + // the colorLayer behind the imageView otherwise the image will not show. + // Because the inkView needs to go below the imageView, but above the colorLayer + // we need to have the colorLayer be at the back + if (gEnablePerformantShadow) { + [_shapedLayer.colorLayer removeFromSuperlayer]; + if (self.enableRippleBehavior) { + [self.layer insertSublayer:_shapedLayer.colorLayer below:self.rippleView.layer]; + } else { + [self.layer insertSublayer:_shapedLayer.colorLayer below:self.inkView.layer]; + } + } else { + [self.layer.colorLayer removeFromSuperlayer]; + if (self.enableRippleBehavior) { + [self.layer insertSublayer:self.layer.colorLayer below:self.rippleView.layer]; + } else { + [self.layer insertSublayer:self.layer.colorLayer below:self.inkView.layer]; + } + } + [self updateBackgroundColor]; + [self updateInkForShape]; +} + +- (id)shapeGenerator { + if (gEnablePerformantShadow) { + return _shapedLayer.shapeGenerator; + } + return self.layer.shapeGenerator; +} + +- (void)updateInkForShape { + CGRect boundingBox = CGPathGetBoundingBox(gEnablePerformantShadow ? _shapedLayer.shapeLayer.path + : self.layer.shapeLayer.path); + self.inkView.maxRippleRadius = + (CGFloat)(hypot(CGRectGetHeight(boundingBox), CGRectGetWidth(boundingBox)) / 2 + 10); + self.inkView.layer.masksToBounds = NO; + self.rippleView.layer.masksToBounds = NO; +} + +#pragma mark - Dynamic Type + +- (BOOL)mdc_adjustsFontForContentSizeCategory { + return _mdc_adjustsFontForContentSizeCategory; +} + +- (void)mdc_setAdjustsFontForContentSizeCategory:(BOOL)adjusts { + _mdc_adjustsFontForContentSizeCategory = adjusts; + if (_mdc_adjustsFontForContentSizeCategory) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(contentSizeCategoryDidChange:) + name:UIContentSizeCategoryDidChangeNotification + object:nil]; + } else { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIContentSizeCategoryDidChangeNotification + object:nil]; + } + + [self updateTitleFont]; +} + +- (void)contentSizeCategoryDidChange:(__unused NSNotification *)notification { + [self updateTitleFont]; + + [self sizeToFit]; +} + +#pragma mark - Deprecations + +- (void)setUnderlyingColor:(UIColor *)underlyingColor { + [self setUnderlyingColorHint:underlyingColor]; +} + +#pragma mark - Visible area + +- (void)setCenterVisibleArea:(BOOL)centerVisibleArea { + if (_centerVisibleArea == centerVisibleArea) { + return; + } + + _centerVisibleArea = centerVisibleArea; + + if (!centerVisibleArea && UIEdgeInsetsEqualToEdgeInsets(_visibleAreaInsets, UIEdgeInsetsZero)) { + self.shapeGenerator = nil; + + if (_cornerRadiusObserverAdded) { + [self.layer removeObserver:self + forKeyPath:NSStringFromSelector(@selector(cornerRadius)) + context:kKVOContextCornerRadius]; + _cornerRadiusObserverAdded = NO; + } + } else { + UIEdgeInsets visibleAreaInsets = self.visibleAreaInsets; + MDCRectangleShapeGenerator *shapeGenerator = + [self generateShapeWithCornerRadius:self.layer.cornerRadius + visibleAreaInsets:visibleAreaInsets]; + [self configureLayerWithShapeGenerator:shapeGenerator]; + + if (!_cornerRadiusObserverAdded) { + [self.layer addObserver:self + forKeyPath:NSStringFromSelector(@selector(cornerRadius)) + options:NSKeyValueObservingOptionNew + context:kKVOContextCornerRadius]; + _cornerRadiusObserverAdded = YES; + } + } +} + +- (void)setVisibleAreaInsets:(UIEdgeInsets)visibleAreaInsets { + if (UIEdgeInsetsEqualToEdgeInsets(visibleAreaInsets, _visibleAreaInsets)) { + return; + } + + _visibleAreaInsets = visibleAreaInsets; + + if (UIEdgeInsetsEqualToEdgeInsets(visibleAreaInsets, UIEdgeInsetsZero) && + !self.centerVisibleArea) { + self.shapeGenerator = nil; + + if (_cornerRadiusObserverAdded) { + [self.layer removeObserver:self + forKeyPath:NSStringFromSelector(@selector(cornerRadius)) + context:kKVOContextCornerRadius]; + _cornerRadiusObserverAdded = NO; + } + } else { + MDCRectangleShapeGenerator *shapeGenerator = + [self generateShapeWithCornerRadius:self.layer.cornerRadius + visibleAreaInsets:visibleAreaInsets]; + [self configureLayerWithShapeGenerator:shapeGenerator]; + + if (!_cornerRadiusObserverAdded) { + [self.layer addObserver:self + forKeyPath:NSStringFromSelector(@selector(cornerRadius)) + options:NSKeyValueObservingOptionNew + context:kKVOContextCornerRadius]; + _cornerRadiusObserverAdded = YES; + } + } +} + +- (UIEdgeInsets)visibleAreaInsets { + if (!UIEdgeInsetsEqualToEdgeInsets(_visibleAreaInsets, UIEdgeInsetsZero)) { + // Use custom visibleAreaInsets value when user sets it. + return _visibleAreaInsets; + } + + UIEdgeInsets visibleAreaInsets = UIEdgeInsetsZero; + if (self.centerVisibleArea) { + CGSize visibleAreaSize = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; + CGFloat additionalRequiredHeight = + MAX(0, CGRectGetHeight(self.bounds) - visibleAreaSize.height); + CGFloat additionalRequiredWidth = MAX(0, CGRectGetWidth(self.bounds) - visibleAreaSize.width); + visibleAreaInsets.top = ceil(additionalRequiredHeight * 0.5f); + visibleAreaInsets.bottom = additionalRequiredHeight - visibleAreaInsets.top; + visibleAreaInsets.left = ceil(additionalRequiredWidth * 0.5f); + visibleAreaInsets.right = additionalRequiredWidth - visibleAreaInsets.left; + } + + return visibleAreaInsets; +} + +- (UILayoutGuide *)visibleAreaLayoutGuide { + if (!_visibleAreaLayoutGuide) { + _visibleAreaLayoutGuide = [[UILayoutGuide alloc] init]; + [self addLayoutGuide:_visibleAreaLayoutGuide]; + _visibleAreaLayoutGuideView = [[UIView alloc] init]; + _visibleAreaLayoutGuideView.userInteractionEnabled = NO; + [self insertSubview:_visibleAreaLayoutGuideView atIndex:0]; + self.visibleAreaLayoutGuideView.frame = + UIEdgeInsetsInsetRect(self.bounds, self.visibleAreaInsets); + + [_visibleAreaLayoutGuide.leftAnchor + constraintEqualToAnchor:_visibleAreaLayoutGuideView.leftAnchor] + .active = YES; + [_visibleAreaLayoutGuide.rightAnchor + constraintEqualToAnchor:_visibleAreaLayoutGuideView.rightAnchor] + .active = YES; + [_visibleAreaLayoutGuide.topAnchor + constraintEqualToAnchor:_visibleAreaLayoutGuideView.topAnchor] + .active = YES; + [_visibleAreaLayoutGuide.bottomAnchor + constraintEqualToAnchor:_visibleAreaLayoutGuideView.bottomAnchor] + .active = YES; + } + return _visibleAreaLayoutGuide; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (context == kKVOContextCornerRadius) { + if ((!UIEdgeInsetsEqualToEdgeInsets(self.visibleAreaInsets, UIEdgeInsetsZero) || + self.centerVisibleArea) && + self.shapeGenerator) { + MDCRectangleShapeGenerator *shapeGenerator = + [self generateShapeWithCornerRadius:self.layer.cornerRadius + visibleAreaInsets:self.visibleAreaInsets]; + [self configureLayerWithShapeGenerator:shapeGenerator]; + } + } else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +- (MDCRectangleShapeGenerator *)generateShapeWithCornerRadius:(CGFloat)cornerRadius + visibleAreaInsets:(UIEdgeInsets)visibleAreaInsets { + MDCRectangleShapeGenerator *shapeGenerator = [[MDCRectangleShapeGenerator alloc] init]; + MDCCornerTreatment *cornerTreatment = + [[MDCRoundedCornerTreatment alloc] initWithRadius:cornerRadius]; + [shapeGenerator setCorners:cornerTreatment]; + shapeGenerator.topLeftCornerOffset = CGPointMake(visibleAreaInsets.left, visibleAreaInsets.top); + shapeGenerator.topRightCornerOffset = + CGPointMake(-visibleAreaInsets.right, visibleAreaInsets.top); + shapeGenerator.bottomLeftCornerOffset = + CGPointMake(visibleAreaInsets.left, -visibleAreaInsets.bottom); + shapeGenerator.bottomRightCornerOffset = + CGPointMake(-visibleAreaInsets.right, -visibleAreaInsets.bottom); + return shapeGenerator; +} + +#pragma mark Multi-line Minimum And Maximum Sizing Inference + +- (BOOL)shouldInferMinimumAndMaximumSize { + return (self.inferMinimumAndMaximumSizeWhenMultiline && self.titleLabel.numberOfLines != 1 && + self.titleLabel.text.length > 0); +} + +- (void)setInferMinimumAndMaximumSizeWhenMultiline:(BOOL)inferMinimumAndMaximumSizeWhenMultiline { + _inferMinimumAndMaximumSizeWhenMultiline = inferMinimumAndMaximumSizeWhenMultiline; + if (!_inferMinimumAndMaximumSizeWhenMultiline) { + self.minimumSize = CGSizeZero; + self.maximumSize = CGSizeZero; + } + [self setNeedsLayout]; + // Call -layoutIfNeeded in addition to -setNeedsLayout. If in a Manual Layout environment, the + // client will probably call -sizeToFit: after enabling this flag, like the docs suggest. We want + // the pending layout pass to happen before they are able to do that, so minimumSize and + // maximumSize are already set when it happens. + [self layoutIfNeeded]; +} + +- (void)inferMinimumAndMaximumSize { + CGSize buttonSize = self.bounds.size; + CGSize sizeShrunkFromVisibleAreaInsets = + CGSizeShrinkWithInsets(buttonSize, self.visibleAreaInsets); + CGSize sizeShrunkFromContentEdgeInsets = + CGSizeShrinkWithInsets(sizeShrunkFromVisibleAreaInsets, self.contentEdgeInsets); + CGSize boundingSizeForLabel = sizeShrunkFromContentEdgeInsets; + if ([self imageForState:self.state]) { + boundingSizeForLabel.width -= CGRectGetWidth(self.imageView.frame); + } + boundingSizeForLabel.height = CGFLOAT_MAX; + CGSize sizeThatFitsLabel = [self.titleLabel sizeThatFits:boundingSizeForLabel]; + CGSize sizeShrunkFromContentEdgeInsetsWithNewHeight = + CGSizeMake(sizeShrunkFromContentEdgeInsets.width, sizeThatFitsLabel.height); + CGSize sizeExpandedFromContentEdgeInsets = + CGSizeExpandWithInsets(sizeShrunkFromContentEdgeInsetsWithNewHeight, self.contentEdgeInsets); + CGSize sizeExpandedFromVisibleAreaInsets = + CGSizeExpandWithInsets(sizeExpandedFromContentEdgeInsets, self.visibleAreaInsets); + self.minimumSize = sizeExpandedFromVisibleAreaInsets; + self.maximumSize = sizeExpandedFromVisibleAreaInsets; + + if (!CGSizeEqualToSize(sizeExpandedFromVisibleAreaInsets, + self.lastRecordedIntrinsicContentSize)) { + [self invalidateIntrinsicContentSize]; + } +} + +#pragma mark - Performant Shadow Toggle + ++ (void)setEnablePerformantShadow:(BOOL)enable { + gEnablePerformantShadow = enable; +} + ++ (BOOL)enablePerformantShadow { + return gEnablePerformantShadow; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h new file mode 100644 index 00000000..09197e13 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h @@ -0,0 +1,49 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCButton.h" + +#pragma mark - Soon to be deprecated + +/** + A "flat" MDCButton. + + Flat buttons should be considered the default button. They do not have their own background color, + do not raise when touched, and have uppercased text to indicate to the user that they are buttons. + Flat buttons should be used in most situations requiring a button. For layouts with many UI + elements in which a flat button might get visually lost, consider using a MDCRaisedButton instead. + + @warning This class will be deprecated soon. Consider using @c MDCTextButtonThemer with an + @c MDCButton instead. + + @see https://material.io/go/design-buttons#buttons-flat-buttons + */ +@interface MDCFlatButton : MDCButton + +@end + +@interface MDCFlatButton (ToBeDeprecated) + +/** + Use an opaque background color (default is NO). + + Flat buttons normally have a transparent background and blend seamlessly with their underlying + color, but occasionally a flat button with an opaque background will be required. Consider using + a raised button instead if possible. + */ +@property(nonatomic) BOOL hasOpaqueBackground; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m new file mode 100644 index 00000000..d979cae4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m @@ -0,0 +1,47 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFlatButton.h" + +#import "private/MDCButton+Subclassing.h" +#import "MDCButton.h" +#import "MaterialShadowElevations.h" + +@interface MDCFlatButton () +@property(nonatomic) BOOL hasOpaqueBackground; +@end + +@implementation MDCFlatButton + ++ (void)initialize { + // Default background colors. + [MDCFlatButton.appearance setBackgroundColor:[UIColor clearColor] forState:UIControlStateNormal]; + [MDCFlatButton.appearance setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [MDCFlatButton.appearance setElevation:MDCShadowElevationNone forState:UIControlStateNormal]; + [MDCFlatButton.appearance setElevation:MDCShadowElevationNone forState:UIControlStateHighlighted]; + MDCFlatButton.appearance.inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.06]; +} + +#pragma mark - MDCButton Subclassing + +- (void)setHasOpaqueBackground:(BOOL)hasOpaqueBackground { + _hasOpaqueBackground = hasOpaqueBackground; + [self updateBackgroundColor]; +} + +- (BOOL)shouldHaveOpaqueBackground { + return [super shouldHaveOpaqueBackground] || self.hasOpaqueBackground; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h new file mode 100644 index 00000000..8b7a360e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h @@ -0,0 +1,49 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFloatingButton.h" + +/** + This category is used to animate @c MDCFloatingButton instances, to expand or + collapse. + */ +@interface MDCFloatingButton (Animation) + +/** + Expand this button to its unscaled (normal) size. + + @param animated YES if the size change should be animated. + @param completion a completion block to call after the size change is complete. + + @note This method will modify the transform property of the button. Apple's documentation about + UIView frames and transforms states that whenever the transform is not the identity + transform, the frame is undefined and should be ignored. + https://developer.apple.com/documentation/uikit/uiview/1622621-frame + */ +- (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion; + +/** + Collapses this button so that it becomes smaller than 0.1% of its normal size. + + @param animated YES if the size change should be animated. + @param completion a completion block to call after the size change is complete. + + @note This method will modify the transform property of the button. Apple's documentation about + UIView frames and transforms states that whenever the transform is not the identity + transform, the frame is undefined and should be ignored. + https://developer.apple.com/documentation/uikit/uiview/1622621-frame + */ +- (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m new file mode 100644 index 00000000..0aedcdf5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m @@ -0,0 +1,285 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFloatingButton+Animation.h" +#import "MDCFloatingButton.h" + +#if TARGET_IPHONE_SIMULATOR +float UIAnimationDragCoefficient(void); // Private API for simulator animation speed +#endif + +static NSString *const kMDCFloatingButtonTransformKey = @"kMDCFloatingButtonTransformKey"; +static NSString *const kMDCFloatingButtonOpacityKey = @"kMDCFloatingButtonOpacityKey"; + +// By using a power of 2 (2^-12), we can reduce rounding errors during transform multiplication +static const CGFloat kMDCFloatingButtonTransformScale = (CGFloat)0.000244140625; + +static const NSTimeInterval kMDCFloatingButtonEnterDuration = 0.270; +static const NSTimeInterval kMDCFloatingButtonExitDuration = 0.180; + +static const NSTimeInterval kMDCFloatingButtonEnterIconDuration = 0.180; +static const NSTimeInterval kMDCFloatingButtonEnterIconOffset = 0.090; +static const NSTimeInterval kMDCFloatingButtonExitIconDuration = 0.135; +static const NSTimeInterval kMDCFloatingButtonExitIconOffset = 0; + +static const NSTimeInterval kMDCFloatingButtonOpacityDuration = 0.015; +static const NSTimeInterval kMDCFloatingButtonOpacityEnterOffset = 0.030; +static const NSTimeInterval kMDCFloatingButtonOpacityExitOffset = 0.150; + +@implementation MDCFloatingButton (Animation) + ++ (CATransform3D)collapseTransform { + return CATransform3DMakeScale(kMDCFloatingButtonTransformScale, kMDCFloatingButtonTransformScale, + 1); +} + ++ (CATransform3D)expandTransform { + return CATransform3DInvert([MDCFloatingButton collapseTransform]); +} + ++ (CABasicAnimation *)animationWithKeypath:(nonnull NSString *)keyPath + toValue:(nonnull id)toValue + fromValue:(nullable id)fromValue + timingFunction:(nonnull CAMediaTimingFunction *)timingFunction + fillMode:(nonnull NSString *)fillMode + duration:(NSTimeInterval)duration + beginOffset:(NSTimeInterval)beginOffset { + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath]; + animation.toValue = toValue; + animation.fromValue = fromValue; + animation.timingFunction = timingFunction; + animation.fillMode = fillMode; + animation.removedOnCompletion = NO; + animation.duration = duration; + if (fabs(beginOffset) > DBL_EPSILON) { + animation.beginTime = CACurrentMediaTime() + beginOffset; + } + +#if TARGET_IPHONE_SIMULATOR + animation.duration *= [self fab_dragCoefficient]; + if (fabs(beginOffset) > DBL_EPSILON) { + animation.beginTime = CACurrentMediaTime() + (beginOffset * [self fab_dragCoefficient]); + } +#endif + + return animation; +} + +#if TARGET_IPHONE_SIMULATOR ++ (float)fab_dragCoefficient { + if (&UIAnimationDragCoefficient) { + float coeff = UIAnimationDragCoefficient(); + if (coeff > 1) { + return coeff; + } + } + return 1; +} +#endif + +- (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion { +// The typical approach to adjusting the iPad's pointer frame is to invalidate the relevant +// pointer interactions. This was attempted, but, as you can see in go/mdc-fab-pointer-bug, +// invalidating the interaction while a transform animation is happening causes undesired behavior. +// Because of this, we instead temporarily disable pointer interaction for the button while it +// animates and reenable (if previously enabled) once the animation has ended. +#ifdef __IPHONE_13_4 +#if !TARGET_OS_TV + BOOL wasPointerInteractionEnabled = NO; + if (@available(iOS 13.4, *)) { + if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { + wasPointerInteractionEnabled = self.pointerInteractionEnabled; + self.pointerInteractionEnabled = NO; + } + } +#endif // !TARGET_OS_TV +#endif // __IPHONE_13_4 + void (^expandActions)(void) = ^{ + self.layer.transform = + CATransform3DConcat(self.layer.transform, [MDCFloatingButton expandTransform]); + self.layer.opacity = 1; + self.imageView.layer.transform = + CATransform3DConcat(self.imageView.layer.transform, [MDCFloatingButton expandTransform]); + [self.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; + [self.layer removeAnimationForKey:kMDCFloatingButtonOpacityKey]; + [self.imageView.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; +#ifdef __IPHONE_13_4 +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { + self.pointerInteractionEnabled = wasPointerInteractionEnabled; + } + } +#endif // !TARGET_OS_TV +#endif // __IPHONE_13_4 + if (completion) { + completion(); + } + }; + + if (animated) { + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [CATransaction setCompletionBlock:expandActions]; + + CABasicAnimation *overallScaleAnimation = [MDCFloatingButton + animationWithKeypath:@"transform" + toValue:[NSValue + valueWithCATransform3D:CATransform3DConcat( + self.layer.transform, + [MDCFloatingButton expandTransform])] + fromValue:nil + timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:0:0:(float)0.2:1] + fillMode:kCAFillModeForwards + duration:kMDCFloatingButtonEnterDuration + beginOffset:0]; + [self.layer addAnimation:overallScaleAnimation forKey:kMDCFloatingButtonTransformKey]; + + CALayer *iconPresentationLayer = self.imageView.layer.presentationLayer; + if (iconPresentationLayer) { + // Transform from a scale of 0, up to the icon view's current (animated) transform + CALayer *presentationLayer = self.layer.presentationLayer; + NSValue *fromValue = + presentationLayer ? [NSValue valueWithCATransform3D:CATransform3DConcat( + presentationLayer.transform, + CATransform3DMakeScale(0, 0, 1))] + : nil; + + // clang-format off + CAMediaTimingFunction *iconScaleTimingFunction = + [[CAMediaTimingFunction alloc] initWithControlPoints:0:0:(float)0.2:1]; + // clang-format on + CABasicAnimation *iconScaleAnimation = [MDCFloatingButton + animationWithKeypath:@"transform" + toValue:[NSValue valueWithCATransform3D:iconPresentationLayer.transform] + fromValue:fromValue + timingFunction:iconScaleTimingFunction + fillMode:kCAFillModeBoth + duration:kMDCFloatingButtonEnterIconDuration + beginOffset:kMDCFloatingButtonEnterIconOffset]; + + [self.imageView.layer addAnimation:iconScaleAnimation forKey:kMDCFloatingButtonTransformKey]; + } + + CABasicAnimation *opacityAnimation = [MDCFloatingButton + animationWithKeypath:@"opacity" + toValue:[NSNumber numberWithInt:1] + fromValue:nil + timingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] + fillMode:kCAFillModeForwards + duration:kMDCFloatingButtonOpacityDuration + beginOffset:kMDCFloatingButtonOpacityEnterOffset]; + [self.layer addAnimation:opacityAnimation forKey:kMDCFloatingButtonOpacityKey]; + + [CATransaction commit]; + } else { + expandActions(); + } +} + +- (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion { +// The typical approach to adjusting the iPad's pointer frame is to invalidate the relevant +// pointer interactions. This was attempted, but, as you can see in go/mdc-fab-pointer-bug, +// invalidating the interaction while a transform animation is happening causes undesired behavior. +// Because of this, we instead temporarily disable pointer interaction for the button while it +// animates and reenable (if previously enabled) once the animation has ended. +#ifdef __IPHONE_13_4 +#if !TARGET_OS_TV + BOOL wasPointerInteractionEnabled = NO; + if (@available(iOS 13.4, *)) { + if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { + wasPointerInteractionEnabled = self.pointerInteractionEnabled; + self.pointerInteractionEnabled = NO; + } + } +#endif // !TARGET_OS_TV +#endif // __IPHONE_13_4 + + void (^collapseActions)(void) = ^{ + self.layer.transform = + CATransform3DConcat(self.layer.transform, [MDCFloatingButton collapseTransform]); + self.layer.opacity = 0; + self.imageView.layer.transform = + CATransform3DConcat(self.imageView.layer.transform, [MDCFloatingButton collapseTransform]); + [self.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; + [self.layer removeAnimationForKey:kMDCFloatingButtonOpacityKey]; + [self.imageView.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; +#ifdef __IPHONE_13_4 +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { + self.pointerInteractionEnabled = wasPointerInteractionEnabled; + } + } +#endif // !TARGET_OS_TV +#endif // __IPHONE_13_4 + if (completion) { + completion(); + } + }; + + if (animated) { + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [CATransaction setCompletionBlock:collapseActions]; + + CABasicAnimation *overallScaleAnimation = [MDCFloatingButton + animationWithKeypath:@"transform" + toValue:[NSValue + valueWithCATransform3D:CATransform3DConcat( + self.layer.transform, + [MDCFloatingButton collapseTransform])] + fromValue:nil + // clang-format off + timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:1:1] + // clang-format on + fillMode:kCAFillModeForwards + duration:kMDCFloatingButtonExitDuration + beginOffset:0]; + + [self.layer addAnimation:overallScaleAnimation forKey:kMDCFloatingButtonTransformKey]; + + // clang-format off + CABasicAnimation *iconScaleAnimation = [MDCFloatingButton + animationWithKeypath:@"transform" + toValue:[NSValue + valueWithCATransform3D:CATransform3DConcat( + self.imageView.layer.transform, + [MDCFloatingButton collapseTransform])] + fromValue:nil + // clang-format off + timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:1:1] + // clang-format on + fillMode:kCAFillModeForwards + duration:kMDCFloatingButtonExitIconDuration + beginOffset:kMDCFloatingButtonExitIconOffset]; + [self.imageView.layer addAnimation:iconScaleAnimation forKey:kMDCFloatingButtonTransformKey]; + + CABasicAnimation *opacityAnimation = [MDCFloatingButton + animationWithKeypath:@"opacity" + toValue:[NSNumber numberWithFloat:0] + fromValue:nil + timingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] + fillMode:kCAFillModeForwards + duration:kMDCFloatingButtonOpacityDuration + beginOffset:kMDCFloatingButtonOpacityExitOffset]; + [self.layer addAnimation:opacityAnimation forKey:kMDCFloatingButtonOpacityKey]; + + [CATransaction commit]; + } else { + collapseActions(); + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h new file mode 100644 index 00000000..a76414c4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h @@ -0,0 +1,275 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "MDCButton.h" + +/** + Shapes for Material Floating buttons. + + The mini size should only be used when required for visual continuity with other elements on the + screen. + */ +typedef NS_ENUM(NSInteger, MDCFloatingButtonShape) { + /** + A 56-point circular button surrounding a 24- or 36-point square icon or short text. + */ + MDCFloatingButtonShapeDefault = 0, + /** + A 40-point circular button surrounding a 24-point square icon or short text. + */ + MDCFloatingButtonShapeMini = 1 +}; + +/** + Size of Material Floating button. + + The expanded mode should only be used when text and an icon are used. + */ +typedef NS_ENUM(NSInteger, MDCFloatingButtonMode) { + /** + The floating button is a circle with its contents centered. + */ + MDCFloatingButtonModeNormal = 0, + + /** + The floating button is a "pill shape" with the image to one side of the title. + */ + MDCFloatingButtonModeExpanded = 1, +}; + +/** + Image location of Material Floating button. + + If the button is @c MDCFloatingButtonModeExpanded this determines where the + text is rendered in relation to the icon. + */ +typedef NS_ENUM(NSInteger, MDCFloatingButtonImageLocation) { + /** + The image of the floating button is on the leading side of the title. + */ + MDCFloatingButtonImageLocationLeading = 0, + + /** + The image of the floating button is on the trailing side of the title. + */ + MDCFloatingButtonImageLocationTrailing = 1, +}; + +/** + A "floating" MDCButton. + + Floating action buttons are circular, float a considerable amount above their parent, have + their own background color, and also raise briefly when touched. Floating action buttons should + only be used rarely, for the main action of a screen. + + @see https://material.io/go/design-buttons#buttons-main-buttons + */ +@interface MDCFloatingButton : MDCButton + +/** + The mode of the floating button can either be .normal (a circle) or .expanded (a pill-shaped + rounded rectangle). In the @c .normal mode, the button should have either an image or a title, + but not both. In the @c .expanded mode, the button should have both an image and a title. + + The @c .normal layout is identical to that of UIButton. The content will be centered (or otherwise + aligned based on the @c contentHorizontalAlignment and @c contentVerticalAlignment properties. In + @c .expanded layout, the image view will be inset from the leading edge (or trailing edge when + @c imageLocation is .trailing). The "bounding box" for the title will be inset from the opposite + edge and separated from @c imageView by @c imageTitleSpace and the title label will be + leading-aligned within this box. In @c .expanded mode, the @c contentVerticalAlignment and + @c contentHorizontalAlignment properties are ignored. + + @note Setting the mode directly is equivalent to calling + @code [self setMode:mode animated:NO] @endcode. + + The default value is @c .normal . + */ +@property(nonatomic, assign) MDCFloatingButtonMode mode; + +/** + The shape of the floating button. + + The default value is decided by the @c -initWithFrame:shape: initializer. + */ +@property(nonatomic, assign) MDCFloatingButtonShape shape; + +/** + Changes the mode (with animation, if desired). + + If animated, the floating button's size will be updated automatically as part of the animation. + Otherwise, the floating button's size will need to be explicitly recalculated after the mode has + changed. + + @see @c mode for more details about the mode value. + */ +- (void)setMode:(MDCFloatingButtonMode)mode animated:(BOOL)animated; + +/** + Changes the mode (with animation, if desired). + + If animated, the floating button's size will be updated automatically as part of the animation. + Otherwise, the floating button's size will need to be explicitly recalculated after the mode has + changed. + + @param animateAlongside An optional block that will be invoked alongside the animation, if + animated, otherwise it will be invoked immediately. + @param completion An optional block that will be invoked upon completion of the animation, if + animated, otherwise it will be invoked immediately. + + @see @c mode for more details about the mode value. + */ +- (void)setMode:(MDCFloatingButtonMode)mode + animated:(BOOL)animated + animateAlongside:(nullable void (^)(void))animateAlongside + completion:(nullable void (^)(BOOL finished))completion; + +/** + The location of the image relative to the title when the floating button is in @c expanded mode. + + The default value is @c .leading . + */ +@property(nonatomic, assign) MDCFloatingButtonImageLocation imageLocation UI_APPEARANCE_SELECTOR; + +/** + The horizontal spacing in points between the @c imageView and @c titleLabel when the button is in + @c .expanded mode. If set to a negative value, the image and title may overlap. + + The default value is 8. + */ +@property(nonatomic, assign) CGFloat imageTitleSpace UI_APPEARANCE_SELECTOR; + +/** + Returns a MDCFloatingButton with default colors and the given @c shape. + + @param shape Button shape. + @return Button with shape. + */ ++ (nonnull instancetype)floatingButtonWithShape:(MDCFloatingButtonShape)shape; + +/** + @return The default floating button size dimension. + */ ++ (CGFloat)defaultDimension; + +/** + @return The mini floating button size dimension. + */ ++ (CGFloat)miniDimension; + +/** + Initializes self to a button with the given @c shape. + + @param frame Button frame. + @param shape Button shape. + @return Button with shape. + */ +- (nonnull instancetype)initWithFrame:(CGRect)frame + shape:(MDCFloatingButtonShape)shape NS_DESIGNATED_INITIALIZER; + +/** + Initializes self to a button with the MDCFloatingButtonShapeDefault shape. + + @param frame Button frame. + @return Button with MDCFloatingButtonShapeDefault shape. + */ +- (nonnull instancetype)initWithFrame:(CGRect)frame; + +/** + Initializes self to a button with the MDCFloatingButtonShapeDefault shape. + + @return Button with MDCFloatingButtonShapeDefault shape. + */ +- (nonnull instancetype)init; + +- (void)setMinimumSize:(CGSize)size NS_UNAVAILABLE; + +/** + Sets the minimum size when the button has the specified @c shape @c mode. + Setting a size of @c CGSizeZero is equivalent to no minimum size. To set a fixed size for a + button, use the same value when setting the minimum and maximum sizes for a @c shape and @c mode + combination. + + @param minimumSize The new minimum size of the button. + @param shape The shape that the size constrains. + @param mode The mode that the size constrains. + */ +- (void)setMinimumSize:(CGSize)minimumSize + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR; + +- (void)setMaximumSize:(CGSize)maximumSize NS_UNAVAILABLE; + +/** + Sets the maximum size when the button has the specified @c shape and @c mode. + Setting a size of @c CGSizeZero is equivalent to no maximum size. To set a fixed size for a + button, use the same value when setting the minimum and maximum sizes for a @c shape and @c mode + combination. + + @param maximumSize The new maximum size of the button. + @param shape The shape that the size constrains. + @param mode The mode that the size constrains. + */ +- (void)setMaximumSize:(CGSize)maximumSize + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR; + +- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets NS_UNAVAILABLE; + +/** + Sets the @c contentEdgeInsets value when the button has the specified @c shape and @c mode. + The behavior of @c contentEdgeInsets is the same as for UIButton. The button will layout its + subviews within the rectangle generated by insetting its @c bounds by @c contentEdgeInsets. + + @param contentEdgeInsets The new content edge insets value. + @param shape The shape for the content edge insets. + @param mode The mode for the content edge insets. + */ +- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR; + +- (void)setHitAreaInsets:(UIEdgeInsets)hitAreaInsets NS_UNAVAILABLE; + +/** + Sets the @c centerVisibleArea value when the button has the specified @c shape and @c mode. + + @param centerVisibleArea The boolean value that determines whether the visible area is centered in + the bounds of the view. + @param shape The floating action button's shape (Default, Mini). + @param mode The floating action button's mode (Normal, Expanded). + */ +- (void)setCenterVisibleArea:(BOOL)centerVisibleArea + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode; + +@end + +@interface MDCFloatingButton (Deprecated) + +/** + Sets the @c hitAreaInsets value when the button has the specified @c shape and @c mode. + + @param hitAreaInsets The new hit area insets value. + @param shape The shape for the hit area insets. + @param mode The mode for the hit area insets. + */ +- (void)setHitAreaInsets:(UIEdgeInsets)hitAreaInsets + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR + __deprecated_msg("Use setCenterVisibleArea:forShape:inMode: instead."); + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m new file mode 100644 index 00000000..894002e3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m @@ -0,0 +1,573 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFloatingButton.h" + +#import "private/MDCFloatingButtonModeAnimator.h" +#import "private/MDCFloatingButtonModeAnimatorDelegate.h" +#import "MDCButton.h" +#import "MaterialShadowElevations.h" + +#import + +static const CGFloat MDCFloatingButtonDefaultDimension = 56; +static const CGFloat MDCFloatingButtonMiniDimension = 40; +static const CGFloat MDCFloatingButtonDefaultImageTitleSpace = 8; +static const UIEdgeInsets internalLayoutInsets = (UIEdgeInsets){0, 16, 0, 24}; + +@interface MDCFloatingButton () + +@property(nonatomic, readonly) + NSMutableDictionary *> + *shapeToModeToMinimumSize; + +@property(nonatomic, readonly) + NSMutableDictionary *> + *shapeToModeToMaximumSize; + +@property(nonatomic, readonly) + NSMutableDictionary *> + *shapeToModeToContentEdgeInsets; + +@property(nonatomic, readonly) + NSMutableDictionary *> + *shapeToModeToHitAreaInsets; + +@property(nonatomic, readonly) + NSMutableDictionary *> + *shapeToModeToCenterVisibleArea; + +@end + +@implementation MDCFloatingButton { + MDCFloatingButtonShape _shape; + + MDCFloatingButtonModeAnimator *_modeAnimator; + // Allows us to perform masking effects during mode animations. + UIView *_titleLabelContainerView; +} + ++ (void)initialize { + [[MDCFloatingButton appearance] setElevation:MDCShadowElevationFABResting + forState:UIControlStateNormal]; + [[MDCFloatingButton appearance] setElevation:MDCShadowElevationFABPressed + forState:UIControlStateHighlighted]; +} + ++ (CGFloat)defaultDimension { + return MDCFloatingButtonDefaultDimension; +} + ++ (CGFloat)miniDimension { + return MDCFloatingButtonMiniDimension; +} + ++ (instancetype)floatingButtonWithShape:(MDCFloatingButtonShape)shape { + return [[[self class] alloc] initWithFrame:CGRectZero shape:shape]; +} + +- (instancetype)init { + return [self initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + return [self initWithFrame:frame shape:MDCFloatingButtonShapeDefault]; +} + +- (instancetype)initWithFrame:(CGRect)frame shape:(MDCFloatingButtonShape)shape { + self = [super initWithFrame:frame]; + if (self) { + _shape = shape; + [self commonMDCFloatingButtonInit]; + // The superclass sets contentEdgeInsets from defaultContentEdgeInsets before the _shape is set. + // Set contentEdgeInsets again to ensure the defaults are for the correct shape. + [self updateShapeAndAllowResize:NO]; + } + return self; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +// https://stackoverflow.com/questions/24458608/convenience-initializer-missing-a-self-call-to-another-initializer +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; +#pragma clang diagnostic pop + if (self) { + // Required to migrate any previously-archived FloatingButtons from .largeIcon shape value + if (@(_shape).integerValue >= 2) { + _shape = MDCFloatingButtonShapeDefault; + } + // Shape must be set first before the common initialization + [self commonMDCFloatingButtonInit]; + + [self updateShapeAndAllowResize:NO]; + } + return self; +} + +- (void)commonMDCFloatingButtonInit { + _imageTitleSpace = MDCFloatingButtonDefaultImageTitleSpace; + + // Create a container view for titleLabel and add the titelLabel to it. This will enable us to + // mask the titleLabel mode animations if desired, while acting effectively as a pass-through for + // the superview layout logic. + _titleLabelContainerView = [[UIView alloc] initWithFrame:self.bounds]; + _titleLabelContainerView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self insertSubview:_titleLabelContainerView belowSubview:self.titleLabel]; + _titleLabelContainerView.userInteractionEnabled = NO; + [_titleLabelContainerView addSubview:self.titleLabel]; + + const CGSize miniNormalSize = + CGSizeMake(MDCFloatingButtonMiniDimension, MDCFloatingButtonMiniDimension); + const CGSize defaultNormalSize = + CGSizeMake(MDCFloatingButtonDefaultDimension, MDCFloatingButtonDefaultDimension); + const CGSize defaultExpandedMinimumSize = CGSizeMake(0, 48); + const CGSize defaultExpandedMaximumSize = CGSizeMake(328, 0); + + // Minimum size values for different shape + mode combinations + NSMutableDictionary *miniShapeMinimumSizeDictionary = + [@{@(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:miniNormalSize]} mutableCopy]; + NSMutableDictionary *defaultShapeMinimumSizeDictionary = [@{ + @(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:defaultNormalSize], + @(MDCFloatingButtonModeExpanded) : [NSValue valueWithCGSize:defaultExpandedMinimumSize], + } mutableCopy]; + _shapeToModeToMinimumSize = [@{ + @(MDCFloatingButtonShapeMini) : miniShapeMinimumSizeDictionary, + @(MDCFloatingButtonShapeDefault) : defaultShapeMinimumSizeDictionary, + } mutableCopy]; + + // Maximum size values for different shape + mode combinations + NSMutableDictionary *miniShapeMaximumSizeDictionary = + [@{@(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:miniNormalSize]} mutableCopy]; + NSMutableDictionary *defaultShapeMaximumSizeDictionary = [@{ + @(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:defaultNormalSize], + @(MDCFloatingButtonModeExpanded) : [NSValue valueWithCGSize:defaultExpandedMaximumSize], + } mutableCopy]; + _shapeToModeToMaximumSize = [@{ + @(MDCFloatingButtonShapeMini) : miniShapeMaximumSizeDictionary, + @(MDCFloatingButtonShapeDefault) : defaultShapeMaximumSizeDictionary, + } mutableCopy]; + + // Content edge insets values for different shape + mode combinations + // .mini shape, .normal mode + const UIEdgeInsets miniNormalContentInsets = UIEdgeInsetsMake(8, 8, 8, 8); + NSMutableDictionary *miniShapeContentEdgeInsetsDictionary = + [@{@(MDCFloatingButtonModeNormal) : [NSValue valueWithUIEdgeInsets:miniNormalContentInsets]} + mutableCopy]; + _shapeToModeToContentEdgeInsets = + [@{@(MDCFloatingButtonShapeMini) : miniShapeContentEdgeInsetsDictionary} mutableCopy]; + + // Hit area insets values for different shape + mode combinations + // .mini shape, .normal mode + const UIEdgeInsets miniNormalHitAreaInset = UIEdgeInsetsMake(-4, -4, -4, -4); + NSMutableDictionary *miniShapeHitAreaInsetsDictionary = [@{ + @(MDCFloatingButtonModeNormal) : [NSValue valueWithUIEdgeInsets:miniNormalHitAreaInset], + } mutableCopy]; + _shapeToModeToHitAreaInsets = [@{ + @(MDCFloatingButtonShapeMini) : miniShapeHitAreaInsetsDictionary, + } mutableCopy]; + + _shapeToModeToCenterVisibleArea = [[NSMutableDictionary alloc] init]; +} + +#pragma mark - UIView + +- (CGSize)sizeThatFits:(__unused CGSize)size { + return [self intrinsicContentSize]; +} + +- (CGSize)intrinsicContentSizeForModeNormal { + switch (_shape) { + case MDCFloatingButtonShapeDefault: + return CGSizeMake(MDCFloatingButtonDefaultDimension, MDCFloatingButtonDefaultDimension); + case MDCFloatingButtonShapeMini: + return CGSizeMake(MDCFloatingButtonMiniDimension, MDCFloatingButtonMiniDimension); + } +} + +- (CGSize)intrinsicContentSizeForModeExpanded { + const CGSize intrinsicTitleSize = + [self.titleLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; + const CGSize intrinsicImageSize = + [self.imageView sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; + CGFloat intrinsicWidth = intrinsicTitleSize.width + intrinsicImageSize.width + + self.imageTitleSpace + internalLayoutInsets.left + + internalLayoutInsets.right + self.contentEdgeInsets.left + + self.contentEdgeInsets.right; + CGFloat intrinsicHeight = MAX(intrinsicTitleSize.height, intrinsicImageSize.height) + + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom + + internalLayoutInsets.top + internalLayoutInsets.bottom; + return CGSizeMake(intrinsicWidth, intrinsicHeight); +} + +- (CGSize)intrinsicContentSize { + CGSize contentSize = CGSizeZero; + if (self.mode == MDCFloatingButtonModeNormal) { + contentSize = [self intrinsicContentSizeForModeNormal]; + } else if (self.mode == MDCFloatingButtonModeExpanded) { + contentSize = [self intrinsicContentSizeForModeExpanded]; + } + + if (self.minimumSize.height > 0) { + contentSize.height = MAX(self.minimumSize.height, contentSize.height); + } + if (self.maximumSize.height > 0) { + contentSize.height = MIN(self.maximumSize.height, contentSize.height); + } + if (self.minimumSize.width > 0) { + contentSize.width = MAX(self.minimumSize.width, contentSize.width); + } + if (self.maximumSize.width > 0) { + contentSize.width = MIN(self.maximumSize.width, contentSize.width); + } + + return contentSize; +} + +/* + Performs custom layout when the FAB is in .expanded mode. Specifically, the layout algorithm is + as follows: + + 1. Inset the bounds by the value of `contentEdgeInsets` and use this as the layout bounds. + 2. Determine the intrinsic sizes of the imageView and titleLabel. + 3. Compute the space remaining for the titleLabel after accounting for the imageView and built-in + alignment guidelines (internalLayoutInsets). + 4. Position the imageView along the leading (or trailing) edge of the button, inset by + internalLayoutInsets.left (flipped for RTL). + 5. Position the titleLabel along the leading edge of its available space. + 6. Apply the imageEdgeInsets and titleEdgeInsets to their respective views. + */ +- (void)layoutSubviews { + // We have to set cornerRadius before laying out subviews so that the boundingPath is correct. + CGRect visibleBounds = UIEdgeInsetsInsetRect(self.bounds, self.visibleAreaInsets); + self.layer.cornerRadius = CGRectGetHeight(visibleBounds) / 2; + [super layoutSubviews]; + + if (self.mode == MDCFloatingButtonModeNormal) { + return; + } + + // Position the imageView and titleView + // + // +------------------------------------+ + // | | | | CEI TOP | | + // |CEI +--+ |+-----+ |CEI| + // | LT ||SP||Title| |RGT| + // | +--+ |+-----+ | | + // | | | | CEI BOT | | + // +------------------------------------+ + // + // (A) The same spacing on either side of the label. + // (SP) The spacing between the image and title + // (CEI) Content Edge Insets + // + // The diagram above assumes an LTR user interface orientation + // and a .leadingIcon imageLocation for this button. + + const CGRect insetBounds = [self insetBoundsForBounds:self.bounds]; + + const CGFloat imageViewWidth = CGRectGetWidth(self.imageView.bounds); + const CGFloat boundsCenterY = CGRectGetMidY(insetBounds); + CGFloat titleWidthAvailable = CGRectGetWidth(insetBounds); + titleWidthAvailable -= imageViewWidth; + titleWidthAvailable -= self.imageTitleSpace; + + const CGFloat availableHeight = CGRectGetHeight(insetBounds); + CGSize titleIntrinsicSize = + [self.titleLabel sizeThatFits:CGSizeMake(titleWidthAvailable, availableHeight)]; + + const CGSize titleSize = CGSizeMake(MAX(0, MIN(titleIntrinsicSize.width, titleWidthAvailable)), + MAX(0, MIN(titleIntrinsicSize.height, availableHeight))); + + CGPoint titleCenter; + CGPoint imageCenter; + BOOL isLTR = + self.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionLeftToRight; + BOOL isLeadingIcon = self.imageLocation == MDCFloatingButtonImageLocationLeading; + + // If we are LTR with a leading image, the image goes on the left. + // If we are RTL with a trailing image, the image goes on the left. + if ((isLTR && isLeadingIcon) || (!isLTR && !isLeadingIcon)) { + const CGFloat imageCenterX = CGRectGetMinX(insetBounds) + (imageViewWidth / 2); + const CGFloat titleCenterX = + CGRectGetMaxX(insetBounds) - titleWidthAvailable + (titleSize.width / 2); + titleCenter = CGPointMake(titleCenterX, boundsCenterY); + imageCenter = CGPointMake(imageCenterX, boundsCenterY); + } + // If we are LTR with a trailing image, the image goes on the right. + // If we are RTL with a leading image, the image goes on the right. + else { + const CGFloat imageCenterX = CGRectGetMaxX(insetBounds) - (imageViewWidth / 2); + const CGFloat titleCenterX = + CGRectGetMinX(insetBounds) + titleWidthAvailable - (titleSize.width / 2); + imageCenter = CGPointMake(imageCenterX, boundsCenterY); + titleCenter = CGPointMake(titleCenterX, boundsCenterY); + } + + self.imageView.center = imageCenter; + self.imageView.frame = UIEdgeInsetsInsetRect(self.imageView.frame, self.imageEdgeInsets); + self.titleLabel.center = titleCenter; + CGRect newBounds = CGRectStandardize(self.titleLabel.bounds); + self.titleLabel.bounds = (CGRect){newBounds.origin, titleSize}; + self.titleLabel.frame = UIEdgeInsetsInsetRect(self.titleLabel.frame, self.titleEdgeInsets); +} + +- (CGRect)insetBoundsForBounds:(CGRect)bounds { + BOOL isLeadingIcon = self.imageLocation == MDCFloatingButtonImageLocationLeading; + UIEdgeInsets adjustedLayoutInsets = + (isLeadingIcon ? internalLayoutInsets : MDFInsetsFlippedHorizontally(internalLayoutInsets)); + return UIEdgeInsetsInsetRect(UIEdgeInsetsInsetRect(bounds, adjustedLayoutInsets), + self.contentEdgeInsets); +} + +#pragma mark - Mode animator + +- (MDCFloatingButtonModeAnimator *)modeAnimator { + if (!_modeAnimator) { + _modeAnimator = + [[MDCFloatingButtonModeAnimator alloc] initWithTitleLabel:self.titleLabel + titleLabelContainerView:_titleLabelContainerView]; + _modeAnimator.delegate = self; + } + return _modeAnimator; +} + +#pragma mark MDCFloatingButtonModeAnimatorDelegate + +- (void)floatingButtonModeAnimatorCommitLayoutChanges:(MDCFloatingButtonModeAnimator *)modeAnimator + mode:(MDCFloatingButtonMode)mode { + [self sizeToFit]; +} + +#pragma mark - Property Setters/Getters + +- (void)setShape:(MDCFloatingButtonShape)shape { + if (_shape == shape) { + return; + } + _shape = shape; + [self updateShapeAndAllowResize:YES]; +} + +- (void)setMode:(MDCFloatingButtonMode)mode { + [self setMode:mode animated:NO animateAlongside:nil completion:nil]; +} + +- (void)setMode:(MDCFloatingButtonMode)mode animated:(BOOL)animated { + [self setMode:mode animated:animated animateAlongside:nil completion:nil]; +} + +- (void)setMode:(MDCFloatingButtonMode)mode + animated:(BOOL)animated + animateAlongside:(void (^)(void))animateAlongside + completion:(void (^)(BOOL finished))completion { + if (_mode == mode) { + if (animateAlongside) { + animateAlongside(); + } + if (completion) { + completion(YES); + } + return; + } + _mode = mode; + + [self updateShapeAndAllowResize:YES]; + + [[self modeAnimator] modeDidChange:mode + animated:animated + animateAlongside:animateAlongside + completion:completion]; +} + +- (void)setMinimumSize:(CGSize)size + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToMinimumSize = self.shapeToModeToMinimumSize[@(shape)]; + if (!modeToMinimumSize) { + modeToMinimumSize = [@{} mutableCopy]; + self.shapeToModeToMinimumSize[@(shape)] = modeToMinimumSize; + } + modeToMinimumSize[@(mode)] = [NSValue valueWithCGSize:size]; + if (shape == _shape && mode == self.mode) { + [self updateShapeAndAllowResize:YES]; + } +} + +- (CGSize)minimumSizeForMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToMinimumSize = self.shapeToModeToMinimumSize[@(_shape)]; + if (!modeToMinimumSize) { + return CGSizeZero; + } + + NSValue *sizeValue = modeToMinimumSize[@(mode)]; + if (sizeValue) { + return [sizeValue CGSizeValue]; + } else { + return CGSizeZero; + } +} + +- (BOOL)updateMinimumSize { + CGSize newSize = [self minimumSizeForMode:self.mode]; + if (CGSizeEqualToSize(newSize, self.minimumSize)) { + return NO; + } + super.minimumSize = newSize; + return YES; +} + +- (void)setMaximumSize:(CGSize)size + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToMaximumSize = self.shapeToModeToMaximumSize[@(shape)]; + if (!modeToMaximumSize) { + modeToMaximumSize = [@{} mutableCopy]; + self.shapeToModeToMaximumSize[@(shape)] = modeToMaximumSize; + } + modeToMaximumSize[@(mode)] = [NSValue valueWithCGSize:size]; + if (shape == _shape && mode == self.mode) { + [self updateShapeAndAllowResize:YES]; + } +} + +- (CGSize)maximumSizeForMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToMaximumSize = self.shapeToModeToMaximumSize[@(_shape)]; + if (!modeToMaximumSize) { + return CGSizeZero; + } + + NSValue *sizeValue = modeToMaximumSize[@(mode)]; + if (sizeValue) { + return [sizeValue CGSizeValue]; + } else { + return CGSizeZero; + } +} + +- (BOOL)updateMaximumSize { + CGSize newSize = [self maximumSizeForMode:self.mode]; + if (CGSizeEqualToSize(newSize, self.maximumSize)) { + return NO; + } + super.maximumSize = newSize; + return YES; +} + +- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToContentEdgeInsets = self.shapeToModeToContentEdgeInsets[@(shape)]; + if (!modeToContentEdgeInsets) { + modeToContentEdgeInsets = [@{} mutableCopy]; + self.shapeToModeToContentEdgeInsets[@(shape)] = modeToContentEdgeInsets; + } + modeToContentEdgeInsets[@(mode)] = [NSValue valueWithUIEdgeInsets:contentEdgeInsets]; + if (shape == _shape && mode == self.mode) { + [self updateShapeAndAllowResize:YES]; + } +} + +- (UIEdgeInsets)contentEdgeInsetsForMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToContentEdgeInsets = self.shapeToModeToContentEdgeInsets[@(_shape)]; + if (!modeToContentEdgeInsets) { + return UIEdgeInsetsZero; + } + + NSValue *insetsValue = modeToContentEdgeInsets[@(mode)]; + if (insetsValue) { + return [insetsValue UIEdgeInsetsValue]; + } else { + return UIEdgeInsetsZero; + } +} + +- (void)updateContentEdgeInsets { + super.contentEdgeInsets = [self contentEdgeInsetsForMode:self.mode]; +} + +- (void)setHitAreaInsets:(UIEdgeInsets)insets + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToHitAreaInsets = self.shapeToModeToHitAreaInsets[@(shape)]; + if (!modeToHitAreaInsets) { + modeToHitAreaInsets = [@{} mutableCopy]; + self.shapeToModeToHitAreaInsets[@(shape)] = modeToHitAreaInsets; + } + modeToHitAreaInsets[@(mode)] = [NSValue valueWithUIEdgeInsets:insets]; + if (shape == _shape && mode == self.mode) { + [self updateShapeAndAllowResize:NO]; + } +} + +- (UIEdgeInsets)hitAreaInsetsForMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToHitAreaInsets = self.shapeToModeToHitAreaInsets[@(_shape)]; + if (!modeToHitAreaInsets) { + return UIEdgeInsetsZero; + } + + NSValue *insetsValue = modeToHitAreaInsets[@(mode)]; + if (insetsValue) { + return [insetsValue UIEdgeInsetsValue]; + } else { + return UIEdgeInsetsZero; + } +} + +- (void)updateHitAreaInsets { + super.hitAreaInsets = [self hitAreaInsetsForMode:self.mode]; +} + +- (void)setCenterVisibleArea:(BOOL)centerVisibleArea + forShape:(MDCFloatingButtonShape)shape + inMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToCenterVisibleArea = self.shapeToModeToCenterVisibleArea[@(shape)]; + if (!modeToCenterVisibleArea) { + modeToCenterVisibleArea = [@{} mutableCopy]; + self.shapeToModeToCenterVisibleArea[@(shape)] = modeToCenterVisibleArea; + } + modeToCenterVisibleArea[@(mode)] = @(centerVisibleArea); + if (shape == _shape && mode == self.mode) { + [self updateShapeAndAllowResize:NO]; + } +} + +- (BOOL)centerVisibleAreaForMode:(MDCFloatingButtonMode)mode { + NSMutableDictionary *modeToCenterVisibleArea = self.shapeToModeToCenterVisibleArea[@(_shape)]; + if (!modeToCenterVisibleArea) { + return NO; + } + + return [modeToCenterVisibleArea[@(mode)] boolValue]; +} + +- (void)updateCenterVisibleArea { + super.centerVisibleArea = [self centerVisibleAreaForMode:self.mode]; +} + +- (void)updateShapeAndAllowResize:(BOOL)allowsResize { + BOOL minimumSizeChanged = [self updateMinimumSize]; + BOOL maximumSizeChanged = [self updateMaximumSize]; + [self updateContentEdgeInsets]; + [self updateHitAreaInsets]; + [self updateCenterVisibleArea]; + + if (allowsResize && (minimumSizeChanged || maximumSizeChanged)) { + [self invalidateIntrinsicContentSize]; + [self setNeedsLayout]; + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h new file mode 100644 index 00000000..4c184f75 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h @@ -0,0 +1,32 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCButton.h" + +#pragma mark - Soon to be deprecated + +/** + A "raised" MDCButton. + + Raised buttons have their own background color, float above their parent slightly, and raise + briefly when touched. Raised buttons should be used when flat buttons would get lost among other + UI elements on the screen. + + @warning This class will be deprecated soon. Consider using @c MDCContainedButtonThemer with an + @c MDCButton instead. + + @see https://material.io/go/design-buttons#buttons-raised-buttons + */ +@interface MDCRaisedButton : MDCButton +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m new file mode 100644 index 00000000..06483389 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m @@ -0,0 +1,28 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRaisedButton.h" + +#import "MaterialShadowElevations.h" + +@implementation MDCRaisedButton + ++ (void)initialize { + [[MDCRaisedButton appearance] setElevation:MDCShadowElevationRaisedButtonResting + forState:UIControlStateNormal]; + [[MDCRaisedButton appearance] setElevation:MDCShadowElevationRaisedButtonPressed + forState:UIControlStateHighlighted]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h new file mode 100644 index 00000000..b9879fdb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h @@ -0,0 +1,19 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCButton.h" +#import "MDCFlatButton.h" +#import "MDCFloatingButton+Animation.h" +#import "MDCFloatingButton.h" +#import "MDCRaisedButton.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h new file mode 100644 index 00000000..ebd3789d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h @@ -0,0 +1,57 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCButton.h" + +@class MDCInkView; + +@interface MDCButton (Subclassing) + +/** Access to the ink view layer. Mainly used for subclasses to override ink properties. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +@property(nonatomic, readonly, strong, nonnull) MDCInkView *inkView; +#pragma clang diagnostic pop + +/** Whether the background color should be opaque. */ +- (BOOL)shouldHaveOpaqueBackground; + +/** Updates the background color based on the button's current configuration. */ +- (void)updateBackgroundColor; + +/** + Should the button raise when touched? + + Default is YES. + */ +@property(nonatomic) BOOL shouldRaiseOnTouch; + +/** The bounding path of the button. The shadow will follow that path. */ +- (nonnull UIBezierPath *)boundingPath; + +@end + +@interface MDCButton () + +/** + A collection of MDCShadow instances each assigned an elevation (in dp). + + To create your own MDCShadowsCollection, please use the provided MDCShadowsCollectionBuilder and + populate it with MDCShadow instances using the provided MDCShadowBuilder. + + Defaults to MDCShadowsCollectionDefault(). + */ +@property(nonatomic, strong, null_resettable) MDCShadowsCollection *shadowsCollection; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h new file mode 100644 index 00000000..203646d3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h @@ -0,0 +1,49 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "MDCFloatingButton.h" + +@protocol MDCFloatingButtonModeAnimatorDelegate; + +/** + Animates an MDCFloatingButton's mode. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCFloatingButtonModeAnimator : NSObject + +- (nonnull instancetype)initWithTitleLabel:(nonnull UILabel *)titleLabel + titleLabelContainerView:(nonnull UIView *)titleLabelContainerView + NS_DESIGNATED_INITIALIZER; + +/** + Informs the animator that the floating button mode has changed. + + If the change was animated, then the animator will initiate the necessary animations to create the + visual effect of the modes animating from one state to the next. + */ +- (void)modeDidChange:(MDCFloatingButtonMode)mode + animated:(BOOL)animated + animateAlongside:(nullable void (^)(void))animateAlongside + completion:(nullable void (^)(BOOL finished))completion; + +/** + The animator uses the delegate to interact with its owning context: the MDCFloatingButton instance. + */ +@property(nonatomic, weak, nullable) id delegate; + +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m new file mode 100644 index 00000000..3771b2d6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m @@ -0,0 +1,206 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFloatingButtonModeAnimator.h" + +#import "MDCFloatingButton.h" +#import "MDCFloatingButtonModeAnimatorDelegate.h" + +#if TARGET_IPHONE_SIMULATOR +UIKIT_EXTERN float UIAnimationDragCoefficient(void); // UIKit private drag coefficient. +#endif + +static CGFloat SimulatorAnimationDragCoefficient(void) { +#if TARGET_IPHONE_SIMULATOR + return UIAnimationDragCoefficient(); +#else + return 1.0; +#endif +} + +typedef struct { + NSTimeInterval duration; + NSTimeInterval titleOpacityDelay; + NSTimeInterval titleOpacityDuration; +} AnimationTiming; + +// go/mdc-fab-expansion-animation +static const AnimationTiming kExpandAnimationTiming = (AnimationTiming){ + .duration = 0.200, + .titleOpacityDelay = 0.083, + .titleOpacityDuration = 0.067, +}; + +// go/mdc-fab-collapse-animation +static const AnimationTiming kCollapseAnimationTiming = (AnimationTiming){ + .duration = 0.167, + .titleOpacityDelay = 0.016, + .titleOpacityDuration = 0.033, +}; + +static const UIViewAnimationOptions kTitleOpacityAnimationOptions = + UIViewAnimationOptionCurveLinear; + +static NSString *const kModeVerticalDriftAnimationKey = @"position.y.fix"; + +@interface MDCFloatingButtonModeAnimator () +@property(nonatomic, strong) UILabel *titleLabel; +@property(nonatomic, strong) UIView *titleLabelContainerView; +@end + +@implementation MDCFloatingButtonModeAnimator + +- (instancetype)initWithTitleLabel:(UILabel *)titleLabel + titleLabelContainerView:(UIView *)titleLabelContainerView { + self = [super init]; + if (self) { + self.titleLabel = titleLabel; + self.titleLabelContainerView = titleLabelContainerView; + } + return self; +} + +- (void)modeDidChange:(MDCFloatingButtonMode)mode + animated:(BOOL)animated + animateAlongside:(nullable void (^)(void))animateAlongside + completion:(nullable void (^)(BOOL finished))completion { + if (!animated) { + self.titleLabelContainerView.clipsToBounds = NO; + if (animateAlongside) { + animateAlongside(); + } + if (completion) { + completion(YES); + } + return; + } + + // Floating button mode animations are relatively rare, so to avoid having the non-animated steady + // state pay any compositing costs due to masking we only enable clipsToBounds for the course of + // the mode animation. The bounds clipping is necessary to achieve the clipped label effect as the + // button expands / collapses. + _titleLabelContainerView.clipsToBounds = YES; + + const BOOL expanding = mode == MDCFloatingButtonModeExpanded; + + // ## Prepare the label for animation + + // Because the titleLabel has an empty frame in the collapsed state, we key this entire animation + // off of the expanded state's frame. When expanding, we can animate the titleLabel directly + // because the destination frame is non-empty. When collapsing, we need to animate a snapshot + // because the titleLabel's destination frame is empty. + UIView *animationTitleLabel; + void (^titleLabelCleanup)(BOOL); + if (expanding) { + animationTitleLabel = self.titleLabel; + animationTitleLabel.alpha = 0; // Start off initially transparent. + titleLabelCleanup = nil; + } else { + animationTitleLabel = [self.titleLabel snapshotViewAfterScreenUpdates:NO]; + animationTitleLabel.frame = self.titleLabel.frame; + [_titleLabelContainerView addSubview:animationTitleLabel]; + titleLabelCleanup = ^(BOOL finished) { + [animationTitleLabel removeFromSuperview]; + }; + } + + // ## Perform the frame animation + + CGRect priorTitleLabelFrame = self.titleLabel.frame; + AnimationTiming timing = expanding ? kExpandAnimationTiming : kCollapseAnimationTiming; + [UIView animateWithDuration:timing.duration + animations:^{ + NSSet *priorTitleAnimationKeys = [NSSet setWithArray:self.titleLabel.layer.animationKeys]; + + // Force the button to adjust its frame in order to generate the default animations. Note + // that this will also animate the title label and any other subviews within the button. + [self.delegate floatingButtonModeAnimatorCommitLayoutChanges:self mode:mode]; + + // If we allowed the title label to animate as a result of the sizeToFit changes, then we + // would see the title expand / collapse its frame in a squishy and undesirable manner. To + // avoid this, we remove any *newly added* animations from the title label before they get + // committed to the render server. The resulting effect is that the title label's frame is + // instantly where it needs to be and we can animate its alpha independently and compensate + // for any vertical drift below. + NSMutableSet *newTitleAnimationKeys = + [NSMutableSet setWithArray:self.titleLabel.layer.animationKeys]; + [newTitleAnimationKeys minusSet:priorTitleAnimationKeys]; + for (NSString *animationKey in newTitleAnimationKeys) { + [self.titleLabel.layer removeAnimationForKey:animationKey]; + } + + if (animateAlongside) { + animateAlongside(); + } + } + completion:^(BOOL finished) { + if (titleLabelCleanup) { + titleLabelCleanup(finished); + } + self.titleLabelContainerView.clipsToBounds = NO; + if (completion) { + completion(finished); + } + }]; + + // ## Compensate for vertical drift + + // As noted above, the default titleLabel frame animations cause an undesired scaling effect of + // the label and so the default animations are removed. The titleLabel's frame is now set to the + // final state of the animation which is generally desired in terms of the titleLabel's size, but + // if the button's height changes then we need to animate the label's y position, otherwise the + // label will appear to be pinned to the top - rather than the center - of the button as the + // button expands / contracts. + // + // We compensate for this effect by creating an additive animation below. The purpose of this + // additive animation is to give the appearance that the label is centered vertically within the + // button over the course of the animation and without making adjustments to the model layer. + + CGRect newTitleLabelFrame = self.titleLabel.frame; + CGFloat centerYDelta = CGRectGetMidY(newTitleLabelFrame) - CGRectGetMidY(priorTitleLabelFrame); + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; + animation.additive = YES; + animation.timingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + if (expanding) { + // When expanding, we initially compensate for the vertical shift and then gradually reduce that + // compensation as the animation progresses. This is because the label's model layer is already + // shifted relative to the original center position. + animation.fromValue = @(-centerYDelta); + animation.toValue = @0; + } else { + // When contracting we undo the effect of the expansion by adding the compensation gradually + // back to the label. + animation.fromValue = @0; + animation.toValue = @(centerYDelta); + } + animation.duration = timing.duration * SimulatorAnimationDragCoefficient(); + [animationTitleLabel.layer addAnimation:animation forKey:kModeVerticalDriftAnimationKey]; + + // ## Animate the title opacity + + [UIView animateWithDuration:timing.titleOpacityDuration + delay:timing.titleOpacityDelay + options:kTitleOpacityAnimationOptions + animations:^{ + if (mode == MDCFloatingButtonModeExpanded) { + animationTitleLabel.alpha = 1; + } else { + animationTitleLabel.alpha = 0; + } + } + completion:nil]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h new file mode 100644 index 00000000..d7d2d71e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h @@ -0,0 +1,35 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "MDCFloatingButton.h" + +@class MDCFloatingButtonModeAnimator; + +/** + MDCFloatingButtonModeAnimator uses this delegate to interact with its owning context. + */ +@protocol MDCFloatingButtonModeAnimatorDelegate +@required + +/** + Asks the receiver to commit any layout changes relevant to the mode change. + */ +- (void)floatingButtonModeAnimatorCommitLayoutChanges: + (nonnull MDCFloatingButtonModeAnimator *)modeAnimator + mode:(MDCFloatingButtonMode)mode; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h new file mode 100644 index 00000000..e03c5f3f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h @@ -0,0 +1,169 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialElevation.h" +#import "MaterialInk.h" +#import "MaterialRipple.h" +#import "MaterialShadowLayer.h" + +@protocol MDCShapeGenerating; + +@interface MDCCard : UIControl + +/** + The corner radius for the card + Default is set to 4. + */ +@property(nonatomic, assign) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; + +/** + The inkView for the card that is initiated on tap + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +@property(nonatomic, readonly, strong, nonnull) MDCInkView *inkView; +#pragma clang diagnostic pop + +/** + The rippleView for the card that is initiated on tap. The ripple view is the successor of ink + view, and can be used by setting `enableRippleBehavior` to YES after initializing the card. + */ +@property(nonatomic, readonly, strong, nonnull) MDCStatefulRippleView *rippleView; + +/** + This property defines if a card as a whole should be interactable or not. + What this means is that when isInteractable is set to NO, there will be no ink ripple and + no change in shadow elevation when tapped or selected. Also the card container itself will not be + tappable, but any of its subviews will still be tappable. + + Default is set to YES. + + Important: Our specification for cards explicitly define a card as being an interactable component. + Therefore, this property should be set to NO *only if* there are other interactable items within + the card's content, such as buttons or other tappable controls. + */ +@property(nonatomic, getter=isInteractable) IBInspectable BOOL interactable; + +/** + By setting this property to YES, you will enable and use inkView's successor rippleView as the + main view to provide visual feedback for taps. It is recommended to set this property right after + initializing the card. + + Defaults to NO. + */ +@property(nonatomic, assign) BOOL enableRippleBehavior; + +/** + Sets the shadow elevation for an UIControlState state + + @param shadowElevation The shadow elevation + @param state UIControlState the card state + */ +- (void)setShadowElevation:(MDCShadowElevation)shadowElevation + forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the shadow elevation for an UIControlState state + + If no elevation has been set for a state, the value for UIControlStateNormal will be returned. + Default value for UIControlStateNormal is 1 + Default value for UIControlStateHighlighted is 8 + + @param state UIControlState the card state + @return The shadow elevation for the requested state. + */ +- (MDCShadowElevation)shadowElevationForState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the border width for an UIControlState state + + @param borderWidth The border width + @param state UIControlState the card state + */ +- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the border width for an UIControlState state + + If no border width has been set for a state, the value for UIControlStateNormal will be returned. + Default value for UIControlStateNormal is 0 + + @param state UIControlState the card state + @return The border width for the requested state. + */ +- (CGFloat)borderWidthForState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the border color for an UIControlState state + + @param borderColor The border color + @param state UIControlState the card state + */ +- (void)setBorderColor:(nullable UIColor *)borderColor + forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the border color for an UIControlState state + + If no border color has been set for a state, it will check the value of UIControlStateNormal. + If that value also isn't set, then nil will be returned. + + @param state UIControlState the card state + @return The border color for the requested state. + */ +- (nullable UIColor *)borderColorForState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the shadow color for an UIControlState state + + @param shadowColor The shadow color + @param state UIControlState the card state + */ +- (void)setShadowColor:(nullable UIColor *)shadowColor + forState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the shadow color for an UIControlState state + + If no color has been set for a state, the value for MDCCardViewStateNormal will be returned. + Default value for UIControlStateNormal is blackColor + + @param state UIControlState the card state + @return The shadow color for the requested state. + */ +- (nullable UIColor *)shadowColorForState:(UIControlState)state UI_APPEARANCE_SELECTOR; + +/** + A block that is invoked when the @c MDCCard receives a call to @c + traitCollectionDidChange:. The block is called after the call to the superclass. + */ +@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) + (MDCCard *_Nonnull card, UITraitCollection *_Nullable previousTraitCollection); + +/* + The shape generator used to define the card's shape. + When set, layer properties such as cornerRadius and other layer properties are nullified/zeroed. + If a layer property is explicitly set after the shapeGenerator has been set, it will lead to + unexpected behavior. + + When the shapeGenerator is nil, MDCCard will use the default underlying layer with + its default settings. + + Default value for shapeGenerator is nil. + */ +@property(nullable, nonatomic, strong) id shapeGenerator; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m new file mode 100644 index 00000000..6c48571d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m @@ -0,0 +1,379 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCard.h" + +#import "MaterialElevation.h" +#import "MaterialInk.h" +#import "MaterialRipple.h" +#import "MaterialShadowElevations.h" +#import "MaterialShadowLayer.h" +#import "MaterialShapes.h" +#import "MaterialMath.h" + +static const CGFloat MDCCardShadowElevationNormal = 1; +static const CGFloat MDCCardShadowElevationHighlighted = 8; +static const CGFloat MDCCardCornerRadiusDefault = 4; +static const BOOL MDCCardIsInteractableDefault = YES; + +@interface MDCCard () +@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; +@end + +@implementation MDCCard { + NSMutableDictionary *_shadowElevations; + NSMutableDictionary *_shadowColors; + NSMutableDictionary *_borderWidths; + NSMutableDictionary *_borderColors; + UIColor *_backgroundColor; + CGPoint _lastTouch; +} + +@dynamic layer; +@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation; +@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock; + ++ (Class)layerClass { + return [MDCShapedShadowLayer class]; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self) { + [self commonMDCCardInit]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCCardInit]; + } + return self; +} + +- (void)commonMDCCardInit { + self.layer.cornerRadius = MDCCardCornerRadiusDefault; + _interactable = MDCCardIsInteractableDefault; + _mdc_overrideBaseElevation = -1; + + if (_inkView == nil) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + _inkView = [[MDCInkView alloc] initWithFrame:self.bounds]; +#pragma clang diagnostic pop + _inkView.autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + _inkView.usesLegacyInkRipple = NO; + _inkView.layer.zPosition = FLT_MAX; + [self addSubview:_inkView]; + } + + if (_shadowElevations == nil) { + _shadowElevations = [NSMutableDictionary dictionary]; + _shadowElevations[@(UIControlStateNormal)] = @(MDCCardShadowElevationNormal); + _shadowElevations[@(UIControlStateHighlighted)] = @(MDCCardShadowElevationHighlighted); + } + + if (_shadowColors == nil) { + _shadowColors = [NSMutableDictionary dictionary]; + _shadowColors[@(UIControlStateNormal)] = UIColor.blackColor; + } + + if (_borderColors == nil) { + _borderColors = [NSMutableDictionary dictionary]; + } + + if (_borderWidths == nil) { + _borderWidths = [NSMutableDictionary dictionary]; + } + + if (_backgroundColor == nil) { + _backgroundColor = UIColor.whiteColor; + } + + [self updateShadowElevation]; + [self updateShadowColor]; + [self updateBorderWidth]; + [self updateBorderColor]; + [self updateBackgroundColor]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + if (!self.layer.shapeGenerator) { + self.layer.shadowPath = [self boundingPath].CGPath; + } + + [self updateShadowColor]; + [self updateBackgroundColor]; + [self updateBorderColor]; +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + + if (self.traitCollectionDidChangeBlock) { + self.traitCollectionDidChangeBlock(self, previousTraitCollection); + } +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + self.layer.cornerRadius = cornerRadius; + [self setNeedsLayout]; +} + +- (CGFloat)cornerRadius { + return self.layer.cornerRadius; +} + +- (UIBezierPath *)boundingPath { + CGFloat cornerRadius = self.cornerRadius; + return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; +} + +- (MDCShadowElevation)shadowElevationForState:(UIControlState)state { + NSNumber *elevation = _shadowElevations[@(state)]; + if (state != UIControlStateNormal && elevation == nil) { + elevation = _shadowElevations[@(UIControlStateNormal)]; + } + if (elevation != nil) { + return (CGFloat)[elevation doubleValue]; + } + return 0; +} + +- (void)setShadowElevation:(MDCShadowElevation)shadowElevation forState:(UIControlState)state { + _shadowElevations[@(state)] = @(shadowElevation); + + [self updateShadowElevation]; +} + +- (void)updateShadowElevation { + CGFloat elevation = [self shadowElevationForState:self.state]; + if (!MDCCGFloatEqual(((MDCShadowLayer *)self.layer).elevation, elevation)) { + if (!self.layer.shapeGenerator) { + self.layer.shadowPath = [self boundingPath].CGPath; + } + [(MDCShadowLayer *)self.layer setElevation:elevation]; + [self mdc_elevationDidChange]; + } +} + +- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state { + _borderWidths[@(state)] = @(borderWidth); + + [self updateBorderWidth]; +} + +- (void)updateBorderWidth { + CGFloat borderWidth = [self borderWidthForState:self.state]; + self.layer.shapedBorderWidth = borderWidth; +} + +- (CGFloat)borderWidthForState:(UIControlState)state { + NSNumber *borderWidth = _borderWidths[@(state)]; + if (state != UIControlStateNormal && borderWidth == nil) { + borderWidth = _borderWidths[@(UIControlStateNormal)]; + } + if (borderWidth != nil) { + return (CGFloat)[borderWidth doubleValue]; + } + return 0; +} + +- (void)setBorderColor:(UIColor *)borderColor forState:(UIControlState)state { + _borderColors[@(state)] = borderColor; + + [self updateBorderColor]; +} + +- (void)updateBorderColor { + UIColor *borderColor = [self borderColorForState:self.state]; + self.layer.shapedBorderColor = borderColor; +} + +- (UIColor *)borderColorForState:(UIControlState)state { + UIColor *borderColor = _borderColors[@(state)]; + if (state != UIControlStateNormal && borderColor == nil) { + borderColor = _borderColors[@(UIControlStateNormal)]; + } + return borderColor; +} + +- (void)setShadowColor:(UIColor *)shadowColor forState:(UIControlState)state { + _shadowColors[@(state)] = shadowColor; + + [self updateShadowColor]; +} + +- (void)updateShadowColor { + CGColorRef shadowColor = [self shadowColorForState:self.state].CGColor; + self.layer.shadowColor = shadowColor; +} + +- (UIColor *)shadowColorForState:(UIControlState)state { + UIColor *shadowColor = _shadowColors[@(state)]; + if (state != UIControlStateNormal && shadowColor == nil) { + shadowColor = _shadowColors[@(UIControlStateNormal)]; + } + if (shadowColor != nil) { + return shadowColor; + } + return [UIColor blackColor]; +} + +- (void)setHighlighted:(BOOL)highlighted { + // Original logic for changing the state to highlighted. + if (self.rippleView == nil) { + if (highlighted && !self.highlighted) { + [self.inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil]; + } else if (!highlighted && self.highlighted) { + [self.inkView startTouchEndedAnimationAtPoint:_lastTouch completion:nil]; + } + } + [super setHighlighted:highlighted]; + // Updated logic using Ripple for changing the state to highlighted. + if (self.rippleView) { + self.rippleView.rippleHighlighted = highlighted; + } + + [self updateShadowElevation]; + [self updateBorderColor]; + [self updateBorderWidth]; + [self updateShadowColor]; +} + +- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event { + BOOL beginTracking = [super beginTrackingWithTouch:touch withEvent:event]; + CGPoint location = [touch locationInView:self]; + _lastTouch = location; + return beginTracking; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + UIView *result = [super hitTest:point withEvent:event]; + if (!_interactable && result == self) { + return nil; + } + if (self.layer.shapeGenerator) { + if (!CGPathContainsPoint(self.layer.shapeLayer.path, nil, point, true)) { + return nil; + } + } + return result; +} + +- (void)setShapeGenerator:(id)shapeGenerator { + if (shapeGenerator) { + self.layer.shadowPath = nil; + } else { + self.layer.shadowPath = [self boundingPath].CGPath; + } + + self.layer.shapeGenerator = shapeGenerator; + self.layer.shadowMaskEnabled = NO; + [self updateBackgroundColor]; + // Original logic for configuring Ink prior to the Ripple integration. + if (self.rippleView == nil) { + [self updateInkForShape]; + } +} + +- (id)shapeGenerator { + return self.layer.shapeGenerator; +} + +- (void)updateInkForShape { + CGRect boundingBox = CGPathGetBoundingBox(self.layer.shapeLayer.path); + self.inkView.maxRippleRadius = + (CGFloat)(hypot(CGRectGetHeight(boundingBox), CGRectGetWidth(boundingBox)) / 2 + 10); + self.inkView.layer.masksToBounds = NO; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + _backgroundColor = backgroundColor; + [self updateBackgroundColor]; +} + +- (UIColor *)backgroundColor { + return _backgroundColor; +} + +- (void)updateBackgroundColor { + self.layer.shapedBackgroundColor = _backgroundColor; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.rippleView) { + [self.rippleView touchesBegan:touches withEvent:event]; + } + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + // The ripple invocation must come before touchesMoved of the super, otherwise the setHighlighted + // of the UIControl will be triggered before the ripple identifies that the highlighted was + // trigerred from a long press entering the view and shouldn't invoke a ripple. + if (self.rippleView) { + [self.rippleView touchesMoved:touches withEvent:event]; + } + [super touchesMoved:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.rippleView) { + [self.rippleView touchesEnded:touches withEvent:event]; + } + [super touchesEnded:touches withEvent:event]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.rippleView) { + [self.rippleView touchesCancelled:touches withEvent:event]; + } + [super touchesCancelled:touches withEvent:event]; +} + +- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior { + if (enableRippleBehavior == _enableRippleBehavior) { + return; + } + _enableRippleBehavior = enableRippleBehavior; + if (enableRippleBehavior) { + if (_rippleView == nil) { + _rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds]; + _rippleView.layer.zPosition = FLT_MAX; + [self addSubview:_rippleView]; + } + if (_inkView) { + [_inkView removeFromSuperview]; + _inkView = nil; + } + } else { + if (_rippleView) { + [_rippleView removeFromSuperview]; + _rippleView = nil; + } + [self addSubview:_inkView]; + } +} + +- (CGFloat)mdc_currentElevation { + return [self shadowElevationForState:self.state]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h new file mode 100644 index 00000000..c84b20ea --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h @@ -0,0 +1,334 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "MaterialElevation.h" +#import "MaterialInk.h" +#import "MaterialRipple.h" +#import "MaterialShadowLayer.h" + +@protocol MDCShapeGenerating; + +/** + Through the lifecycle of the cell, the cell can go through one of the 3 states, + normal, highlighted, and selected. The cell starts in its default state, normal. + When `selectable` is set to NO, each touch on the cell turns it to the highlighted state, and when + the touch is released, it is returned to the normal state. When `selectable` is set to YES. Each + touch on the cell that isn't cancelled turns the cell to its selected state. Another touch on the + cell changes it back to normal. + */ +typedef NS_ENUM(NSInteger, MDCCardCellState) { + /** The visual state when the cell is in its normal state. */ + MDCCardCellStateNormal = 0, + + /** The visual state when the cell is in its highlighted state. */ + MDCCardCellStateHighlighted, + + /** The visual state when the cell has been selected. */ + MDCCardCellStateSelected, + + /** + The visual state when the cell is being dragged. + Currently only used with the Ripple Beta component. + */ + MDCCardCellStateDragged +}; + +/** + The horizontal alignment of the image when in selectable mode (`selectable` is set to YES). + */ +typedef NS_ENUM(NSInteger, MDCCardCellHorizontalImageAlignment) { + /** The alignment of the image is to the right of the card. */ + MDCCardCellHorizontalImageAlignmentRight = 0, + + /** The alignment of the image is to the center of the card. */ + MDCCardCellHorizontalImageAlignmentCenter, + + /** The alignment of the image is to the left of the card. */ + MDCCardCellHorizontalImageAlignmentLeft, + + // TODO: Add AlignmentLeading and AlignmentTrailing. See Github issue #3045 +}; + +/** + The vertical alignment of the image when in selectable mode (`selectable` is set to YES). + */ +typedef NS_ENUM(NSInteger, MDCCardCellVerticalImageAlignment) { + /** The alignment of the image is to the top of the card. */ + MDCCardCellVerticalImageAlignmentTop = 0, + + /** The alignment of the image is to the center of the card. */ + MDCCardCellVerticalImageAlignmentCenter, + + /** The alignment of the image is to the bottom of the card. */ + MDCCardCellVerticalImageAlignmentBottom, +}; + +@interface MDCCardCollectionCell : UICollectionViewCell + +/** + When selectable is set to YES, a tap on a cell will trigger a visual change between selected + and unselected. When it is set to NO, a tap will trigger a normal tap (rather than trigger + different visual selection states on the card). + Default is set to NO. + */ +@property(nonatomic, assign, getter=isSelectable) BOOL selectable; + +/** + A Boolean value indicating whether the card is in the dragged state. + */ +@property(nonatomic, getter=isDragged) BOOL dragged; + +/** + The corner radius for the card + Default is set to 4. + */ +@property(nonatomic, assign) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; + +/** + The inkView for the card that is initiated on tap + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +@property(nonatomic, readonly, strong, nonnull) MDCInkView *inkView; +#pragma clang diagnostic pop + +/** + The rippleView for the card that is initiated on tap. The ripple view is the successor of ink + view, and can be used by setting `enableRippleBehavior` to YES after initializing the card. + */ +@property(nonatomic, readonly, strong, nonnull) MDCStatefulRippleView *rippleView; + +/** + This property defines if a card as a whole should be interactable or not. + What this means is that when isInteractable is set to NO, there will be no ink ripple and + no change in shadow elevation when tapped or selected. Also the card container itself will not be + tappable, but any of its subviews will still be tappable. + + Default is set to YES. + + Important: Our specification for cards explicitly define a card as being an interactable component. + Therefore, this property should be set to NO *only if* there are other interactable items within + the card's content, such as buttons or other tappable controls. + */ +@property(nonatomic, getter=isInteractable) IBInspectable BOOL interactable; + +/* + The shape generator used to define the card cell's shape. + When set, layer properties such as cornerRadius and other layer properties are nullified/zeroed. + If a layer property is explicitly set after the shapeGenerator has been set, it will lead to + unexpected behavior. + + When the shapeGenerator is nil, MDCCardCollectionCell will use the default underlying layer with + its default settings. + + Default value for shapeGenerator is nil. + */ +@property(nullable, nonatomic, strong) id shapeGenerator; + +/** + By setting this property to YES, you will enable and use inkView's successor rippleView as the + main view to provide visual feedback for taps. It is recommended to set this property right after + initializing the card. + + Defaults to NO. + */ +@property(nonatomic, assign) BOOL enableRippleBehavior; + +/** + Sets the shadow elevation for an MDCCardViewState state + + @param shadowElevation The shadow elevation + @param state MDCCardCellState the card state + */ +- (void)setShadowElevation:(MDCShadowElevation)shadowElevation + forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the shadow elevation for an MDCCardViewState state + + If no elevation has been set for a state, the value for MDCCardCellStateNormal will be returned. + Default value for MDCCardCellStateNormal is 1 + Default value for MDCCardCellStateHighlighted is 8 + Default value for MDCCardCellStateSelected is 8 + + @param state MDCCardCellStateNormal the card state + @return The shadow elevation for the requested state. + */ +- (MDCShadowElevation)shadowElevationForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the border width for an MDCCardViewState state + + @param borderWidth The border width + @param state MDCCardCellState the card state + */ +- (void)setBorderWidth:(CGFloat)borderWidth forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the border width for an MDCCardCellState state + + If no border width has been set for a state, the value for MDCCardCellStateNormal will be returned. + Default value for MDCCardCellStateNormal is 0 + + @param state MDCCardCellState the card state + @return The border width for the requested state. + */ +- (CGFloat)borderWidthForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the border color for an MDCCardCellStateNormal state + + @param borderColor The border color + @param state MDCCardCellState the card state + */ +- (void)setBorderColor:(nullable UIColor *)borderColor + forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the border color for an MDCCardCellStateNormal state + + If no border color has been set for a state, it will check the value of UIControlStateNormal. + If that value also isn't set, then nil will be returned. + + @param state MDCCardCellState the card state + @return The border color for the requested state. + */ +- (nullable UIColor *)borderColorForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the shadow color for an MDCCardCellStateNormal state + + @param shadowColor The shadow color + @param state MDCCardCellState the card state + */ +- (void)setShadowColor:(nullable UIColor *)shadowColor + forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the shadow color for an MDCCardCellStateNormal state + + If no color has been set for a state, the value for MDCCardViewStateNormal will be returned. + Default value for MDCCardCellStateNormal is blackColor + + @param state MDCCardCellState the card state + @return The shadow color for the requested state. + */ +- (nullable UIColor *)shadowColorForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the image for an MDCCardCellStateNormal state. + + @note The image is only displayed when `selectable` is YES. + If no image has been set for a state, it will check the value of UIControlStateNormal. + If that value also isn't set, then nil will be returned. + Default value for MDCCardCellStateSelected is ic_check_circle + + @param state MDCCardCellState the card state + @return The image for the requested state. + */ +- (nullable UIImage *)imageForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the image for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + @param image The image + @param state MDCCardCellState the card state + */ +- (void)setImage:(nullable UIImage *)image forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the horizontal image alignment for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + If no alignment has been set for a state, it will check the value of UIControlStateNormal. + If that value also isn't set, then MDCCardCellImageHorizontalAlignmentRight will be returned. + + @param state MDCCardCellState the card state + @return The horizontal alignment for the requested state. + */ +- (MDCCardCellHorizontalImageAlignment)horizontalImageAlignmentForState:(MDCCardCellState)state + UI_APPEARANCE_SELECTOR; + +/** + Sets the image alignment for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + @param horizontalImageAlignment The image alignment + @param state MDCCardCellState the card state + */ +- (void)setHorizontalImageAlignment:(MDCCardCellHorizontalImageAlignment)horizontalImageAlignment + forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the vertical image alignment for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + If no alignment has been set for a state, it will check the value of UIControlStateNormal. + If that value also isn't set, then MDCCardCellImageVerticalAlignmentTop will be returned. + + @param state MDCCardCellState the card state + @return The vertical alignment for the requested state. + */ +- (MDCCardCellVerticalImageAlignment)verticalImageAlignmentForState:(MDCCardCellState)state + UI_APPEARANCE_SELECTOR; + +/** + Sets the image alignment for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + @param verticalImageAlignment The image alignment + @param state MDCCardCellState the card state + */ +- (void)setVerticalImageAlignment:(MDCCardCellVerticalImageAlignment)verticalImageAlignment + forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Returns the image tint color for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + If no tint color has been set for a state, it will check the value of UIControlStateNormal. + If that value also isn't set, then nil will be returned. + + @param state MDCCardCellState the card state + @return The image tint color for the requested state. + */ +- (nullable UIColor *)imageTintColorForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + Sets the image tint color for an MDCCardCellStateNormal state + + @note The image is only displayed when `selectable` is YES. + @param imageTintColor The image tint color + @param state MDCCardCellState the card state + */ +- (void)setImageTintColor:(nullable UIColor *)imageTintColor + forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; + +/** + The state of the card cell. + Default is MDCCardCellStateNormal. + */ +@property(nonatomic, readonly) MDCCardCellState state; + +/** + A block that is invoked when the @c MDCCardCollectionCell receives a call to @c + traitCollectionDidChange:. The block is called after the call to the superclass. + */ +@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) + (MDCCardCollectionCell *_Nonnull collectionCell, + UITraitCollection *_Nullable previousTraitCollection); + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m new file mode 100644 index 00000000..baffd348 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m @@ -0,0 +1,659 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCardCollectionCell.h" + +#import "MaterialElevation.h" +#import "MaterialInk.h" +#import "MaterialRipple.h" +#import "MaterialShadowElevations.h" +#import "MaterialShadowLayer.h" +#import "MaterialShapes.h" +#import "MaterialIcons+ic_check_circle.h" +#import "MaterialMath.h" + +static const CGFloat MDCCardCellCornerRadiusDefault = 4; +static const CGFloat MDCCardCellSelectedImagePadding = 8; +static const CGFloat MDCCardCellShadowElevationHighlighted = 8; +static const CGFloat MDCCardCellShadowElevationNormal = 1; +static const CGFloat MDCCardCellShadowElevationSelected = 8; +static const CGFloat MDCCardCellShadowElevationDragged = 8; +static const BOOL MDCCardCellIsInteractableDefault = YES; + +@interface MDCCardCollectionCell () +@property(nonatomic, strong, nullable) UIImageView *selectedImageView; +@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; +@end + +@implementation MDCCardCollectionCell { + NSMutableDictionary *_shadowElevations; + NSMutableDictionary *_shadowColors; + NSMutableDictionary *_borderWidths; + NSMutableDictionary *_borderColors; + NSMutableDictionary *_images; + NSMutableDictionary *_horizontalImageAlignments; + NSMutableDictionary *_verticalImageAlignments; + NSMutableDictionary *_imageTintColors; + UIColor *_backgroundColor; + CGPoint _lastTouch; +} + +@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation; +@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock; +@synthesize state = _state; +@dynamic layer; + ++ (Class)layerClass { + return [MDCShapedShadowLayer class]; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self) { + self.layer.cornerRadius = MDCCardCellCornerRadiusDefault; + _interactable = MDCCardCellIsInteractableDefault; + [self commonMDCCardCollectionCellInit]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.layer.cornerRadius = MDCCardCellCornerRadiusDefault; + _interactable = MDCCardCellIsInteractableDefault; + [self commonMDCCardCollectionCellInit]; + } + return self; +} + +- (void)commonMDCCardCollectionCellInit { + _mdc_overrideBaseElevation = -1; + + if (_inkView == nil) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + _inkView = [[MDCInkView alloc] initWithFrame:self.bounds]; +#pragma clang diagnostic pop + _inkView.autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + _inkView.usesLegacyInkRipple = NO; + _inkView.layer.zPosition = FLT_MAX; + [self addSubview:_inkView]; + } + + if (_selectedImageView == nil) { + _selectedImageView = [[UIImageView alloc] init]; + _selectedImageView.layer.zPosition = _inkView.layer.zPosition - 1; + _selectedImageView.autoresizingMask = + (UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin); + [self.contentView addSubview:_selectedImageView]; + _selectedImageView.hidden = YES; + } + + if (_shadowElevations == nil) { + _shadowElevations = [NSMutableDictionary dictionary]; + _shadowElevations[@(MDCCardCellStateNormal)] = @(MDCCardCellShadowElevationNormal); + _shadowElevations[@(MDCCardCellStateHighlighted)] = @(MDCCardCellShadowElevationHighlighted); + _shadowElevations[@(MDCCardCellStateSelected)] = @(MDCCardCellShadowElevationSelected); + _shadowElevations[@(MDCCardCellStateDragged)] = @(MDCCardCellShadowElevationDragged); + } + + if (_shadowColors == nil) { + _shadowColors = [NSMutableDictionary dictionary]; + _shadowColors[@(MDCCardCellStateNormal)] = UIColor.blackColor; + } + + if (_borderColors == nil) { + _borderColors = [NSMutableDictionary dictionary]; + } + + if (_borderWidths == nil) { + _borderWidths = [NSMutableDictionary dictionary]; + } + + if (_images == nil) { + _images = [NSMutableDictionary dictionary]; + UIImage *circledCheck = [MDCIcons imageFor_ic_check_circle]; + circledCheck = [circledCheck imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + _images[@(MDCCardCellStateSelected)] = circledCheck; + } + + if (_horizontalImageAlignments == nil) { + _horizontalImageAlignments = [NSMutableDictionary dictionary]; + _horizontalImageAlignments[@(MDCCardCellStateNormal)] = + @(MDCCardCellHorizontalImageAlignmentRight); + } + + if (_verticalImageAlignments == nil) { + _verticalImageAlignments = [NSMutableDictionary dictionary]; + _verticalImageAlignments[@(MDCCardCellStateNormal)] = @(MDCCardCellVerticalImageAlignmentTop); + } + + if (_imageTintColors == nil) { + _imageTintColors = [NSMutableDictionary dictionary]; + } + + if (_backgroundColor == nil) { + _backgroundColor = UIColor.whiteColor; + } + + [self updateCardCellVisuals]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + if (!self.layer.shapeGenerator) { + self.layer.shadowPath = [self boundingPath].CGPath; + } + [self updateImageAlignment]; +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + + if (self.traitCollectionDidChangeBlock) { + self.traitCollectionDidChangeBlock(self, previousTraitCollection); + } +} + +- (void)updateCardCellVisuals { + [self updateShadowElevation]; + [self updateBorderColor]; + [self updateBorderWidth]; + [self updateShadowColor]; + [self updateImage]; + [self updateImageAlignment]; + [self updateImageTintColor]; + [self updateBackgroundColor]; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + self.layer.cornerRadius = cornerRadius; + [self setNeedsLayout]; +} + +- (CGFloat)cornerRadius { + return self.layer.cornerRadius; +} + +- (void)setState:(MDCCardCellState)state animated:(BOOL)animated { + if (self.rippleView) { + return; + } + switch (state) { + case MDCCardCellStateSelected: { + if (_state != MDCCardCellStateHighlighted) { + if (animated) { + [self.inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil]; + } else { + [self.inkView cancelAllAnimationsAnimated:NO]; + [self.inkView startTouchBeganAtPoint:self.center animated:NO withCompletion:nil]; + } + } + break; + } + case MDCCardCellStateNormal: { + [self.inkView startTouchEndAtPoint:_lastTouch animated:animated withCompletion:nil]; + break; + } + case MDCCardCellStateHighlighted: { + // Note: setHighlighted: can get getting more calls with YES than NO when clicking rapidly. + // To guard against ink never going away and darkening our card we call + // startTouchEndedAnimationAtPoint:completion:. + [self.inkView startTouchEndAtPoint:_lastTouch animated:animated withCompletion:nil]; + [self.inkView startTouchBeganAtPoint:_lastTouch animated:animated withCompletion:nil]; + break; + } + default: + break; + } + _state = state; + [self updateCardCellVisuals]; +} + +- (MDCCardCellState)state { + if (self.rippleView) { + if (self.selected && self.selectable) { + return MDCCardCellStateSelected; + } else if (self.dragged) { + return MDCCardCellStateDragged; + } else if (self.highlighted) { + return MDCCardCellStateHighlighted; + } else { + return MDCCardCellStateNormal; + } + } + return _state; +} + +- (void)setSelected:(BOOL)selected { + [super setSelected:selected]; + if (self.rippleView) { + if (!self.selectable) { + return; + } + self.rippleView.selected = selected; + [self updateCardCellVisuals]; + } else { + if (self.selectable) { + if (selected) { + [self setState:MDCCardCellStateSelected animated:NO]; + } else { + [self setState:MDCCardCellStateNormal animated:NO]; + } + } + } +} + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + if (self.rippleView) { + self.rippleView.rippleHighlighted = highlighted; + [self updateCardCellVisuals]; + } +} + +- (void)setSelectable:(BOOL)selectable { + _selectable = selectable; + if (self.rippleView) { + self.rippleView.allowsSelection = selectable; + } else { + self.selectedImageView.hidden = !selectable; + } +} + +- (void)setDragged:(BOOL)dragged { + _dragged = dragged; + if (self.rippleView) { + self.rippleView.dragged = dragged; + if (dragged) { + self.highlighted = NO; + } + [self updateCardCellVisuals]; + } +} + +- (UIBezierPath *)boundingPath { + CGFloat cornerRadius = self.cornerRadius; + return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; +} + +- (MDCShadowElevation)shadowElevationForState:(MDCCardCellState)state { + NSNumber *elevation = _shadowElevations[@(state)]; + if (state != MDCCardCellStateNormal && elevation == nil) { + elevation = _shadowElevations[@(MDCCardCellStateNormal)]; + } + if (elevation != nil) { + return (CGFloat)[elevation doubleValue]; + } + return 0; +} + +- (void)setShadowElevation:(MDCShadowElevation)shadowElevation forState:(MDCCardCellState)state { + _shadowElevations[@(state)] = @(shadowElevation); + + [self updateShadowElevation]; +} + +- (void)updateShadowElevation { + CGFloat elevation = [self shadowElevationForState:self.state]; + if (!MDCCGFloatEqual(((MDCShadowLayer *)self.layer).elevation, elevation)) { + if (!self.layer.shapeGenerator) { + self.layer.shadowPath = [self boundingPath].CGPath; + } + [(MDCShadowLayer *)self.layer setElevation:elevation]; + [self mdc_elevationDidChange]; + } +} + +- (void)setBorderWidth:(CGFloat)borderWidth forState:(MDCCardCellState)state { + _borderWidths[@(state)] = @(borderWidth); + + [self updateBorderWidth]; +} + +- (void)updateBorderWidth { + CGFloat borderWidth = [self borderWidthForState:self.state]; + self.layer.shapedBorderWidth = borderWidth; +} + +- (CGFloat)borderWidthForState:(MDCCardCellState)state { + NSNumber *borderWidth = _borderWidths[@(state)]; + if (state != MDCCardCellStateNormal && borderWidth == nil) { + borderWidth = _borderWidths[@(MDCCardCellStateNormal)]; + } + if (borderWidth != nil) { + return (CGFloat)[borderWidth doubleValue]; + } + return 0; +} + +- (void)setBorderColor:(UIColor *)borderColor forState:(MDCCardCellState)state { + _borderColors[@(state)] = borderColor; + + [self updateBorderColor]; +} + +- (void)updateBorderColor { + UIColor *borderColor = [self borderColorForState:self.state]; + self.layer.shapedBorderColor = borderColor; +} + +- (UIColor *)borderColorForState:(MDCCardCellState)state { + UIColor *borderColor = _borderColors[@(state)]; + if (state != MDCCardCellStateNormal && borderColor == nil) { + borderColor = _borderColors[@(MDCCardCellStateNormal)]; + } + return borderColor; +} + +- (void)setShadowColor:(UIColor *)shadowColor forState:(MDCCardCellState)state { + _shadowColors[@(state)] = shadowColor; + + [self updateShadowColor]; +} + +- (void)updateShadowColor { + CGColorRef shadowColor = [self shadowColorForState:self.state].CGColor; + self.layer.shadowColor = shadowColor; +} + +- (UIColor *)shadowColorForState:(MDCCardCellState)state { + UIColor *shadowColor = _shadowColors[@(state)]; + if (state != MDCCardCellStateNormal && shadowColor == nil) { + shadowColor = _shadowColors[@(MDCCardCellStateNormal)]; + } + if (shadowColor != nil) { + return shadowColor; + } + return [UIColor blackColor]; +} + +- (void)setImage:(UIImage *)image forState:(MDCCardCellState)state { + _images[@(state)] = image; + + [self updateImage]; +} + +- (void)updateImage { + UIImage *image = [self imageForState:self.state]; + if (self.rippleView) { + // TODO(#6661): CardCollectionCell's state system doesn't incorporate multiple states occurring + // simultaneously. When the card is selected and highlighted it should take the image of + // MDCCardCellStateSelected. + if (self.rippleView.selected) { + image = [self imageForState:MDCCardCellStateSelected]; + } + } + [self.selectedImageView setImage:image]; + [self.selectedImageView sizeToFit]; +} + +- (UIImage *)imageForState:(MDCCardCellState)state { + UIImage *image = _images[@(state)]; + if (state != MDCCardCellStateNormal && image == nil) { + image = _images[@(MDCCardCellStateNormal)]; + } + return image; +} + +- (void)setHorizontalImageAlignment:(MDCCardCellHorizontalImageAlignment)horizontalImageAlignment + forState:(MDCCardCellState)state { + _horizontalImageAlignments[@(state)] = @(horizontalImageAlignment); + + [self updateImageAlignment]; +} + +- (MDCCardCellHorizontalImageAlignment)horizontalImageAlignmentForState:(MDCCardCellState)state { + NSNumber *horizontalImageAlignment = _horizontalImageAlignments[@(state)]; + if (state != MDCCardCellStateNormal && horizontalImageAlignment == nil) { + horizontalImageAlignment = _horizontalImageAlignments[@(MDCCardCellStateNormal)]; + } + if (horizontalImageAlignment != nil) { + return (MDCCardCellHorizontalImageAlignment)[horizontalImageAlignment integerValue]; + } + return MDCCardCellHorizontalImageAlignmentRight; +} + +- (void)setVerticalImageAlignment:(MDCCardCellVerticalImageAlignment)verticalImageAlignment + forState:(MDCCardCellState)state { + _verticalImageAlignments[@(state)] = @(verticalImageAlignment); + + [self updateImageAlignment]; +} + +- (MDCCardCellVerticalImageAlignment)verticalImageAlignmentForState:(MDCCardCellState)state { + NSNumber *verticalImageAlignment = _verticalImageAlignments[@(state)]; + if (state != MDCCardCellStateNormal && verticalImageAlignment == nil) { + verticalImageAlignment = _verticalImageAlignments[@(MDCCardCellStateNormal)]; + } + if (verticalImageAlignment != nil) { + return (MDCCardCellVerticalImageAlignment)[verticalImageAlignment integerValue]; + } + return MDCCardCellVerticalImageAlignmentTop; +} + +- (void)updateImageAlignment { + MDCCardCellVerticalImageAlignment verticalImageAlignment = + [self verticalImageAlignmentForState:self.state]; + MDCCardCellHorizontalImageAlignment horizontalImageAlignment = + [self horizontalImageAlignmentForState:self.state]; + + CGFloat yAlignment = 0; + CGFloat xAlignment = 0; + + switch (verticalImageAlignment) { + case MDCCardCellVerticalImageAlignmentTop: + yAlignment = + MDCCardCellSelectedImagePadding + CGRectGetHeight(self.selectedImageView.frame) / 2; + break; + case MDCCardCellVerticalImageAlignmentCenter: + yAlignment = CGRectGetHeight(self.bounds) / 2; + break; + case MDCCardCellVerticalImageAlignmentBottom: + yAlignment = CGRectGetHeight(self.bounds) - MDCCardCellSelectedImagePadding - + CGRectGetHeight(self.selectedImageView.frame) / 2; + break; + } + + switch (horizontalImageAlignment) { + case MDCCardCellHorizontalImageAlignmentLeft: + xAlignment = + MDCCardCellSelectedImagePadding + CGRectGetWidth(self.selectedImageView.frame) / 2; + break; + case MDCCardCellHorizontalImageAlignmentCenter: + xAlignment = CGRectGetWidth(self.bounds) / 2; + break; + case MDCCardCellHorizontalImageAlignmentRight: + xAlignment = CGRectGetWidth(self.bounds) - MDCCardCellSelectedImagePadding - + CGRectGetWidth(self.selectedImageView.frame) / 2; + break; + } + + self.selectedImageView.center = CGPointMake(xAlignment, yAlignment); +} + +- (void)setImageTintColor:(UIColor *)imageTintColor forState:(MDCCardCellState)state { + _imageTintColors[@(state)] = imageTintColor; + + [self updateImageTintColor]; +} + +- (void)updateImageTintColor { + UIColor *imageTintColor = [self imageTintColorForState:self.state]; + if (self.rippleView) { + // TODO(#6661): CardCollectionCell's state system doesn't incorporate multiple states occurring + // simultaneously. When the card is selected and highlighted it should take the image tint of + // MDCCardCellStateSelected. + if (self.rippleView.selected) { + imageTintColor = [self imageTintColorForState:MDCCardCellStateSelected]; + } + } + [self.selectedImageView setTintColor:imageTintColor]; +} + +- (UIColor *)imageTintColorForState:(MDCCardCellState)state { + UIColor *imageTintColor = _imageTintColors[@(state)]; + if (state != MDCCardCellStateNormal && imageTintColor == nil) { + imageTintColor = _imageTintColors[@(MDCCardCellStateNormal)]; + } + return imageTintColor; +} + +- (void)tintColorDidChange { + [super tintColorDidChange]; + [self setImageTintColor:self.tintColor forState:MDCCardCellStateNormal]; +} + +- (void)setShapeGenerator:(id)shapeGenerator { + if (shapeGenerator) { + self.layer.shadowPath = nil; + } else { + self.layer.shadowPath = [self boundingPath].CGPath; + } + + self.layer.shapeGenerator = shapeGenerator; + self.layer.shadowMaskEnabled = NO; + [self updateBackgroundColor]; + // Original logic for configuring Ink prior to the Ripple integration. + if (self.rippleView == nil) { + [self updateInkForShape]; + } +} + +- (id)shapeGenerator { + return self.layer.shapeGenerator; +} + +- (void)updateInkForShape { + CGRect boundingBox = CGPathGetBoundingBox(self.layer.shapeLayer.path); + self.inkView.maxRippleRadius = + (CGFloat)(hypot(CGRectGetHeight(boundingBox), CGRectGetWidth(boundingBox)) / 2 + 10); + self.inkView.layer.masksToBounds = NO; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + _backgroundColor = backgroundColor; + [self updateBackgroundColor]; +} + +- (UIColor *)backgroundColor { + return _backgroundColor; +} + +- (void)updateBackgroundColor { + self.layer.shapedBackgroundColor = _backgroundColor; +} + +#pragma mark - UIResponder + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + UIView *result = [super hitTest:point withEvent:event]; + if (!_interactable && (result == self.contentView || result == self)) { + return nil; + } + return result; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.rippleView) { + [self.rippleView touchesBegan:touches withEvent:event]; + } + [super touchesBegan:touches withEvent:event]; + if (self.rippleView == nil) { + UITouch *touch = [touches anyObject]; + CGPoint location = [touch locationInView:self]; + _lastTouch = location; + if (!self.selected || !self.selectable) { + [self setState:MDCCardCellStateHighlighted animated:YES]; + } + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + // The ripple invocation must come before touchesMoved of the super, otherwise the setHighlighted + // of the UICollectionViewCell will be triggered before the ripple identifies that the highlighted + // was trigerred from a long press entering the view and shouldn't invoke a ripple. + if (self.rippleView) { + [self.rippleView touchesMoved:touches withEvent:event]; + } + [super touchesMoved:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.rippleView) { + [self.rippleView touchesEnded:touches withEvent:event]; + if (self.dragged) { + self.dragged = NO; + } + } + [super touchesEnded:touches withEvent:event]; + if (self.rippleView == nil) { + if (!self.selected || !self.selectable) { + [self setState:MDCCardCellStateNormal animated:YES]; + } + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + if (self.rippleView) { + [self.rippleView touchesCancelled:touches withEvent:event]; + if (self.dragged) { + self.dragged = NO; + } + } + [super touchesCancelled:touches withEvent:event]; + if (self.rippleView == nil) { + if (!self.selected || !self.selectable) { + [self setState:MDCCardCellStateNormal animated:YES]; + } + } +} + +- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior { + if (enableRippleBehavior == _enableRippleBehavior) { + return; + } + _enableRippleBehavior = enableRippleBehavior; + if (enableRippleBehavior) { + // With the new states implementation the selectedImageView doesn't need to be hidden as + // there can be an image apparent not only when the cell is selected, but rather + // depending on the setImage:ForState: API. + self.selectedImageView.hidden = NO; + if (_rippleView == nil) { + _rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds]; + _rippleView.layer.zPosition = FLT_MAX; + [self addSubview:_rippleView]; + } + if (_inkView) { + [_inkView removeFromSuperview]; + _inkView = nil; + } + } else { + self.selectedImageView.hidden = YES; + if (_rippleView) { + [_rippleView removeFromSuperview]; + _rippleView = nil; + } + [self addSubview:_inkView]; + } +} + +- (CGFloat)mdc_currentElevation { + return [self shadowElevationForState:self.state]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h new file mode 100644 index 00000000..69bd1b0a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h @@ -0,0 +1,16 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCard.h" +#import "MDCCardCollectionCell.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h new file mode 100644 index 00000000..1a0c608f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h @@ -0,0 +1,28 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface UICollectionViewController (MDCCardReordering) + +/** + This method should be called when using a UICollectionViewController and want to have + the reorder or drag and drop visuals. Please see + https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623979-installsstandardgestureforintera + for more information. It will make sure that the underlying longPressGestureRecognizer + doesn't cancel the ink tap visual causing the ink to disappear once the reorder begins. + */ +- (void)mdc_setupCardReordering; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m new file mode 100644 index 00000000..948e1224 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m @@ -0,0 +1,39 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UICollectionViewController+MDCCardReordering.h" + + +@implementation UICollectionViewController (MDCCardReordering) + +- (void)mdc_setupCardReordering { + UILongPressGestureRecognizer *longGestureRecognizer = + [[UILongPressGestureRecognizer alloc] initWithTarget:self action:nil]; + + longGestureRecognizer.delegate = self; + longGestureRecognizer.cancelsTouchesInView = NO; + [self.collectionView addGestureRecognizer:longGestureRecognizer]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer + shouldReceiveTouch:(UITouch *)touch { + for (UIGestureRecognizer *gesture in self.collectionView.gestureRecognizers) { + if ([gesture isKindOfClass:[UILongPressGestureRecognizer class]]) { + gesture.cancelsTouchesInView = NO; + } + } + return YES; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h new file mode 100644 index 00000000..09681a8f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h @@ -0,0 +1,42 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + Provides APIs for @c UIViews to communicate their elevation throughout the view hierarchy. + */ +@protocol MDCElevatable + +/** + The current elevation of the conforming @c UIView. + */ +@property(nonatomic, assign, readonly) CGFloat mdc_currentElevation; + +/** + This block is called when the elevation changes for the conforming @c UIView or @c UIViewController + receiver or one of its direct ancestors in the view hierarchy. + + Use this block to respond to elevation changes in the view or its ancestor views. + + @param absoluteElevation The @c mdc_currentElevation plus the @c mdc_currentElevation of all + ancestor views. This equates to @c mdc_absoluteElevation of the UIView+MaterialElevationResponding + category. + @param object The receiver (self) which conforms to the protocol. + */ +@property(nonatomic, copy, nullable) void (^mdc_elevationDidChangeBlock) + (id _Nonnull object, CGFloat absoluteElevation); + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h new file mode 100644 index 00000000..b3e0e9fb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h @@ -0,0 +1,34 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + Provides APIs for @c UIViews to communicate their elevation throughout the view hierarchy. + */ +@protocol MDCElevationOverriding + +/** + Used by @c MaterialElevationResponding instead of @c mdc_baseElevation. + + This can be used in cases where there is elevation behind an object that is not part of the + view hierarchy, like a @c UIPresentationController. + + Note: If set to a negative value, this property is ignored as part of the @c mdc_baseElevation + calculation. + */ +@property(nonatomic, assign, readwrite) CGFloat mdc_overrideBaseElevation; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h new file mode 100644 index 00000000..60c02b35 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h @@ -0,0 +1,18 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCElevatable.h" +#import "MDCElevationOverriding.h" +#import "UIColor+MaterialElevation.h" +#import "UIView+MaterialElevationResponding.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h new file mode 100644 index 00000000..14a1aa14 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h @@ -0,0 +1,65 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + Provides extension to UIColor for Material Elevation usage. + */ +@interface UIColor (MaterialElevation) + +/** + Returns a color that takes the specified elevation value into account. + The color is the blended color of Surface and Elevation Overlay in + https://material.io/design/color/dark-theme.html#properties + Negative elevation is treated as 0. + Pattern-based UIColor is not supported. + @param elevation The @c mdc_absoluteElevation value to use when resolving the color. + */ +- (nonnull UIColor *)mdc_resolvedColorWithElevation:(CGFloat)elevation; + +/** + Returns a color that takes the specified elevation value and traits into account when there is a + color appearance difference between current traits and previous traits. When userInterfaceStyle is + UIUserInterfaceStyleDark in currentTraitCollection, elevation will be used to resolve the color. + + Negative elevation is treated as 0. + Pattern-based UIColor is not supported. + UIColor in UIExtendedGrayColorSpace will be resolved to UIExtendedSRGBColorSpace. + + @param traitCollection The traits to use when resolving the color. + @param previousTraitCollection The previous traits to use when comparing color appearance. + @param elevation The @c mdc_absoluteElevation to use when resolving the color. + */ +- (nonnull UIColor *) + mdc_resolvedColorWithTraitCollection:(nonnull UITraitCollection *)traitCollection + previousTraitCollection:(nonnull UITraitCollection *)previousTraitCollection + elevation:(CGFloat)elevation; + +/** + Returns a color that takes the specified elevation value and traits into account. + When userInterfaceStyle is UIUserInterfaceStyleDark in traitCollection, elevation will be used + to resolve the color. + Negative elevation is treated as 0. + Pattern-based UIColor is not supported. + UIColor in UIExtendedGrayColorSpace will be resolved to UIExtendedSRGBColorSpace. + + @param traitCollection The traits to use when resolving the color. + @param elevation The @c mdc_absoluteElevation to use when resolving the color. + */ +- (nonnull UIColor *)mdc_resolvedColorWithTraitCollection: + (nonnull UITraitCollection *)traitCollection + elevation:(CGFloat)elevation; +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m new file mode 100644 index 00000000..274b0d34 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m @@ -0,0 +1,85 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIColor+MaterialElevation.h" + +#import + +#import "MaterialAvailability.h" +#import "MaterialMath.h" +#import "UIColor+MaterialBlending.h" + +@implementation UIColor (MaterialElevation) + +- (UIColor *)mdc_resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection + previousTraitCollection:(UITraitCollection *)previousTraitCollection + elevation:(CGFloat)elevation { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + if ([traitCollection + hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { + return [self mdc_resolvedColorWithTraitCollection:traitCollection elevation:elevation]; + } + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + return self; +} + +- (UIColor *)mdc_resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection + elevation:(CGFloat)elevation { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + UIColor *resolvedColor = [self resolvedColorWithTraitCollection:traitCollection]; + if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + return [resolvedColor mdc_resolvedColorWithElevation:elevation]; + } else { + return resolvedColor; + } + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + return self; +} + +- (UIColor *)mdc_resolvedColorWithElevation:(CGFloat)elevation { + if (CGColorGetPattern(self.CGColor)) { + [NSException raise:NSGenericException + format:@"Pattern-based colors are not supported by %@", NSStringFromSelector(_cmd)]; + } + + UIColor *overlayColor = UIColor.whiteColor; + elevation = MAX(elevation, 0); + CGFloat alphaValue = 0; + if (!MDCCGFloatEqual(elevation, 0)) { + if (elevation < 1) { + // A formula for values between 0 to 1 is used here to simulate the alpha percentage + // as in the main formula below there is a jump between any number larger than 0 to an + // alpha value of 2. This formula provides a gradual polynomial curve that makes the delta + // of the alpha value between lower numbers to be smaller than the higher numbers. + // AlphaValue = 5.11916 * elevationValue ^ 2 + alphaValue = (CGFloat)5.11916 * pow((CGFloat)elevation, 2); + } else { + // A formula is used here to simulate the alpha percentage stated on + // https://material.io/design/color/dark-theme.html#properties + // AlphaValue = 4.5 * ln (elevationValue + 1) + 2 + // Note: Both formulas meet at the transition point of (1, 5.11916). + alphaValue = (CGFloat)4.5 * (CGFloat)log(elevation + 1) + 2; + } + } + // TODO (https://github.com/material-components/material-components-ios/issues/8096): + // Grayscale color should be returned if color space is UIExtendedGrayColorSpace. + return [UIColor mdc_blendColor:[overlayColor colorWithAlphaComponent:alphaValue * (CGFloat)0.01] + withBackgroundColor:self]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h new file mode 100644 index 00000000..f559a327 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h @@ -0,0 +1,53 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + Allows elevation changes to propagate down the view hierarchy and allows objects conforming to + @c MDCElevatable to react to those changes accordingly. + */ +@interface UIView (MaterialElevationResponding) + +/** + Returns the sum of all @c mdc_currentElevation of the superviews going up the view hierarchy + recursively. + + If a view in the hierarchy conforms to @c MDCElevationOveriding and @c mdc_overrideBaseElevation + is non-negative, then the sum of the current total plus the value of @c mdc_overrideBaseElevation + is returned. + + If a @c UIViewController conforms to @c MDCElevatable or @c MDCElevationOveriding then its @c view + will report the view controllers base elevation. + */ +@property(nonatomic, assign, readonly) CGFloat mdc_baseElevation; + +/** + Returns the sum of the view's @c mdc_currentElevation with the @c mdc_currentElevation of its + superviews going up the view hierarchy recursively. + + This value is effectively the sum of @c mdc_baseElevation and @c mdc_currentElevation. + */ +@property(nonatomic, assign, readonly) CGFloat mdc_absoluteElevation; + +/** + Should be called when the view's @c mdc_currentElevation has changed. Will be called on the + receiver's @c subviews. + + If a @c UIView views conform to @c MDCElevation then @c mdc_elevationDidChangeBlock: is called. + */ +- (void)mdc_elevationDidChange; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m new file mode 100644 index 00000000..95bc4dec --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m @@ -0,0 +1,108 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIView+MaterialElevationResponding.h" + +#import "MDCElevatable.h" +#import "MDCElevationOverriding.h" + +@implementation UIView (MaterialElevationResponding) + +- (void)mdc_elevationDidChange { + CGFloat baseElevation = self.mdc_baseElevation; + [self mdc_elevationDidChangeWithBaseElevation:baseElevation]; +} + +- (void)mdc_elevationDidChangeWithBaseElevation:(CGFloat)baseElevation { + CGFloat elevation = baseElevation; + id elevatableSelf = [self objectConformingToElevationInResponderChain]; + if (elevatableSelf.mdc_elevationDidChangeBlock) { + elevation += elevatableSelf.mdc_currentElevation; + elevatableSelf.mdc_elevationDidChangeBlock(elevatableSelf, elevation); + } + + for (UIView *subview in self.subviews) { + [subview mdc_elevationDidChangeWithBaseElevation:elevation]; + } +} + +- (CGFloat)mdc_baseElevation { + CGFloat totalElevation = 0; + UIView *current = self; + + while (current != nil) { + id elevatableCurrent = [current objectConformingToElevationInResponderChain]; + if (current != self) { + totalElevation += elevatableCurrent.mdc_currentElevation; + } + id elevatableCurrentOverride = + [current objectConformingToOverrideInResponderChain]; + if (elevatableCurrentOverride != nil && + elevatableCurrentOverride.mdc_overrideBaseElevation >= 0) { + totalElevation += elevatableCurrentOverride.mdc_overrideBaseElevation; + break; + } + current = current.superview; + } + return totalElevation; +} + +- (CGFloat)mdc_absoluteElevation { + CGFloat elevation = self.mdc_baseElevation; + id elevatableSelf = [self objectConformingToElevationInResponderChain]; + elevation += elevatableSelf.mdc_currentElevation; + return elevation; +} + +/** + Checks whether a @c UIView or it's managing @c UIViewController conform to @c + MDCOverrideElevation. + + @returns the conforming @c UIView then @c UIViewController, otherwise @c nil. + */ +- (id)objectConformingToOverrideInResponderChain { + if ([self conformsToProtocol:@protocol(MDCElevationOverriding)]) { + return (id)self; + } + + UIResponder *nextResponder = self.nextResponder; + if ([nextResponder isKindOfClass:[UIViewController class]] && + [nextResponder conformsToProtocol:@protocol(MDCElevationOverriding)]) { + return (id)nextResponder; + } + + return nil; +} + +/** + Checks whether a @c UIView or it's managing @c UIViewController conform to @c + MDCElevation. + + @returns the conforming @c UIView then @c UIViewController, otherwise @c nil. + */ +- (id)objectConformingToElevationInResponderChain { + if ([self conformsToProtocol:@protocol(MDCElevatable)]) { + return (id)self; + } + + UIResponder *nextResponder = self.nextResponder; + if ([nextResponder isKindOfClass:[UIViewController class]] && + [nextResponder conformsToProtocol:@protocol(MDCElevatable)]) { + return (id)nextResponder; + } + + return nil; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h new file mode 100644 index 00000000..9d3b2c15 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h @@ -0,0 +1,60 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + Custom gesture recognizer to observe the various ink response states. + + MDCInkGestureRecognizer is a continuous recognizer that tracks single touches and optionally + fails if the touch moves outside the recongizer's view. Multiple touches will cause the + recognizer to transition to the UIGestureRecognizerStateCancelled state. + */ +__deprecated_msg("Please use MDCRippleTouchController instead.") @interface MDCInkGestureRecognizer + : UIGestureRecognizer + +/** + Set the distance that causes the recognizer to cancel. + */ +@property(nonatomic, assign) CGFloat dragCancelDistance; + +/** + Set when dragging outside of the view causes the gesture recognizer to cancel. + + Defaults to YES. + */ +@property(nonatomic, assign) BOOL cancelOnDragOut; + +/** + Bounds inside of which the recognizer will recognize ink gestures, relative to self.view.frame. + + If set to CGRectNull (the default), then the recognizer will use self.view.bounds as the target + bounds. + + If cancelOnDragOut is YES and the user's touch moves beyond the target bounds inflated by + dragCancelDistance then the gesture is cancelled. + */ +@property(nonatomic) CGRect targetBounds; + +/** + Returns the point where the ink starts spreading from. + + @param view View which the point is relative to. + */ +- (CGPoint)touchStartLocationInView:(UIView *)view; + +/** Returns YES if the touch's current location is still within the target bounds. */ +- (BOOL)isTouchWithinTargetBounds; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m new file mode 100644 index 00000000..d87a48f8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m @@ -0,0 +1,94 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCInkGestureRecognizer.h" + +#import + +static const CGFloat kInkGestureDefaultDragCancelDistance = 20; + +@implementation MDCInkGestureRecognizer { + CGPoint _touchStartLocation; + CGPoint _touchCurrentLocation; + BOOL _cancelOnDragOut; +} + +- (instancetype)initWithTarget:(id)target action:(SEL)action { + self = [super initWithTarget:target action:action]; + if (self) { + _cancelOnDragOut = YES; + _dragCancelDistance = kInkGestureDefaultDragCancelDistance; + _targetBounds = CGRectNull; + self.cancelsTouchesInView = NO; + self.delaysTouchesEnded = NO; + } + return self; +} + +- (CGPoint)touchStartLocationInView:(UIView *)view { + return [view convertPoint:_touchStartLocation fromView:nil]; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesBegan:touches withEvent:event]; + if ([touches count] == 1) { + self.state = UIGestureRecognizerStateBegan; + _touchStartLocation = [[touches anyObject] locationInView:nil]; + _touchCurrentLocation = _touchStartLocation; + } else { + self.state = UIGestureRecognizerStateCancelled; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesMoved:touches withEvent:event]; + if (self.state == UIGestureRecognizerStateFailed) { + return; + } + + _touchCurrentLocation = [[touches anyObject] locationInView:nil]; + + // Cancel the gesture if it is too far away. + if (_cancelOnDragOut && ![self isTouchWithinTargetBounds]) { + self.state = UIGestureRecognizerStateCancelled; + } else { + self.state = UIGestureRecognizerStateChanged; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesEnded:touches withEvent:event]; + self.state = UIGestureRecognizerStateEnded; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesCancelled:touches withEvent:event]; + self.state = UIGestureRecognizerStateCancelled; +} + +- (BOOL)isTouchWithinTargetBounds { + CGRect targetBounds = [self effectiveTargetBounds]; + CGRect boundsInWindowCoord = [self.view convertRect:targetBounds toView:nil]; + boundsInWindowCoord = + CGRectInset(boundsInWindowCoord, -_dragCancelDistance, -_dragCancelDistance); + return CGRectContainsPoint(boundsInWindowCoord, _touchCurrentLocation); +} + +#pragma mark - Private methods + +- (CGRect)effectiveTargetBounds { + return CGRectEqualToRect(_targetBounds, CGRectNull) ? self.view.bounds : _targetBounds; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h new file mode 100644 index 00000000..7dc112d6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h @@ -0,0 +1,129 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCInkTouchControllerDelegate.h" + +@class MDCInkGestureRecognizer; +@class MDCInkTouchController; +@class MDCInkView; +@protocol MDCInkTouchControllerDelegate; + +/** + MDCInkTouchController associates a MDCInkView with a UIGestureRecognizer to control the spread of + the ink. + + Subclasses should avoid overriding the UIGestureRecognizerDelegate gestureRecognizerShouldBegin: + and gestureRecognizer:shouldReceiveTouch: methods to avoid breaking + MDCInkTouchControllerDelegate. + + **NOTE:** The controller does not keep a strong reference to the view to which it is attaching an + ink view. + It is expected that the view will keep a strong reference to its own ink controller, or that the + view controller controlling the view will keep a strong reference to that view's ink controller. + */ +__deprecated_msg("Please use MDCRippleTouchController instead.") @interface MDCInkTouchController + : NSObject + +/** Weak reference to the view that responds to touch events. */ +@property(nonatomic, weak, readonly, nullable) UIView *view; + +/** + The ink view for clients who do not create their own ink views via the delegate. + */ +@property(nonatomic, strong, readonly, nonnull) MDCInkView *defaultInkView; + +/** Delegate to extend the behavior of the touch control. */ +@property(nonatomic, weak, nullable) id delegate; + +/** If YES, the gesture recognizer should delay the start of ink spread. Default is NO. */ +@property(nonatomic, assign) BOOL delaysInkSpread; + +/** The distance that causes the recognizer to cancel. Defaults to 20pt. */ +@property(nonatomic, assign) CGFloat dragCancelDistance; + +/** + Whether dragging outside of the view causes the gesture recognizer to cancel. + Defaults to YES. + */ +@property(nonatomic, assign) BOOL cancelsOnDragOut; + +/** + If enabled, the ink gesture will require failure of UIScrollView gesture recognizers in order to + activate. + + Defaults to NO. + */ +@property(nonatomic, assign) BOOL requiresFailureOfScrollViewGestures; + +/** + Bounds inside of which the recognizer will recognize ink gestures, relative to self.view.frame. + + If set to CGRectNull (the default), then the recognizer will use self.view.bounds as the target + bounds. + + If cancelsOnDragOut is YES and the user's touch moves beyond the target bounds inflated by + dragCancelDistance then the gesture is cancelled. + */ +@property(nonatomic) CGRect targetBounds; + +/** Gesture recognizer used to bind touch events to ink. */ +@property(nonatomic, strong, readonly, nonnull) MDCInkGestureRecognizer *gestureRecognizer; + +/** Unavailable, please use initWithView: instead. */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Initializes the controller. + + @param view View that responds to touch events for ink. + */ +- (nonnull instancetype)initWithView:(nonnull UIView *)view NS_DESIGNATED_INITIALIZER; + +/** + When called the @c defaultInkView is added to the @c view. + + This method is a no-op when the delegate conforms to @c inkTouchController:inkViewAtTouchLocation: + because this is how a client specifies a custom ink view. + + If you want to specify a specific z-index order for your inkView please conform to + @c inkTouchController:insertInkView:intoView: and do so there. + */ +- (void)addInkView; + +/** + Cancels all touch processing and dissipates the ink. + + This is useful if your application needs to remove the ink on scrolling, when preparing a view + for reuse, etc. + */ +- (void)cancelInkTouchProcessing; + +/** + Returns the ink view at a particular touch location. + + If the delegate responds to @c inkTouchController:inkViewAtLocation: then this method queries it. + Otherwise, if @c addInkView has been called and @c location is in the bounds of + @c self.defaultView, then that view is returned. If none of these conditions are met, @c nil is + returned. + + @param location The query location in the coordinates of @c self.view. + @return The ink view at the touch location, or nil. +*/ +- (MDCInkView *_Nullable)inkViewAtTouchLocation:(CGPoint)location; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m new file mode 100644 index 00000000..670b4020 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m @@ -0,0 +1,222 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCInkTouchController.h" + +#import "MDCInkGestureRecognizer.h" +#import "MDCInkTouchControllerDelegate.h" +#import "MDCInkView.h" + +static const NSTimeInterval kInkTouchDelayInterval = 0.1; + +@interface MDCInkTouchController () +@property(nonatomic, strong) MDCInkView *addedInkView; +@property(nonatomic, strong) MDCInkView *defaultInkView; +@property(nonatomic, assign) BOOL shouldRespondToTouch; +@property(nonatomic, assign) CGPoint previousLocation; +@end + +@implementation MDCInkTouchController + +- (CGFloat)dragCancelDistance { + return _gestureRecognizer.dragCancelDistance; +} + +- (void)setDragCancelDistance:(CGFloat)dragCancelDistance { + _gestureRecognizer.dragCancelDistance = dragCancelDistance; +} + +- (BOOL)cancelsOnDragOut { + return _gestureRecognizer.cancelOnDragOut; +} + +- (void)setCancelsOnDragOut:(BOOL)cancelsOnDragOut { + _gestureRecognizer.cancelOnDragOut = cancelsOnDragOut; +} + +- (CGRect)targetBounds { + return _gestureRecognizer.targetBounds; +} + +- (void)setTargetBounds:(CGRect)targetBounds { + _gestureRecognizer.targetBounds = targetBounds; +} + +- (instancetype)initWithView:(UIView *)view { + self = [super init]; + if (self) { + _requiresFailureOfScrollViewGestures = NO; + + _gestureRecognizer = + [[MDCInkGestureRecognizer alloc] initWithTarget:self action:@selector(handleInkGesture:)]; + _gestureRecognizer.delegate = self; + + _view = view; + [_view addGestureRecognizer:_gestureRecognizer]; + + _defaultInkView = [[MDCInkView alloc] initWithFrame:view.bounds]; + _defaultInkView.inkColor = _defaultInkView.defaultInkColor; + _defaultInkView.autoresizingMask = + UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + } + return self; +} + +- (void)dealloc { + [_view removeGestureRecognizer:_gestureRecognizer]; + _gestureRecognizer.delegate = nil; +} + +- (void)addInkView { + if (![_delegate respondsToSelector:@selector(inkTouchController:inkViewAtTouchLocation:)]) { + _addedInkView = _defaultInkView; + + if ([_delegate respondsToSelector:@selector(inkTouchController:insertInkView:intoView:)]) { + [_delegate inkTouchController:self insertInkView:_addedInkView intoView:_view]; + } else { + [_view addSubview:_addedInkView]; + } + } +} + +- (void)cancelInkTouchProcessing { + [_addedInkView cancelAllAnimationsAnimated:YES]; +} + +- (MDCInkView *_Nullable)inkViewAtTouchLocation:(CGPoint)location { + MDCInkView *inkView; + if ([_delegate respondsToSelector:@selector(inkTouchController:inkViewAtTouchLocation:)]) { + inkView = [_delegate inkTouchController:self inkViewAtTouchLocation:location]; + } else { + CGPoint locationInInkCoords = [self.view convertPoint:location toView:_addedInkView]; + if ([_addedInkView pointInside:locationInInkCoords withEvent:nil]) { + inkView = _addedInkView; + } + } + return inkView; +} + +- (void)handleInkGesture:(MDCInkGestureRecognizer *)recognizer { + CGPoint touchLocation = [recognizer locationInView:_view]; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + if ([_delegate respondsToSelector:@selector(inkTouchController:inkViewAtTouchLocation:)]) { + _addedInkView = [_delegate inkTouchController:self inkViewAtTouchLocation:touchLocation]; + if (!_addedInkView) { + return [self cancelInkGestureWithRecognizer:recognizer]; + } + NSAssert([_addedInkView isDescendantOfView:_view], + @"Ink view %@ returned by inkTouchController:inkViewAtTouchLocation: must be a " + "subview of base view %@", + _addedInkView, _view); + recognizer.targetBounds = [_addedInkView convertRect:_addedInkView.bounds toView:_view]; + } + + _shouldRespondToTouch = YES; + dispatch_time_t delayTime = + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * kInkTouchDelayInterval)); + dispatch_after(_delaysInkSpread ? delayTime : 0, dispatch_get_main_queue(), ^(void) { + [self touchBeganAtPoint:[recognizer locationInView:self.addedInkView] + touchLocation:touchLocation]; + }); + break; + } + case UIGestureRecognizerStatePossible: // Ignored + break; + case UIGestureRecognizerStateChanged: { + // Due to changes on iPhone 6s, possibly due to the force touch, + // @c UIGestureRecognizerStateChanged constantly fires. However, we do not want to cancel the + // ink unless the users moves. + if (_shouldRespondToTouch && !CGPointEqualToPoint(touchLocation, _previousLocation)) { + _shouldRespondToTouch = NO; + } + break; + } + case UIGestureRecognizerStateCancelled: + [_addedInkView cancelAllAnimationsAnimated:YES]; + _shouldRespondToTouch = NO; + break; + case UIGestureRecognizerStateRecognized: + [_addedInkView startTouchEndedAnimationAtPoint:touchLocation completion:nil]; + _shouldRespondToTouch = NO; + break; + case UIGestureRecognizerStateFailed: + [_addedInkView cancelAllAnimationsAnimated:YES]; + _shouldRespondToTouch = NO; + break; + } + + if (_shouldRespondToTouch) { + _previousLocation = touchLocation; + } else { + _previousLocation = CGPointZero; + } +} + +- (void)cancelInkGestureWithRecognizer:(MDCInkGestureRecognizer *)recognizer { + // To exit, disable the recognizer immediately which forces it to drop out of the current + // loop and prevent any state updates. Then re-enable to allow future gesture recognition. + recognizer.enabled = NO; + recognizer.enabled = YES; +} + +- (void)touchBeganAtPoint:(CGPoint)point touchLocation:(CGPoint)touchLocation { + if (_shouldRespondToTouch) { + [_addedInkView startTouchBeganAnimationAtPoint:point completion:nil]; + if ([_delegate respondsToSelector:@selector(inkTouchController: + didProcessInkView:atTouchLocation:)]) { + [_delegate inkTouchController:self + didProcessInkView:_addedInkView + atTouchLocation:touchLocation]; + } + _shouldRespondToTouch = NO; + } +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer + shouldRecognizeSimultaneouslyWithGestureRecognizer:(__unused UIGestureRecognizer *)other { + // Subclasses can override this to prioritize another recognizer. + return YES; +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { + if ([_delegate respondsToSelector:@selector(inkTouchController: + shouldProcessInkTouchesAtTouchLocation:)]) { + CGPoint touchLocation = [gestureRecognizer locationInView:_view]; + return [_delegate inkTouchController:self shouldProcessInkTouchesAtTouchLocation:touchLocation]; + } + return YES; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer + shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + if (self.requiresFailureOfScrollViewGestures && + [otherGestureRecognizer.view isKindOfClass:[UIScrollView class]] && + ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && + ![otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { + return YES; + } + return NO; +} + +#pragma mark - Deprecations + +- (MDCInkView *)inkView { + return _defaultInkView; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h new file mode 100644 index 00000000..87cd14ea --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h @@ -0,0 +1,84 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCInkTouchController; +@class MDCInkView; + +@protocol MDCInkTouchControllerDelegate +@optional + +/** + Inserts the ink view into the given view. + + If this method is not implemented, the ink view is added as a subview of the view when the + controller's addInkView method is called. Delegates can choose to insert the ink view below the + contents as a background view. When inkTouchController:inkViewAtTouchLocation is implemented + this method will not be invoked. + + @param inkTouchController The ink touch controller. + @param inkView The ink view. + @param view The view to add the ink view to. + */ +- (void)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController + insertInkView:(nonnull UIView *)inkView + intoView:(nonnull UIView *)view; + +/** + Returns the ink view to use for a touch located at location in inkTouchController.view. + + If the delegate implements this method, the controller will not create an ink view of its own and + inkTouchController:insertInkView:intoView: will not be called. This method allows the delegate + to control the creation and reuse of ink views. + + @param inkTouchController The ink touch controller. + @param location The touch location in the coords of @c inkTouchController.view. + @return An ink view to use at the touch location. + */ +- (nullable MDCInkView *)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController + inkViewAtTouchLocation:(CGPoint)location; + +/** + Controls whether the ink touch controller should be processing touches. + + The touch controller will query this method to determine if it should start or continue to + process touches controlling the ink. Returning NO at the start of a gesture will prevent any ink + from being displayed, and returning NO in the middle of a gesture will cancel that gesture and + evaporate the ink. + + If not implemented then YES is assumed. + + @param inkTouchController The ink touch controller. + @param location The touch location relative to the inkTouchController view. + @return YES if the controller should process touches at @c location. + + @see cancelInkTouchProcessing + */ +- (BOOL)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController + shouldProcessInkTouchesAtTouchLocation:(CGPoint)location; + +/** + Notifies the receiver that the ink touch controller did process an ink view at the + touch location. + + @param inkTouchController The ink touch controller. + @param inkView The ink view. + @param location The touch location relative to the inkTouchController superView. + */ +- (void)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController + didProcessInkView:(nonnull MDCInkView *)inkView + atTouchLocation:(CGPoint)location; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h new file mode 100644 index 00000000..e2d065c4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h @@ -0,0 +1,180 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCInkViewDelegate.h" + +@protocol MDCInkViewDelegate; + +/** Completion block signature for all ink animations. */ +typedef void (^MDCInkCompletionBlock)(void); + +/** Ink styles. */ +typedef NS_ENUM(NSInteger, MDCInkStyle) { + /** Ink is clipped to the view's bounds. */ + MDCInkStyleBounded, + /** Ink is not clipped to the view's bounds. */ + MDCInkStyleUnbounded +}; + +/** + A UIView that draws and animates the Material Design ink effect for touch interactions. + + There are two kinds of ink: + + Bounded ink: Ink that spreads from a point and is contained in the bounds of a UI element such as a + button. The ink is visually clipped to the bounds of the UI element. Bounded ink is the most + commonly-used ink in the system. Examples include basic Material buttons, list menu items, and tile + grids. + + Unbounded ink: Ink that spreads out from a point "on top" of other UI elements. It typically + reaches a maximum circle radius and then fades, unclipped by other UI elements. Typically used + when interacting with small UI elements such as navigation bar icons or slider "thumb" controls. + Examples include overflow menus, icon toggle buttons, and phone dialer keys. + + Note that the two kinds of ink are designed to have different animation parameters, that is, + bounded ink isn't just clipped unbounded ink. Whether the ink is bounded or not depends on the kind + of UI element the user is interacting with. + */ +__deprecated_msg("Please use MDCRippleView instead.") @interface MDCInkView : UIView + +/** + Ink view animation delegate. Clients set this delegate to receive updates when ink animations + start and end. + */ +@property(nonatomic, weak, nullable) id animationDelegate; + +/** + The style of ink for this view. Defaults to MDCInkStyleBounded. + + Changes only affect subsequent animations, not animations in progress. + */ +@property(nonatomic, assign) MDCInkStyle inkStyle; + +/** The foreground color of the ink. The default value is defaultInkColor. */ +@property(nonatomic, strong, null_resettable) UIColor *inkColor UI_APPEARANCE_SELECTOR; + +/** Default color used for ink if no color is specified. */ +@property(nonatomic, strong, readonly, nonnull) UIColor *defaultInkColor; + +/** + Maximum radius of the ink. If the radius <= 0 then half the length of the diagonal of self.bounds + is used. This value is ignored if @c inkStyle is set to MDCInkStyleBounded and @c + usesLegacyInkLayer is set to NO. + */ +@property(nonatomic, assign) CGFloat maxRippleRadius; + +/** + Use the older legacy version of the ink ripple. Default is YES. + */ +@property(nonatomic, assign) BOOL usesLegacyInkRipple; + +/** + Use a custom center for the ink splash. If YES, then customInkCenter is used, otherwise the + center of self.bounds is used. Default is NO. + + Affects behavior only if usesLegacyInkRipple is enabled. + */ +@property(nonatomic, assign) BOOL usesCustomInkCenter; + +/** + Custom center for the ink splash in the view’s coordinate system. + + Affects behavior only if both usesCustomInkCenter and usesLegacyInkRipple are enabled. + */ +@property(nonatomic, assign) CGPoint customInkCenter; + +/** + A block that is invoked when the @c MDCInkView receives a call to @c + traitCollectionDidChange:. The block is called after the call to the superclass. + */ +@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) + (MDCInkView *_Nonnull ink, UITraitCollection *_Nullable previousTraitCollection); + +/** + Start the first part of the "press and release" animation at a particular point. + + The "press and release" animation begins by fading in the ink ripple when this method is called. + + @param point The user interaction position in the view’s coordinate system. + @param completionBlock Block called after the completion of the animation. + */ +- (void)startTouchBeganAnimationAtPoint:(CGPoint)point + completion:(nullable MDCInkCompletionBlock)completionBlock; + +/** + Start the second part of the "press and release" animation at a particular point. + + The "press and release" animation ends by completing the ink ripple expansion while fading out when + this method is called. + + @param point The user interaction position in the view’s coordinate system. + @param completionBlock Block called after the completion of the animation. + */ +- (void)startTouchEndedAnimationAtPoint:(CGPoint)point + completion:(nullable MDCInkCompletionBlock)completionBlock; + +/** + Cancel all animations. + + @param animated If false, remove the animations immediately. + */ +- (void)cancelAllAnimationsAnimated:(BOOL)animated; + +/** + Start the first part of spreading the ink at a particular point. + + This begins by fading in the ink ripple when this method is called. + + @param point The user interaction position in the view’s coordinate system. + @param animated to add the ink sublayer with animation or not. + @param completionBlock Block called after the completion of the animation. + */ +- (void)startTouchBeganAtPoint:(CGPoint)point + animated:(BOOL)animated + withCompletion:(nullable MDCInkCompletionBlock)completionBlock; + +/** + Start the second part of evaporating the ink at a particular point. + + This ends by completing the ink ripple expansion while fading out when + this method is called. + + @param point The user interaction position in the view’s coordinate system. + @param animated to remove the ink sublayer with animation or not. + @param completionBlock Block called after the completion of the animation. + */ +- (void)startTouchEndAtPoint:(CGPoint)point + animated:(BOOL)animated + withCompletion:(nullable MDCInkCompletionBlock)completionBlock; + +/** + Enumerates the given view's subviews for an instance of MDCInkView and returns it if found, or + creates and adds a new instance of MDCInkView if not. + + This method is a convenience method for adding ink to an arbitrary view without needing to subclass + the target view. Use this method in situations where you expect there to be many distinct ink views + in existence for a single ink touch controller. Example scenarios include: + + - Adding ink to individual collection view/table view cells + + This method can be used in your MDCInkTouchController delegate's + -inkTouchController:inkViewAtTouchLocation; implementation. + */ ++ (nonnull MDCInkView *)injectedInkViewForView:(nonnull UIView *)view; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m new file mode 100644 index 00000000..58c80ee8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m @@ -0,0 +1,326 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCInkView.h" + +#import "private/MDCInkLayer.h" +#import "private/MDCLegacyInkLayer.h" +#import "MDCInkViewDelegate.h" +#import "MDCInkLayerDelegate.h" +#import "MDCLegacyInkLayerDelegate.h" + +@interface MDCInkPendingAnimation : NSObject + +@property(nonatomic, weak) CALayer *animationSourceLayer; +@property(nonatomic, strong) NSString *keyPath; +@property(nonatomic, strong) id fromValue; +@property(nonatomic, strong) id toValue; + +@end + +@interface MDCInkView () + +@property(nonatomic, strong) CAShapeLayer *maskLayer; +@property(nonatomic, copy) MDCInkCompletionBlock startInkRippleCompletionBlock; +@property(nonatomic, copy) MDCInkCompletionBlock endInkRippleCompletionBlock; +@property(nonatomic, strong) MDCInkLayer *activeInkLayer; + +// Legacy ink ripple +@property(nonatomic, readonly, weak) MDCLegacyInkLayer *inkLayer; + +@end + +@implementation MDCInkView + ++ (Class)layerClass { + return [MDCLegacyInkLayer class]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCInkViewInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCInkViewInit]; + } + return self; +} + +- (void)commonMDCInkViewInit { + self.userInteractionEnabled = NO; + self.backgroundColor = [UIColor clearColor]; + self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + self.layer.delegate = self; + self.inkColor = self.defaultInkColor; + _usesLegacyInkRipple = YES; + + // Use mask layer when the superview has a shadowPath. + _maskLayer = [CAShapeLayer layer]; + _maskLayer.delegate = self; + + self.inkLayer.animationDelegate = self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + if (self.inkStyle == MDCInkStyleUnbounded) { + self.layer.mask = nil; + } else if (self.superview.layer.shadowPath) { + // If the superview has a shadowPath make sure ink does not spread outside of the shadowPath. + self.maskLayer.path = self.superview.layer.shadowPath; + self.layer.mask = _maskLayer; + } + + CGRect inkBounds = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); + self.layer.bounds = inkBounds; + + // When bounds change ensure all ink layer bounds are changed too. + for (CALayer *layer in self.layer.sublayers) { + if ([layer isKindOfClass:[MDCInkLayer class]]) { + MDCInkLayer *inkLayer = (MDCInkLayer *)layer; + inkLayer.bounds = inkBounds; + inkLayer.fillColor = self.inkColor.CGColor; + } + } +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + + if (self.traitCollectionDidChangeBlock) { + self.traitCollectionDidChangeBlock(self, previousTraitCollection); + } +} + +- (void)setInkStyle:(MDCInkStyle)inkStyle { + _inkStyle = inkStyle; + switch (inkStyle) { + case MDCInkStyleBounded: + self.inkLayer.masksToBounds = YES; + self.inkLayer.bounded = YES; + break; + case MDCInkStyleUnbounded: + self.inkLayer.masksToBounds = NO; + self.inkLayer.bounded = NO; + break; + } +} + +- (UIColor *)inkColor { + return self.inkLayer.inkColor; +} + +- (void)setInkColor:(UIColor *)inkColor { + self.inkLayer.inkColor = inkColor ?: self.defaultInkColor; +} + +- (CGFloat)maxRippleRadius { + return [self shouldIgnoreMaxRippleRadius] ? 0 : self.inkLayer.maxRippleRadius; +} + +- (void)setMaxRippleRadius:(CGFloat)radius { + self.inkLayer.maxRippleRadius = radius; + // This is required for legacy Ink so that the Ink bounds will be adjusted correctly + [self setNeedsLayout]; +} + +- (BOOL)shouldIgnoreMaxRippleRadius { + return !self.usesLegacyInkRipple && self.inkStyle == MDCInkStyleBounded; +} + +- (BOOL)usesCustomInkCenter { + return self.inkLayer.useCustomInkCenter; +} + +- (void)setUsesCustomInkCenter:(BOOL)usesCustomInkCenter { + self.inkLayer.useCustomInkCenter = usesCustomInkCenter; +} + +- (CGPoint)customInkCenter { + return self.inkLayer.customInkCenter; +} + +- (void)setCustomInkCenter:(CGPoint)customInkCenter { + self.inkLayer.customInkCenter = customInkCenter; +} + +- (MDCLegacyInkLayer *)inkLayer { + return (MDCLegacyInkLayer *)self.layer; +} + +- (void)startTouchBeganAnimationAtPoint:(CGPoint)point + completion:(MDCInkCompletionBlock)completionBlock { + [self startTouchBeganAtPoint:point animated:YES withCompletion:completionBlock]; +} + +- (void)startTouchBeganAtPoint:(CGPoint)point + animated:(BOOL)animated + withCompletion:(nullable MDCInkCompletionBlock)completionBlock { + if (self.usesLegacyInkRipple) { + [self.inkLayer spreadFromPoint:point completion:completionBlock]; + } else { + self.startInkRippleCompletionBlock = completionBlock; + MDCInkLayer *inkLayer = [MDCInkLayer layer]; + inkLayer.inkColor = self.inkColor; + inkLayer.maxRippleRadius = self.maxRippleRadius; + inkLayer.animationDelegate = self; + inkLayer.opacity = 0; + inkLayer.frame = self.bounds; + [self.layer addSublayer:inkLayer]; + [inkLayer startInkAtPoint:point animated:animated]; + self.activeInkLayer = inkLayer; + } +} + +- (void)startTouchEndAtPoint:(CGPoint)point + animated:(BOOL)animated + withCompletion:(nullable MDCInkCompletionBlock)completionBlock { + if (self.usesLegacyInkRipple) { + [self.inkLayer evaporateWithCompletion:completionBlock]; + } else { + self.endInkRippleCompletionBlock = completionBlock; + [self.activeInkLayer endInkAtPoint:point animated:animated]; + } +} + +- (void)startTouchEndedAnimationAtPoint:(CGPoint)point + completion:(MDCInkCompletionBlock)completionBlock { + [self startTouchEndAtPoint:point animated:YES withCompletion:completionBlock]; +} + +- (void)cancelAllAnimationsAnimated:(BOOL)animated { + if (self.usesLegacyInkRipple) { + [self.inkLayer resetAllInk:animated]; + } else { + NSArray *sublayers = [self.layer.sublayers copy]; + for (CALayer *layer in sublayers) { + if ([layer isKindOfClass:[MDCInkLayer class]]) { + MDCInkLayer *inkLayer = (MDCInkLayer *)layer; + if (animated) { + [inkLayer endAnimationAtPoint:CGPointZero]; + } else { + [inkLayer removeFromSuperlayer]; + } + } + } + } +} + +- (UIColor *)defaultInkColor { + static UIColor *defaultInkColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultInkColor = [[UIColor alloc] initWithWhite:0 alpha:(CGFloat)0.14]; + }); + return defaultInkColor; +} + ++ (MDCInkView *)injectedInkViewForView:(UIView *)view { + MDCInkView *foundInkView = nil; + for (MDCInkView *subview in view.subviews) { + if ([subview isKindOfClass:[MDCInkView class]]) { + foundInkView = subview; + break; + } + } + if (!foundInkView) { + foundInkView = [[MDCInkView alloc] initWithFrame:view.bounds]; + foundInkView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [view addSubview:foundInkView]; + } + return foundInkView; +} + +#pragma mark - MDCLegacyInkLayerDelegate + +- (void)legacyInkLayerAnimationDidStart:(MDCLegacyInkLayer *)inkLayer { + if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidStart:)]) { + [self.animationDelegate inkAnimationDidStart:self]; + } +} + +- (void)legacyInkLayerAnimationDidEnd:(MDCLegacyInkLayer *)inkLayer { + if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidEnd:)]) { + [self.animationDelegate inkAnimationDidEnd:self]; + } +} + +#pragma mark - MDCInkLayerDelegate + +- (void)inkLayerAnimationDidStart:(MDCInkLayer *)inkLayer { + if (self.activeInkLayer == inkLayer && self.startInkRippleCompletionBlock) { + self.startInkRippleCompletionBlock(); + } + if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidStart:)]) { + [self.animationDelegate inkAnimationDidStart:self]; + } +} + +- (void)inkLayerAnimationDidEnd:(MDCInkLayer *)inkLayer { + if (self.activeInkLayer == inkLayer && self.endInkRippleCompletionBlock) { + self.endInkRippleCompletionBlock(); + } + if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidEnd:)]) { + [self.animationDelegate inkAnimationDidEnd:self]; + } +} + +#pragma mark - CALayerDelegate + +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { + if ([event isEqualToString:@"path"] || [event isEqualToString:@"shadowPath"]) { + // We have to create a pending animation because if we are inside a UIKit animation block we + // won't know any properties of the animation block until it is commited. + MDCInkPendingAnimation *pendingAnim = [[MDCInkPendingAnimation alloc] init]; + pendingAnim.animationSourceLayer = self.superview.layer; + pendingAnim.fromValue = [layer.presentationLayer valueForKey:event]; + pendingAnim.toValue = nil; + pendingAnim.keyPath = event; + + return pendingAnim; + } + return nil; +} + +@end + +@implementation MDCInkPendingAnimation + +- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict { + if ([anObject isKindOfClass:[CAShapeLayer class]]) { + CAShapeLayer *layer = (CAShapeLayer *)anObject; + + // In order to synchronize our animation with UIKit animations we have to fetch the resizing + // animation created by UIKit and copy the configuration to our custom animation. + CAAnimation *boundsAction = [self.animationSourceLayer animationForKey:@"bounds.size"]; + if ([boundsAction isKindOfClass:[CABasicAnimation class]]) { + CABasicAnimation *animation = (CABasicAnimation *)[boundsAction copy]; + animation.keyPath = self.keyPath; + animation.fromValue = self.fromValue; + animation.toValue = self.toValue; + + [layer addAnimation:animation forKey:event]; + } + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h new file mode 100644 index 00000000..1afa84af --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h @@ -0,0 +1,41 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCInkView; + +/** + Delegate protocol for MDCInkView. Clients may implement this protocol to receive updates when ink + layer start and end. + */ +__deprecated_msg("Please use MDCRippleViewDelegate instead.") @protocol MDCInkViewDelegate + +@optional + +/** + Called when the ink ripple animation begins. + + @param inkView The MDCInkView that starts animating. + */ +- (void)inkAnimationDidStart:(nonnull MDCInkView *)inkView; + +/** + Called when the ink ripple animation ends. + + @param inkView The MDCInkView that ends animating. + */ +- (void)inkAnimationDidEnd:(nonnull MDCInkView *)inkView; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h new file mode 100644 index 00000000..de515671 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h @@ -0,0 +1,19 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCInkGestureRecognizer.h" +#import "MDCInkTouchController.h" +#import "MDCInkTouchControllerDelegate.h" +#import "MDCInkView.h" +#import "MDCInkViewDelegate.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h new file mode 100644 index 00000000..711a7833 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h @@ -0,0 +1,108 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCInkLayerDelegate.h" + +@protocol MDCInkLayerDelegate; + +/** + A Core Animation layer that draws and animates the ink effect. + + Quick summary of how the ink ripple works: + + 1. On touch down, ink spreads from the touch point. + 2. On touch down hold, ink continues to spread, but will gravitate to the center point + of the view. + 3. On touch up, the ink ripple opacity will start to decrease. + */ +@interface MDCInkLayer : CAShapeLayer + +/** + Ink layer animation delegate. Clients set this delegate to receive updates when ink layer + animations start and end. + */ +@property(nonatomic, weak, nullable) id animationDelegate; + +/** + The start ink ripple spread animation has started and is active. + */ +@property(nonatomic, assign, readonly, getter=isStartAnimationActive) BOOL startAnimationActive; + +/** + Delay time in milliseconds before the end ink ripple spread animation begins. + */ +@property(nonatomic, assign) CGFloat endAnimationDelay; + +/** + The radius the ink ripple grows to when ink ripple ends. + + Default value is half the diagonal of the containing frame plus 10pt. + */ +@property(nonatomic, assign) CGFloat finalRadius; + +/** + The radius the ink ripple starts to grow from when the ink ripple begins. + + Default value is half the diagonal of the containing frame multiplied by 0.6. + */ +@property(nonatomic, assign) CGFloat initialRadius; + +/** + Maximum radius of the ink. If this is not set then the final radius value is used. + */ +@property(nonatomic, assign) CGFloat maxRippleRadius; + +/** + The color of the ink ripple. + */ +@property(nonatomic, strong, nonnull) UIColor *inkColor; + +/** + Starts the ink ripple animation at a specified point. + */ +- (void)startAnimationAtPoint:(CGPoint)point; + +/** + Starts the ink ripple + + @param point the point where to start the ink ripple + @param animated if to animate the ripple or not + */ +- (void)startInkAtPoint:(CGPoint)point animated:(BOOL)animated; + +/** + Changes the opacity of the ink ripple depending on if touch point is contained within or + outside of the ink layer. + */ +- (void)changeAnimationAtPoint:(CGPoint)point; + +/** + Ends the ink ripple animation. + */ +- (void)endAnimationAtPoint:(CGPoint)point; + +/** + Ends the ink ripple + + @param point the point where to end the ink ripple + @param animated if to animate the ripple or not + */ +- (void)endInkAtPoint:(CGPoint)point animated:(BOOL)animated; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m new file mode 100644 index 00000000..6f53b0b0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m @@ -0,0 +1,246 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCInkLayer.h" + +#import "MDCInkLayerDelegate.h" + +static const CGFloat MDCInkLayerCommonDuration = (CGFloat)0.083; +static const CGFloat MDCInkLayerEndFadeOutDuration = (CGFloat)0.15; +static const CGFloat MDCInkLayerStartScalePositionDuration = (CGFloat)0.333; +static const CGFloat MDCInkLayerStartFadeHalfDuration = (CGFloat)0.167; +static const CGFloat MDCInkLayerStartFadeHalfBeginTimeFadeOutDuration = (CGFloat)0.25; + +static const CGFloat MDCInkLayerScaleStartMin = (CGFloat)0.2; +static const CGFloat MDCInkLayerScaleStartMax = (CGFloat)0.6; +static const CGFloat MDCInkLayerScaleDivisor = 300; + +static NSString *const MDCInkLayerOpacityString = @"opacity"; +static NSString *const MDCInkLayerPositionString = @"position"; +static NSString *const MDCInkLayerScaleString = @"transform.scale"; + +@implementation MDCInkLayer + +- (instancetype)init { + self = [super init]; + if (self) { + _inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; + } + return self; +} + +- (instancetype)initWithLayer:(id)layer { + self = [super initWithLayer:layer]; + if (self) { + _endAnimationDelay = 0; + _finalRadius = 0; + _initialRadius = 0; + _inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; + _startAnimationActive = NO; + if ([layer isKindOfClass:[MDCInkLayer class]]) { + MDCInkLayer *inkLayer = (MDCInkLayer *)layer; + _endAnimationDelay = inkLayer.endAnimationDelay; + _finalRadius = inkLayer.finalRadius; + _initialRadius = inkLayer.initialRadius; + _maxRippleRadius = inkLayer.maxRippleRadius; + _inkColor = inkLayer.inkColor; + _startAnimationActive = NO; + } + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + + if (self) { + _inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; + } + + return self; +} + +- (void)setNeedsLayout { + [super setNeedsLayout]; + [self setRadiiWithRect:self.bounds]; +} + +- (void)setRadiiWithRect:(CGRect)rect { + self.initialRadius = + (CGFloat)(hypot(CGRectGetHeight(rect), CGRectGetWidth(rect)) / 2 * (CGFloat)0.6); + self.finalRadius = (CGFloat)(hypot(CGRectGetHeight(rect), CGRectGetWidth(rect)) / 2 + 10); +} + +- (void)startAnimationAtPoint:(CGPoint)point { + [self startInkAtPoint:point animated:YES]; +} + +- (void)startInkAtPoint:(CGPoint)point animated:(BOOL)animated { + CGFloat radius = self.finalRadius; + if (self.maxRippleRadius > 0) { + radius = self.maxRippleRadius; + } + CGRect ovalRect = CGRectMake(CGRectGetWidth(self.bounds) / 2 - radius, + CGRectGetHeight(self.bounds) / 2 - radius, radius * 2, radius * 2); + UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:ovalRect]; + self.path = circlePath.CGPath; + self.fillColor = self.inkColor.CGColor; + if (!animated) { + self.opacity = 1; + self.position = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2); + } else { + self.opacity = 0; + self.position = point; + _startAnimationActive = YES; + + CAMediaTimingFunction *materialTimingFunction = + [[CAMediaTimingFunction alloc] initWithControlPoints:(float) 0.4:0:(float)0.2:1]; + + CGFloat scaleStart = + MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)) / MDCInkLayerScaleDivisor; + if (scaleStart < MDCInkLayerScaleStartMin) { + scaleStart = MDCInkLayerScaleStartMin; + } else if (scaleStart > MDCInkLayerScaleStartMax) { + scaleStart = MDCInkLayerScaleStartMax; + } + + CABasicAnimation *scaleAnim = [[CABasicAnimation alloc] init]; + scaleAnim.keyPath = MDCInkLayerScaleString; + scaleAnim.fromValue = @(scaleStart); + scaleAnim.toValue = @1; + scaleAnim.duration = MDCInkLayerStartScalePositionDuration; + scaleAnim.beginTime = MDCInkLayerCommonDuration; + scaleAnim.timingFunction = materialTimingFunction; + scaleAnim.fillMode = kCAFillModeForwards; + scaleAnim.removedOnCompletion = NO; + + UIBezierPath *centerPath = [UIBezierPath bezierPath]; + CGPoint startPoint = point; + CGPoint endPoint = + CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2); + [centerPath moveToPoint:startPoint]; + [centerPath addLineToPoint:endPoint]; + [centerPath closePath]; + + CAKeyframeAnimation *positionAnim = [[CAKeyframeAnimation alloc] init]; + positionAnim.keyPath = MDCInkLayerPositionString; + positionAnim.path = centerPath.CGPath; + positionAnim.keyTimes = @[ @0, @1 ]; + positionAnim.values = @[ @0, @1 ]; + positionAnim.duration = MDCInkLayerStartScalePositionDuration; + positionAnim.beginTime = MDCInkLayerCommonDuration; + positionAnim.timingFunction = materialTimingFunction; + positionAnim.fillMode = kCAFillModeForwards; + positionAnim.removedOnCompletion = NO; + + CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; + fadeInAnim.keyPath = MDCInkLayerOpacityString; + fadeInAnim.fromValue = @0; + fadeInAnim.toValue = @1; + fadeInAnim.duration = MDCInkLayerCommonDuration; + fadeInAnim.beginTime = MDCInkLayerCommonDuration; + fadeInAnim.timingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + fadeInAnim.fillMode = kCAFillModeForwards; + fadeInAnim.removedOnCompletion = NO; + + [CATransaction begin]; + CAAnimationGroup *animGroup = [[CAAnimationGroup alloc] init]; + animGroup.animations = @[ scaleAnim, positionAnim, fadeInAnim ]; + animGroup.duration = MDCInkLayerStartScalePositionDuration; + animGroup.fillMode = kCAFillModeForwards; + animGroup.removedOnCompletion = NO; + [CATransaction setCompletionBlock:^{ + self->_startAnimationActive = NO; + }]; + [self addAnimation:animGroup forKey:nil]; + [CATransaction commit]; + } + if ([self.animationDelegate respondsToSelector:@selector(inkLayerAnimationDidStart:)]) { + [self.animationDelegate inkLayerAnimationDidStart:self]; + } +} + +- (void)changeAnimationAtPoint:(CGPoint)point { + CGFloat animationDelay = 0; + if (self.startAnimationActive) { + animationDelay = + MDCInkLayerStartFadeHalfBeginTimeFadeOutDuration + MDCInkLayerStartFadeHalfDuration; + } + + BOOL viewContainsPoint = CGRectContainsPoint(self.bounds, point) ? YES : NO; + CGFloat currOpacity = self.presentationLayer.opacity; + CGFloat updatedOpacity = 0; + if (viewContainsPoint) { + updatedOpacity = 1; + } + + CABasicAnimation *changeAnim = [[CABasicAnimation alloc] init]; + changeAnim.keyPath = MDCInkLayerOpacityString; + changeAnim.fromValue = @(currOpacity); + changeAnim.toValue = @(updatedOpacity); + changeAnim.duration = MDCInkLayerCommonDuration; + changeAnim.beginTime = [self convertTime:(CACurrentMediaTime() + animationDelay) fromLayer:nil]; + changeAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + changeAnim.fillMode = kCAFillModeForwards; + changeAnim.removedOnCompletion = NO; + [self addAnimation:changeAnim forKey:nil]; +} + +- (void)endAnimationAtPoint:(CGPoint)point { + [self endInkAtPoint:point animated:YES]; +} + +- (void)endInkAtPoint:(CGPoint)point animated:(BOOL)animated { + if (self.startAnimationActive) { + self.endAnimationDelay = MDCInkLayerStartFadeHalfBeginTimeFadeOutDuration; + } + + CGFloat opacity = 1; + BOOL viewContainsPoint = CGRectContainsPoint(self.bounds, point) ? YES : NO; + if (!viewContainsPoint) { + opacity = 0; + } + + if (!animated) { + self.opacity = 0; + if ([self.animationDelegate respondsToSelector:@selector(inkLayerAnimationDidEnd:)]) { + [self.animationDelegate inkLayerAnimationDidEnd:self]; + } + [self removeFromSuperlayer]; + } else { + [CATransaction begin]; + CABasicAnimation *fadeOutAnim = [[CABasicAnimation alloc] init]; + fadeOutAnim.keyPath = MDCInkLayerOpacityString; + fadeOutAnim.fromValue = @(opacity); + fadeOutAnim.toValue = @0; + fadeOutAnim.duration = MDCInkLayerEndFadeOutDuration; + fadeOutAnim.beginTime = [self convertTime:(CACurrentMediaTime() + self.endAnimationDelay) + fromLayer:nil]; + fadeOutAnim.timingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + fadeOutAnim.fillMode = kCAFillModeForwards; + fadeOutAnim.removedOnCompletion = NO; + [CATransaction setCompletionBlock:^{ + if ([self.animationDelegate respondsToSelector:@selector(inkLayerAnimationDidEnd:)]) { + [self.animationDelegate inkLayerAnimationDidEnd:self]; + } + [self removeFromSuperlayer]; + }]; + [self addAnimation:fadeOutAnim forKey:nil]; + [CATransaction commit]; + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h new file mode 100644 index 00000000..8eb1797c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h @@ -0,0 +1,41 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCInkLayer; + +/** + Delegate protocol for the MDCInkLayer. Clients may implement this protocol to receive updates when + ink layer animations start and end. + */ +@protocol MDCInkLayerDelegate + +@optional + +/** + Called when the ink ripple animation begins. + + @param inkLayer The MDCInkLayer that starts animating. + */ +- (void)inkLayerAnimationDidStart:(nonnull MDCInkLayer *)inkLayer; + +/** + Called when the ink ripple animation ends. + + @param inkLayer The MDCInkLayer that ends animating. + */ +- (void)inkLayerAnimationDidEnd:(nonnull MDCInkLayer *)inkLayer; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h new file mode 100644 index 00000000..263fa3b5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h @@ -0,0 +1,46 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCLegacyInkLayer.h" +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCLegacyInkLayerRippleDelegate.h" + +@class MDCLegacyInkLayerRipple; +@protocol MDCLegacyInkLayerRippleDelegate; + +@interface MDCLegacyInkLayer () + +/// A Boolean value indicating whether animations of this layer are in progress. +@property(nonatomic, assign, getter=isAnimating) BOOL animating; + +/// Enter any ink applied to the layer. Currently only exposed for testing. +- (void)enterAllInks; + +@end + +@interface MDCLegacyInkLayerRipple : CAShapeLayer +@end + +@interface MDCLegacyInkLayerForegroundRipple : MDCLegacyInkLayerRipple + +- (void)exit:(BOOL)animated; + +@end + +@interface MDCLegacyInkLayerBackgroundRipple : MDCLegacyInkLayerRipple + +- (void)exit:(BOOL)animated; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h new file mode 100644 index 00000000..cc93304c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h @@ -0,0 +1,112 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCLegacyInkLayerDelegate.h" + +@protocol MDCLegacyInkLayerDelegate; + +/** + A Core Animation layer that draws and animates the ink effect. + + Quick summary of how the ink ripple works: + + 1. On touch down, blast initiates from the touch point. + 2. On touch down hold, it continues to spread, but will gravitate to the center point + of the view. + 3. On touch up, the ink ripple will lose energy, opacity will start to decrease. + */ +@interface MDCLegacyInkLayer : CALayer + +/** + Ink layer animation delegate. Clients set this delegate to receive updates when ink layer + animations start and end. + */ +@property(nonatomic, weak, nullable) id animationDelegate; + +/** Clips the ripple to the bounds of the layer. */ +@property(nonatomic, assign, getter=isBounded) BOOL bounded; + +/** Maximum radius of the ink. No maximum if radius is 0 or less. This value is ignored if + @c bounded is set to |YES|.*/ +@property(nonatomic, assign) CGFloat maxRippleRadius; + +/** Set the foreground color of the ink. */ +@property(nonatomic, strong, nonnull) UIColor *inkColor; + +/** Spread duration. */ +@property(nonatomic, readonly, assign) NSTimeInterval spreadDuration; + +/** Evaporate duration */ +@property(nonatomic, readonly, assign) NSTimeInterval evaporateDuration; + +/** + Set to YES if the ink layer should be using a custom center. + */ +@property(nonatomic, assign) BOOL useCustomInkCenter; + +/** + Center point which ink gravitates towards. + + Ignored if useCustomInkCenter is not set. + */ +@property(nonatomic, assign) CGPoint customInkCenter; + +/** + Whether linear expansion should be used for the ink, rather than a Quantum curve. Useful for + ink which needs to fill the bounds of its view completely and leave those bounds at full speed. + */ +@property(nonatomic, assign) BOOL userLinearExpansion; + +/** + Reset any ink applied to the layer. + + @param animated Enables the ink ripple fade out animation. + */ +- (void)resetAllInk:(BOOL)animated; + +/** + Spreads the ink over the whole view. + + Can be called multiple times which will result in multiple ink ripples. + + @param completionBlock Block called after the completion of the animation. + @param point Point at which the ink spreads from. + */ +- (void)spreadFromPoint:(CGPoint)point completion:(void (^_Nullable)(void))completionBlock; + +/** + Dissipate ink blast, should be called on touch up. + + If there are multiple ripples at once, the oldest ripple will be evaporated. + + @param completionBlock Block called after the completion of the evaporation. + */ +- (void)evaporateWithCompletion:(void (^_Nullable)(void))completionBlock; + +/** + Dissipates the ink blast, but condenses to a point. Used for touch exit or cancel. + + If there are mulitple ripples, the oldest ripple will be evaporated. + + @param point Evaporate the ink towards the point. + @param completionBlock Block called after the completion of the evaporation. + */ +- (void)evaporateToPoint:(CGPoint)point completion:(void (^_Nullable)(void))completionBlock; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m new file mode 100644 index 00000000..7d29db72 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m @@ -0,0 +1,664 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCLegacyInkLayer.h" +#import "MDCLegacyInkLayer+Private.h" + +#import + +#import "MaterialAvailability.h" +#import "MDCLegacyInkLayerDelegate.h" +#import "MDCLegacyInkLayerRippleDelegate.h" + +static inline CGPoint MDCLegacyInkLayerInterpolatePoint(CGPoint start, + CGPoint end, + CGFloat offsetPercent) { + CGPoint centerOffsetPoint = CGPointMake(start.x + (end.x - start.x) * offsetPercent, + start.y + (end.y - start.y) * offsetPercent); + return centerOffsetPoint; +} + +static inline CGFloat MDCLegacyInkLayerRadiusBounds(CGFloat maxRippleRadius, + CGFloat inkLayerRectHypotenuse, + __unused BOOL bounded) { + if (maxRippleRadius > 0) { +#ifdef MDC_BOUNDED_INK_IGNORES_MAX_RIPPLE_RADIUS + if (!bounded) { + return maxRippleRadius; + } else { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog(@"Implementation of MDCInkView with |MDCInkStyle| MDCInkStyleBounded and " + @"maxRippleRadius has changed.\n\n" + @"MDCInkStyleBounded ignores maxRippleRadius. " + @"Please use |MDCInkStyle| MDCInkStyleUnbounded to continue using maxRippleRadius."); + }); + return inkLayerRectHypotenuse; + } +#else + return maxRippleRadius; +#endif + } else { + return inkLayerRectHypotenuse; + } +} + +static inline CGFloat MDCLegacyInkLayerRandom() { + const uint32_t max_value = 10000; + return (CGFloat)arc4random_uniform(max_value + 1) / max_value; +} + +static inline CGPoint MDCLegacyInkLayerRectGetCenter(CGRect rect) { + return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); +} + +static inline CGFloat MDCLegacyInkLayerRectHypotenuse(CGRect rect) { + return (CGFloat)hypot(CGRectGetWidth(rect), CGRectGetHeight(rect)); +} + +static NSString *const kInkLayerOpacity = @"opacity"; +static NSString *const kInkLayerPosition = @"position"; +static NSString *const kInkLayerScale = @"transform.scale"; + +// State tracking for ink. +typedef NS_ENUM(NSInteger, MDCInkRippleState) { + kInkRippleNone, + kInkRippleSpreading, + kInkRippleComplete, + kInkRippleCancelled, +}; + +#if MDC_AVAILABLE_SDK_IOS(10_0) +@interface MDCLegacyInkLayerRipple () +@end +#endif // MDC_AVAILABLE_SDK_IOS(10_0) + +@interface MDCLegacyInkLayerRipple () + +@property(nonatomic, assign, getter=isAnimationCleared) BOOL animationCleared; +@property(nonatomic, weak) id animationDelegate; +@property(nonatomic, assign) BOOL bounded; +@property(nonatomic, weak) CALayer *inkLayer; +@property(nonatomic, assign) CGFloat radius; +@property(nonatomic, assign) CGPoint point; +@property(nonatomic, assign) CGRect targetFrame; +@property(nonatomic, assign) MDCInkRippleState rippleState; +@property(nonatomic, strong) UIColor *color; +@end + +@implementation MDCLegacyInkLayerRipple + +- (instancetype)init { + self = [super init]; + if (self) { + _rippleState = kInkRippleNone; + _animationCleared = YES; + } + return self; +} + +- (void)setupRipple { + self.fillColor = self.color.CGColor; + CGFloat dim = self.radius * 2; + self.frame = CGRectMake(0, 0, dim, dim); + UIBezierPath *ripplePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, dim, dim)]; + self.path = ripplePath.CGPath; +} + +- (void)enter { + [self.animationDelegate animationDidStart:self]; + + _rippleState = kInkRippleSpreading; + [_inkLayer addSublayer:self]; + _animationCleared = NO; +} + +- (void)exit { + if (_rippleState != kInkRippleCancelled) { + _rippleState = kInkRippleComplete; + } +} + +- (CAKeyframeAnimation *)opacityAnimWithValues:(NSArray *)values + times:(NSArray *)times { + CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:kInkLayerOpacity]; + anim.fillMode = kCAFillModeForwards; + anim.keyTimes = times; + anim.removedOnCompletion = NO; + anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + anim.values = values; + return anim; +} + +- (CAKeyframeAnimation *)positionAnimWithPath:(CGPathRef)path + duration:(CGFloat)duration + timingFunction:(CAMediaTimingFunction *)timingFunction { + CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:kInkLayerPosition]; + anim.duration = duration; + anim.fillMode = kCAFillModeForwards; + anim.path = path; + anim.removedOnCompletion = NO; + anim.timingFunction = timingFunction; + return anim; +} + +- (CAKeyframeAnimation *)scaleAnimWithValues:(NSArray *)values + times:(NSArray *)times { + CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:kInkLayerScale]; + anim.fillMode = kCAFillModeForwards; + anim.keyTimes = times; + anim.removedOnCompletion = NO; + anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + anim.values = values; + return anim; +} + +- (CAMediaTimingFunction *)logDecelerateEasing { + // This bezier curve is an approximation of a log curve. + return + [[CAMediaTimingFunction alloc] initWithControlPoints:(float) + 0.157:(float)0.72:(float)0.386:(float)0.987]; +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)finished { + if (!self.isAnimationCleared) { + [self.animationDelegate animationDidStop:anim shapeLayer:self finished:finished]; + } +} + +@end + +static CGFloat const kInkLayerForegroundBoundedOpacityExitDuration = (CGFloat)0.4; +static CGFloat const kInkLayerForegroundBoundedPositionExitDuration = (CGFloat)0.3; +static CGFloat const kInkLayerForegroundBoundedRadiusExitDuration = (CGFloat)0.8; +static CGFloat const kInkLayerForegroundRadiusGrowthMultiplier = 350; +static CGFloat const kInkLayerForegroundUnboundedEnterDelay = (CGFloat)0.08; +static CGFloat const kInkLayerForegroundUnboundedOpacityEnterDuration = (CGFloat)0.12; +static CGFloat const kInkLayerForegroundWaveTouchDownAcceleration = 1024; +static CGFloat const kInkLayerForegroundWaveTouchUpAcceleration = 3400; +static NSString *const kInkLayerForegroundOpacityAnim = @"foregroundOpacityAnim"; +static NSString *const kInkLayerForegroundPositionAnim = @"foregroundPositionAnim"; +static NSString *const kInkLayerForegroundScaleAnim = @"foregroundScaleAnim"; + +@interface MDCLegacyInkLayerForegroundRipple () +@property(nonatomic, assign) BOOL useCustomInkCenter; +@property(nonatomic, assign) CGPoint customInkCenter; +@property(nonatomic, strong) CAKeyframeAnimation *foregroundOpacityAnim; +@property(nonatomic, strong) CAKeyframeAnimation *foregroundPositionAnim; +@property(nonatomic, strong) CAKeyframeAnimation *foregroundScaleAnim; +@end + +@implementation MDCLegacyInkLayerForegroundRipple + +- (void)setupRipple { + CGFloat random = MDCLegacyInkLayerRandom(); + self.radius = + (CGFloat)((CGFloat)0.9 + random * (CGFloat)0.1) * kInkLayerForegroundRadiusGrowthMultiplier; + [super setupRipple]; +} + +- (void)enterWithCompletion:(void (^)(void))completionBlock { + [super enter]; + + if (self.bounded) { + _foregroundOpacityAnim = [self opacityAnimWithValues:@[ @0 ] times:@[ @0 ]]; + _foregroundScaleAnim = [self scaleAnimWithValues:@[ @0 ] times:@[ @0 ]]; + } else { + _foregroundOpacityAnim = [self opacityAnimWithValues:@[ @0, @1 ] times:@[ @0, @1 ]]; + _foregroundOpacityAnim.duration = kInkLayerForegroundUnboundedOpacityEnterDuration; + + CGFloat duration = (CGFloat)sqrt(self.radius / kInkLayerForegroundWaveTouchDownAcceleration); + _foregroundScaleAnim = + [self scaleAnimWithValues:@[ @0, @1 ] + times:@[ @(kInkLayerForegroundUnboundedEnterDelay), @1 ]]; + _foregroundScaleAnim.duration = duration; + + CGFloat xOffset = self.targetFrame.origin.x - self.inkLayer.frame.origin.x; + CGFloat yOffset = self.targetFrame.origin.y - self.inkLayer.frame.origin.y; + + UIBezierPath *movePath = [UIBezierPath bezierPath]; + CGPoint startPoint = CGPointMake(self.point.x + xOffset, self.point.y + yOffset); + CGPoint endPoint = MDCLegacyInkLayerRectGetCenter(self.targetFrame); + if (self.useCustomInkCenter) { + endPoint = self.customInkCenter; + } + endPoint = CGPointMake(endPoint.x + xOffset, endPoint.y + yOffset); + [movePath moveToPoint:startPoint]; + [movePath addLineToPoint:endPoint]; + + CAMediaTimingFunction *linearTimingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + _foregroundPositionAnim = [self positionAnimWithPath:movePath.CGPath + duration:duration + timingFunction:linearTimingFunction]; + _foregroundPositionAnim.keyTimes = @[ @(kInkLayerForegroundUnboundedEnterDelay), @1 ]; + [self addAnimation:_foregroundPositionAnim forKey:kInkLayerForegroundPositionAnim]; + } + + [CATransaction begin]; + if (completionBlock) { + __weak MDCLegacyInkLayerForegroundRipple *weakSelf = self; + [CATransaction setCompletionBlock:^{ + MDCLegacyInkLayerForegroundRipple *strongSelf = weakSelf; + if (strongSelf.rippleState != kInkRippleCancelled) { + completionBlock(); + } + }]; + } + [self addAnimation:_foregroundOpacityAnim forKey:kInkLayerForegroundOpacityAnim]; + [self addAnimation:_foregroundScaleAnim forKey:kInkLayerForegroundScaleAnim]; + [CATransaction commit]; +} + +- (void)exit:(BOOL)animated { + self.rippleState = kInkRippleCancelled; + [self exit:animated completion:nil]; +} + +- (void)exit:(BOOL)animated completion:(void (^)(void))completionBlock { + [super exit]; + + if (!animated) { + [self removeAllAnimations]; + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + self.opacity = 0; + __weak MDCLegacyInkLayerForegroundRipple *weakSelf = self; + [CATransaction setCompletionBlock:^(void) { + MDCLegacyInkLayerForegroundRipple *strongSelf = weakSelf; + [strongSelf removeFromSuperlayer]; + [strongSelf.animationDelegate animationDidStop:nil shapeLayer:strongSelf finished:YES]; + }]; + [CATransaction commit]; + return; + } + + if (self.bounded) { + _foregroundOpacityAnim.values = @[ @1, @0 ]; + _foregroundOpacityAnim.duration = kInkLayerForegroundBoundedOpacityExitDuration; + + // Bounded ripples move slightly towards the center of the tap target. Unbounded ripples + // move to the center of the tap target. + + CGFloat xOffset = self.targetFrame.origin.x - self.inkLayer.frame.origin.x; + CGFloat yOffset = self.targetFrame.origin.y - self.inkLayer.frame.origin.y; + + CGPoint startPoint = CGPointMake(self.point.x + xOffset, self.point.y + yOffset); + CGPoint endPoint = MDCLegacyInkLayerRectGetCenter(self.targetFrame); + if (self.useCustomInkCenter) { + endPoint = self.customInkCenter; + } + endPoint = CGPointMake(endPoint.x + xOffset, endPoint.y + yOffset); + CGPoint centerOffsetPoint = + MDCLegacyInkLayerInterpolatePoint(startPoint, endPoint, (CGFloat)0.3); + UIBezierPath *movePath = [UIBezierPath bezierPath]; + [movePath moveToPoint:startPoint]; + [movePath addLineToPoint:centerOffsetPoint]; + + _foregroundPositionAnim = + [self positionAnimWithPath:movePath.CGPath + duration:kInkLayerForegroundBoundedPositionExitDuration + timingFunction:[self logDecelerateEasing]]; + _foregroundScaleAnim.values = @[ @0, @1 ]; + _foregroundScaleAnim.keyTimes = @[ @0, @1 ]; + _foregroundScaleAnim.duration = kInkLayerForegroundBoundedRadiusExitDuration; + } else { + NSNumber *opacityVal = [self.presentationLayer valueForKeyPath:kInkLayerOpacity]; + if (opacityVal == nil) { + opacityVal = [NSNumber numberWithFloat:0]; + } + CGFloat adjustedDuration = kInkLayerForegroundBoundedPositionExitDuration; + CGFloat normOpacityVal = opacityVal.floatValue; + CGFloat opacityDuration = normOpacityVal / 3; + _foregroundOpacityAnim.values = @[ opacityVal, @0 ]; + _foregroundOpacityAnim.duration = opacityDuration + adjustedDuration; + + NSNumber *scaleVal = [self.presentationLayer valueForKeyPath:kInkLayerScale]; + if (scaleVal == nil) { + scaleVal = [NSNumber numberWithFloat:0]; + } + CGFloat unboundedDuration = (CGFloat)sqrt(((1 - scaleVal.floatValue) * self.radius) / + (kInkLayerForegroundWaveTouchDownAcceleration + + kInkLayerForegroundWaveTouchUpAcceleration)); + _foregroundPositionAnim.duration = unboundedDuration + adjustedDuration; + _foregroundScaleAnim.values = @[ scaleVal, @1 ]; + _foregroundScaleAnim.duration = unboundedDuration + adjustedDuration; + } + + _foregroundOpacityAnim.keyTimes = @[ @0, @1 ]; + if (_foregroundOpacityAnim.duration < _foregroundScaleAnim.duration) { + _foregroundScaleAnim.delegate = self; + } else { + _foregroundOpacityAnim.delegate = self; + } + + _foregroundOpacityAnim.timingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + _foregroundPositionAnim.timingFunction = [self logDecelerateEasing]; + _foregroundScaleAnim.timingFunction = [self logDecelerateEasing]; + + [CATransaction begin]; + if (completionBlock) { + __weak MDCLegacyInkLayerForegroundRipple *weakSelf = self; + [CATransaction setCompletionBlock:^{ + MDCLegacyInkLayerForegroundRipple *strongSelf = weakSelf; + if (strongSelf.rippleState != kInkRippleCancelled) { + completionBlock(); + } + }]; + } + [self addAnimation:_foregroundOpacityAnim forKey:kInkLayerForegroundOpacityAnim]; + [self addAnimation:_foregroundPositionAnim forKey:kInkLayerForegroundPositionAnim]; + [self addAnimation:_foregroundScaleAnim forKey:kInkLayerForegroundScaleAnim]; + [CATransaction commit]; +} + +- (void)removeAllAnimations { + [super removeAllAnimations]; + _foregroundOpacityAnim = nil; + _foregroundPositionAnim = nil; + _foregroundScaleAnim = nil; + self.animationCleared = YES; +} + +@end + +static CGFloat const kInkLayerBackgroundOpacityEnterDuration = (CGFloat)0.6; +static CGFloat const kInkLayerBackgroundBaseOpacityExitDuration = (CGFloat)0.48; +static CGFloat const kInkLayerBackgroundFastEnterDuration = (CGFloat)0.12; +static NSString *const kInkLayerBackgroundOpacityAnim = @"backgroundOpacityAnim"; + +@interface MDCLegacyInkLayerBackgroundRipple () +@property(nonatomic, strong, nullable) CAKeyframeAnimation *backgroundOpacityAnim; +@end + +@implementation MDCLegacyInkLayerBackgroundRipple + +- (void)enter { + [super enter]; + _backgroundOpacityAnim = [self opacityAnimWithValues:@[ @0, @1 ] times:@[ @0, @1 ]]; + _backgroundOpacityAnim.duration = kInkLayerBackgroundOpacityEnterDuration; + [self addAnimation:_backgroundOpacityAnim forKey:kInkLayerBackgroundOpacityAnim]; +} + +- (void)exit:(BOOL)animated { + [super exit]; + + if (!animated) { + [self removeAllAnimations]; + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + self.opacity = 0; + __weak MDCLegacyInkLayerBackgroundRipple *weakSelf = self; + [CATransaction setCompletionBlock:^(void) { + MDCLegacyInkLayerBackgroundRipple *strongSelf = weakSelf; + [strongSelf removeFromSuperlayer]; + [strongSelf.animationDelegate animationDidStop:nil shapeLayer:strongSelf finished:YES]; + }]; + [CATransaction commit]; + return; + } + + NSNumber *opacityVal = [self.presentationLayer valueForKeyPath:kInkLayerOpacity]; + if (opacityVal == nil) { + opacityVal = [NSNumber numberWithFloat:0]; + } + CGFloat duration = kInkLayerBackgroundBaseOpacityExitDuration; + if (self.bounded) { + // The end (tap release) animation should continue at the opacity level of the start animation. + CGFloat enterDuration = (1 - opacityVal.floatValue / 1) * kInkLayerBackgroundFastEnterDuration; + duration += enterDuration; + _backgroundOpacityAnim = [self opacityAnimWithValues:@[ opacityVal, @1, @0 ] + times:@[ @0, @(enterDuration / duration), @1 ]]; + } else { + _backgroundOpacityAnim = [self opacityAnimWithValues:@[ opacityVal, @0 ] times:@[ @0, @1 ]]; + } + _backgroundOpacityAnim.duration = duration; + _backgroundOpacityAnim.delegate = self; + [self addAnimation:_backgroundOpacityAnim forKey:kInkLayerBackgroundOpacityAnim]; +} + +- (void)removeAllAnimations { + [super removeAllAnimations]; + _backgroundOpacityAnim = nil; + self.animationCleared = YES; +} + +@end + +@interface MDCLegacyInkLayer () + +/** + Reset the bottom-most ink applied to the layer with a completion handler to be called on completion + if applicable. + + @param animated Enables the ink ripple fade out animation. + @param completionBlock Block called after the completion of the animation. + */ +- (void)resetBottomInk:(BOOL)animated completion:(void (^)(void))completionBlock; + +/** + Reset the bottom-most ink applied to the layer with a completion handler to be called on completion + if applicable. + + @param animated Enables the ink ripple fade out animation. + @param point Evaporate the ink towards the point. + @param completionBlock Block called after the completion of the animation. + */ +- (void)resetBottomInk:(BOOL)animated + toPoint:(CGPoint)point + completion:(void (^)(void))completionBlock; + +@property(nonatomic, strong, nonnull) CAShapeLayer *compositeRipple; +@property(nonatomic, strong, nonnull) + NSMutableArray *foregroundRipples; +@property(nonatomic, strong, nonnull) + NSMutableArray *backgroundRipples; + +@end + +@implementation MDCLegacyInkLayer + +- (instancetype)init { + self = [super init]; + if (self) { + self.masksToBounds = YES; + [self commonMDCLegacyInkLayerInit]; + _animating = NO; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + + if (self) { + _animating = NO; + // Discard any sublayers, which should be the composite ripple and any active ripples + if (self.sublayers.count > 0) { + NSArray *sublayers = [self.sublayers copy]; + for (CALayer *sublayer in sublayers) { + [sublayer removeFromSuperlayer]; + } + } + [self commonMDCLegacyInkLayerInit]; + } + + return self; +} + +- (void)commonMDCLegacyInkLayerInit { + static UIColor *defaultInkColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultInkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; + }); + + _bounded = YES; + _inkColor = defaultInkColor; + _compositeRipple = [CAShapeLayer layer]; + _foregroundRipples = [NSMutableArray array]; + _backgroundRipples = [NSMutableArray array]; + [self addSublayer:_compositeRipple]; +} + +- (void)layoutSublayers { + [super layoutSublayers]; + CGFloat radius = MDCLegacyInkLayerRadiusBounds( + _maxRippleRadius, MDCLegacyInkLayerRectHypotenuse(self.bounds) / 2, _bounded); + + CGRect rippleFrame = + CGRectMake(-(radius * 2 - self.bounds.size.width) / 2, + -(radius * 2 - self.bounds.size.height) / 2, radius * 2, radius * 2); + _compositeRipple.frame = rippleFrame; + CGRect rippleBounds = CGRectMake(0, 0, radius * 2, radius * 2); + CAShapeLayer *rippleMaskLayer = [CAShapeLayer layer]; + UIBezierPath *ripplePath = [UIBezierPath bezierPathWithOvalInRect:rippleBounds]; + rippleMaskLayer.path = ripplePath.CGPath; + _compositeRipple.mask = rippleMaskLayer; +} + +- (void)enterAllInks { + for (MDCLegacyInkLayerForegroundRipple *foregroundRipple in self.foregroundRipples) { + [foregroundRipple enterWithCompletion:nil]; + } + for (MDCLegacyInkLayerBackgroundRipple *backgroundRipple in self.backgroundRipples) { + [backgroundRipple enter]; + } +} + +- (void)resetAllInk:(BOOL)animated { + for (MDCLegacyInkLayerForegroundRipple *foregroundRipple in self.foregroundRipples) { + [foregroundRipple exit:animated]; + } + for (MDCLegacyInkLayerBackgroundRipple *backgroundRipple in self.backgroundRipples) { + [backgroundRipple exit:animated]; + } +} + +- (void)resetBottomInk:(BOOL)animated completion:(void (^)(void))completionBlock { + if (self.foregroundRipples.count > 0) { + [[self.foregroundRipples objectAtIndex:(self.foregroundRipples.count - 1)] + exit:animated + completion:completionBlock]; + } + if (self.backgroundRipples.count > 0) { + [[self.backgroundRipples objectAtIndex:(self.backgroundRipples.count - 1)] exit:animated]; + } +} + +- (void)resetBottomInk:(BOOL)animated + toPoint:(CGPoint)point + completion:(void (^)(void))completionBlock { + if (self.foregroundRipples.count > 0) { + MDCLegacyInkLayerForegroundRipple *foregroundRipple = + [self.foregroundRipples objectAtIndex:(self.foregroundRipples.count - 1)]; + foregroundRipple.point = point; + [foregroundRipple exit:animated completion:completionBlock]; + } + if (self.backgroundRipples.count > 0) { + [[self.backgroundRipples objectAtIndex:(self.backgroundRipples.count - 1)] exit:animated]; + } +} + +#pragma mark - Properties + +- (void)spreadFromPoint:(CGPoint)point completion:(void (^)(void))completionBlock { + // Create a mask layer before drawing the ink using the superlayer's shadowPath + // if it exists. This helps the FAB when it is not rectangular. + if (self.masksToBounds && self.superlayer.shadowPath) { + CAShapeLayer *mask = [CAShapeLayer layer]; + mask.path = self.superlayer.shadowPath; + mask.fillColor = [UIColor whiteColor].CGColor; + self.mask = mask; + } else { + self.mask = nil; + } + + CGFloat radius = MDCLegacyInkLayerRadiusBounds( + _maxRippleRadius, MDCLegacyInkLayerRectHypotenuse(self.bounds) / 2, _bounded); + + MDCLegacyInkLayerBackgroundRipple *backgroundRipple = + [[MDCLegacyInkLayerBackgroundRipple alloc] init]; + backgroundRipple.inkLayer = _compositeRipple; + backgroundRipple.targetFrame = self.bounds; + backgroundRipple.point = point; + backgroundRipple.color = self.inkColor; + backgroundRipple.radius = radius; + backgroundRipple.bounded = self.bounded; + backgroundRipple.animationDelegate = self; + [backgroundRipple setupRipple]; + + MDCLegacyInkLayerForegroundRipple *foregroundRipple = + [[MDCLegacyInkLayerForegroundRipple alloc] init]; + foregroundRipple.inkLayer = _compositeRipple; + foregroundRipple.targetFrame = self.bounds; + foregroundRipple.point = point; + foregroundRipple.color = self.inkColor; + foregroundRipple.radius = radius; + foregroundRipple.bounded = self.bounded; + foregroundRipple.animationDelegate = self; + foregroundRipple.useCustomInkCenter = self.useCustomInkCenter; + foregroundRipple.customInkCenter = self.customInkCenter; + [foregroundRipple setupRipple]; + + [backgroundRipple enter]; + [self.backgroundRipples addObject:backgroundRipple]; + [foregroundRipple enterWithCompletion:completionBlock]; + [self.foregroundRipples addObject:foregroundRipple]; +} + +- (void)evaporateWithCompletion:(void (^)(void))completionBlock { + [self resetBottomInk:YES completion:completionBlock]; +} + +- (void)evaporateToPoint:(CGPoint)point completion:(void (^)(void))completionBlock { + [self resetBottomInk:YES toPoint:point completion:completionBlock]; +} + +#pragma mark - MDCLegacyRippleInkLayerDelegate + +- (void)animationDidStart:(MDCLegacyInkLayerRipple *)layerRipple { + if (!self.isAnimating) { + self.animating = YES; + + if ([self.animationDelegate respondsToSelector:@selector(legacyInkLayerAnimationDidStart:)]) { + [self.animationDelegate legacyInkLayerAnimationDidStart:self]; + } + } +} + +- (void)animationDidStop:(__unused CAAnimation *)anim + shapeLayer:(MDCLegacyInkLayerRipple *)layerRipple + finished:(__unused BOOL)finished { + // Even when the ripple is "exited" without animation, we need to remove it from compositeRipple + [layerRipple removeFromSuperlayer]; + [layerRipple removeAllAnimations]; + + if ([layerRipple isKindOfClass:[MDCLegacyInkLayerForegroundRipple class]]) { + [self.foregroundRipples removeObject:(MDCLegacyInkLayerForegroundRipple *)layerRipple]; + } else if ([layerRipple isKindOfClass:[MDCLegacyInkLayerBackgroundRipple class]]) { + [self.backgroundRipples removeObject:(MDCLegacyInkLayerBackgroundRipple *)layerRipple]; + } + + // Check if all ink layer animations did finish and call animation end callback + if (self.isAnimating && self.foregroundRipples.count == 0 && self.backgroundRipples.count == 0) { + self.animating = NO; + if ([self.animationDelegate respondsToSelector:@selector(legacyInkLayerAnimationDidEnd:)]) { + [self.animationDelegate legacyInkLayerAnimationDidEnd:self]; + } + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h new file mode 100644 index 00000000..200e889a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h @@ -0,0 +1,41 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCLegacyInkLayer; + +/** + Delegate protocol for the MDCLegacyInkLayer. Clients may implement this protocol to receive updates + when ink layer animations start and end. + */ +@protocol MDCLegacyInkLayerDelegate + +@optional + +/** + Called when the ink ripple animation begins. + + @param inkLayer The MDCLegacyInkLayer that starts animating. + */ +- (void)legacyInkLayerAnimationDidStart:(nonnull MDCLegacyInkLayer *)inkLayer; + +/** + Called when the ink ripple animation ends. + + @param inkLayer The MDCLegacyInkLayer that ends animating. + */ +- (void)legacyInkLayerAnimationDidEnd:(nonnull MDCLegacyInkLayer *)inkLayer; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h new file mode 100644 index 00000000..93320c7b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h @@ -0,0 +1,29 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCLegacyInkLayerRipple; + +@protocol MDCLegacyInkLayerRippleDelegate + +/// Called if MDCLegacyInkLayerRipple did start animating. +- (void)animationDidStart:(nonnull MDCLegacyInkLayerRipple *)layerRipple; + +/// Called for every MDCLegacyInkLayerRipple if an animation did end. +- (void)animationDidStop:(nullable CAAnimation *)anim + shapeLayer:(nullable CAShapeLayer *)layerRipple + finished:(BOOL)finished; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h new file mode 100644 index 00000000..2995a079 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h @@ -0,0 +1,113 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCRippleTouchControllerDelegate.h" +#import "MDCRippleView.h" + +@protocol MDCRippleTouchControllerDelegate; + +/** + The MDCRippleTouchController is a convenience controller that adds all the needed touch tracking + and logic to provide the correct ripple effect based on the user interacting with the view the + ripple is added to. + */ +@interface MDCRippleTouchController : NSObject + +/** + A weak reference to the view that the ripple is added to, and that responds to the user + events. + */ +@property(nonatomic, weak, readonly, nullable) UIView *view; + +/** + The ripple view added to the view. + */ +@property(nonatomic, strong, readonly, nonnull) MDCRippleView *rippleView; + +/** + A delegate to extend the behavior of the touch controller. + */ +@property(nonatomic, weak, nullable) id delegate; + +/** + The gesture recognizer used to bind the touch events to the ripple. + */ +@property(nonatomic, strong, readonly, nonnull) UILongPressGestureRecognizer *gestureRecognizer; + +/** + If set to NO, the ripple gesture will fail and not be initiated if there are other competing + gestures that belong to a UIScrollView. This helps the ripple not be invoked if a user wants + to scroll but does so while tapping on a view that incorporates a ripple. + + Defaults to YES. + */ +@property(nonatomic, assign) BOOL shouldProcessRippleWithScrollViewGestures; + +/** + Initializes the controller and adds the initialized ripple view as a subview of the provided view. + + Note: When using this initializer, calling `addRippleToView:` isn't needed. + + @param view The view that responds to the touch events for the ripple. The ripple + is added to it as a subview. + @return an MDCRippleTouchController instance. + */ +- (nonnull instancetype)initWithView:(nonnull UIView *)view; + +/** + Initializes the controller. + + Note: To effectively use the controller a call to `addRippleToView:` is needed to provide a view + that responds to the touch events for the ripple. The ripple is added to the view as a subview. + + @return an MDCRippleTouchController instance. + */ +- (nonnull instancetype)init; + +/** + Initializes the controller and based on the deferred parameter is adding the ripple view + immediately as subview of the priovided view. + + @Note If YES is passed for the deferred parameter and + @c rippleTouchController:insertRippleView:intoView: is not implemented, the rippleview will be + automatically added as the top subview to the given view when the first tap event is happening. + When @c rippleTouchController:insertRippleView:intoView: is implemented, it's the responsibility of + the delegate to add the ripple view in the proper position within view's hierarchy if the delegate + method is called. + + @param view The view that responds to the touch events for the ripple. The ripple + is added to it as a subview. + @param deferred Wheter the insertion of the rippleView to the provided view should be + happened deferred + @return an MDCRippleTouchController instance. +*/ +- (nonnull instancetype)initWithView:(nonnull UIView *)view deferred:(BOOL)deferred; + +/** + Adds the ripple view as a subview to the provided view, and adds the ripple's gesture recognizer + to it. + + Note: This needs to be called if using the `init` initialized rather than the `initWithView:` + initializer. + + @param view The view that responds to the touch events for the ripple. The ripple + is added to it as a subview. + */ +- (void)addRippleToView:(nonnull UIView *)view; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m new file mode 100644 index 00000000..875d8fe2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m @@ -0,0 +1,205 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRippleTouchController.h" + +#import "MDCRippleTouchControllerDelegate.h" + +@implementation MDCRippleTouchController { + BOOL _tapWentOutsideOfBounds; + BOOL _deferred; + + struct { + unsigned int rippleTouchControllerShouldProcessRippleTouchesAtTouchLocation : 1; + unsigned int rippleTouchControllerDidProcessRippleViewAtTouchLocation : 1; + unsigned int rippleTouchControllerInsertRippleViewIntoView : 1; + unsigned int rippleTouchControllerRippleViewAtTouchLocation : 1; + } _delegateFlags; +} + +@synthesize rippleView = _rippleView; + +- (instancetype)initWithView:(UIView *)view { + return [self initWithView:view deferred:NO]; +} + +- (nonnull instancetype)initWithView:(nonnull UIView *)view deferred:(BOOL)deferred { + self = [self init]; + if (self) { + _deferred = deferred; + if (deferred) { + [self attachGestureRecognizerToView:view]; + } else { + [self configureRippleWithView:view]; + } + } + return self; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _gestureRecognizer = + [[UILongPressGestureRecognizer alloc] initWithTarget:self + action:@selector(handleRippleGesture:)]; + _gestureRecognizer.minimumPressDuration = 0; + _gestureRecognizer.delegate = self; + _gestureRecognizer.cancelsTouchesInView = NO; + _gestureRecognizer.delaysTouchesEnded = NO; + + _shouldProcessRippleWithScrollViewGestures = YES; + } + return self; +} + +- (void)dealloc { + [_view removeGestureRecognizer:_gestureRecognizer]; + _gestureRecognizer.delegate = nil; +} + +- (MDCRippleView *)rippleView { + if (_rippleView == nil) { + _rippleView = [[MDCRippleView alloc] init]; + } + return _rippleView; +} + +- (void)setDelegate:(id)delegate { + _delegate = delegate; + + // The delegate's behavior - in terms of which optional methods are deemed to be + // implemented - is cached at assignment rather than inspected on each invocation. + _delegateFlags.rippleTouchControllerShouldProcessRippleTouchesAtTouchLocation = + [_delegate respondsToSelector:@selector(rippleTouchController: + shouldProcessRippleTouchesAtTouchLocation:)]; + _delegateFlags.rippleTouchControllerDidProcessRippleViewAtTouchLocation = [_delegate + respondsToSelector:@selector(rippleTouchController:didProcessRippleView:atTouchLocation:)]; + _delegateFlags.rippleTouchControllerInsertRippleViewIntoView = + [delegate respondsToSelector:@selector(rippleTouchController:insertRippleView:intoView:)]; + _delegateFlags.rippleTouchControllerRippleViewAtTouchLocation = + [_delegate respondsToSelector:@selector(rippleTouchController:rippleViewAtTouchLocation:)]; +} + +- (void)addRippleToView:(UIView *)view { + [self configureRippleWithView:view]; +} + +- (void)configureRippleWithView:(UIView *)view { + [self attachGestureRecognizerToView:view]; + [self insertRippleViewIntoView:view]; +} + +- (void)attachGestureRecognizerToView:(UIView *)view { + [_view removeGestureRecognizer:_gestureRecognizer]; + _view = view; + [_view addGestureRecognizer:_gestureRecognizer]; +} + +- (void)insertRippleViewIntoView:(UIView *)view { + if (!_delegateFlags.rippleTouchControllerRippleViewAtTouchLocation) { + // Insert the rippleView to the _view + MDCRippleView *rippleView = self.rippleView; + if (_delegateFlags.rippleTouchControllerInsertRippleViewIntoView) { + [_delegate rippleTouchController:self insertRippleView:rippleView intoView:view]; + } else { + [_view addSubview:rippleView]; + } + rippleView.frame = view.bounds; + } +} + +- (void)handleRippleGesture:(UILongPressGestureRecognizer *)recognizer { + CGPoint touchLocation = [recognizer locationInView:_view]; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + if (_delegateFlags.rippleTouchControllerRippleViewAtTouchLocation) { + _rippleView = [_delegate rippleTouchController:self + rippleViewAtTouchLocation:touchLocation]; + if (!_rippleView) { + // If we find that a return isn't enough here, we may need to disable and then + // re-enable the recognizer so there are no side effects. + return; + } + } else { + MDCRippleView *rippleView = self.rippleView; + if (_deferred && rippleView.superview != _view) { + [self insertRippleViewIntoView:_view]; + } + } + + [_rippleView beginRippleTouchDownAtPoint:[recognizer locationInView:self.rippleView] + animated:YES + completion:nil]; + if (_delegateFlags.rippleTouchControllerDidProcessRippleViewAtTouchLocation) { + [_delegate rippleTouchController:self + didProcessRippleView:_rippleView + atTouchLocation:touchLocation]; + } + break; + } + case UIGestureRecognizerStatePossible: // Ignored + break; + case UIGestureRecognizerStateChanged: { + BOOL pointContainedinBounds = CGRectContainsPoint(_view.bounds, touchLocation); + if (pointContainedinBounds && _tapWentOutsideOfBounds) { + _tapWentOutsideOfBounds = NO; + [_rippleView fadeInRippleAnimated:YES completion:nil]; + } else if (!pointContainedinBounds && !_tapWentOutsideOfBounds) { + _tapWentOutsideOfBounds = YES; + [_rippleView fadeOutRippleAnimated:YES completion:nil]; + } + break; + } + case UIGestureRecognizerStateEnded: { + [_rippleView beginRippleTouchUpAnimated:YES completion:nil]; + break; + } + case UIGestureRecognizerStateCancelled: + case UIGestureRecognizerStateFailed: { + [_rippleView cancelAllRipplesAnimated:YES completion:nil]; + break; + } + } +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer + shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + if (!self.shouldProcessRippleWithScrollViewGestures && + [otherGestureRecognizer.view isKindOfClass:[UIScrollView class]] && + ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && + ![otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { + return YES; + } + return NO; +} + +- (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer + shouldRecognizeSimultaneouslyWithGestureRecognizer:(__unused UIGestureRecognizer *)other { + // Subclasses can override this to prioritize another recognizer. + return YES; +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { + if (_delegateFlags.rippleTouchControllerShouldProcessRippleTouchesAtTouchLocation) { + CGPoint touchLocation = [gestureRecognizer locationInView:_view]; + return [_delegate rippleTouchController:self + shouldProcessRippleTouchesAtTouchLocation:touchLocation]; + } + return YES; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h new file mode 100644 index 00000000..b0d40a96 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h @@ -0,0 +1,87 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCRippleTouchController; +@class MDCRippleView; + +/** + Delegate methods for MDCRippleTouchController. + */ +@protocol MDCRippleTouchControllerDelegate +@optional + +/** + Controls whether the ripple touch controller should process touches. + + The touch controller will query this method to determine if it should start or continue to + process touches controlling the ripple. Returning NO at the start of a gesture will prevent any + ripple from being displayed, and returning NO in the middle of a gesture will cancel that gesture + and evaporate the ripple. + + If not implemented then YES is assumed. + + @param rippleTouchController The ripple touch controller. + @param location The touch location relative to the rippleTouchController view. + @return YES if the controller should process touches at @c location. + + @see cancelRippleTouchProcessing + */ +- (BOOL)rippleTouchController:(nonnull MDCRippleTouchController *)rippleTouchController + shouldProcessRippleTouchesAtTouchLocation:(CGPoint)location; + +/** + Notifies the receiver that the ripple touch controller did process an ripple view at the + touch location. + + @param rippleTouchController The ripple touch controller. + @param rippleView The ripple view. + @param location The touch location relative to the rippleTouchController superView. + */ +- (void)rippleTouchController:(nonnull MDCRippleTouchController *)rippleTouchController + didProcessRippleView:(nonnull MDCRippleView *)rippleView + atTouchLocation:(CGPoint)location; + +/** + Provides an opportunity to add the rippleView anywhere in the given view's hierarchy. + + If this method is not implemented, the ripple view is added as a subview of the given view provided + in the controller's `addRippleToView:` method or convenience initializer `initWithView:`. + Delegates can choose to insert the ripple view anywhere in the view hierarchy. + + @param rippleTouchController The ripple touch controller. + @param rippleView The ripple view. + @param view The requested superview of the ripple view. + */ +- (void)rippleTouchController:(nonnull MDCRippleTouchController *)rippleTouchController + insertRippleView:(nonnull MDCRippleView *)rippleView + intoView:(nonnull UIView *)view; + +/** + Returns the ripple view to use for a touch located at location in rippleTouchController.view. + + If the delegate implements this method, the controller will not create a ripple view of its own and + rippleTouchController:insertRippleView:intoView: will not be called. This method allows the + delegate to control the creation and reuse of ripple views. + + @param rippleTouchController The ripple touch controller. + @param location The touch location in the coordinates of @c rippleTouchController.view. + @return A ripple view to use at the touch location. + */ +- (nullable MDCRippleView *)rippleTouchController: + (nonnull MDCRippleTouchController *)rippleTouchController + rippleViewAtTouchLocation:(CGPoint)location; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h new file mode 100644 index 00000000..f4837ea5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h @@ -0,0 +1,170 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCRippleViewDelegate.h" + +@protocol MDCRippleViewDelegate; + +/** + Convenience naming for the completion blocks the ripple animation provides. + */ +typedef void (^MDCRippleCompletionBlock)(void); + +/** + The different possible ripple styles. The ripple can either be bound to the view or not. + + - MDCRippleStyleBounded: The ripple is bound to the view. + - MDCRippleStyleUnbounded: The ripple is unbounded and ripples to the size of the smallest circle + that covers the entire rectangular bounds, plus an additional 10 points. + */ +typedef NS_ENUM(NSInteger, MDCRippleStyle) { + MDCRippleStyleBounded = 0, + MDCRippleStyleUnbounded, +}; + +/** + A UIView that draws and animates the Material Design ripple effect for touch interactions. + + The Ripple is a visual flourish consisting of an animated circle with various scale, opacity and + position animations applied simultaneously to give the illusion of ink applied to a paper surface. + + Our touch feedback ripple effect is a prominent entity across all our interactable components: + i.e., buttons, cards, tab bars, list items. + + There can be multiple riples occurring at the same time, each represented by an MDCRippleLayer. + */ +@interface MDCRippleView : UIView + +/** + The ripple view delegate. + */ +@property(nonatomic, weak, nullable) id rippleViewDelegate; + +/** + The ripple style indicating if the ripple is bounded or unbounded to the view. + */ +@property(nonatomic, assign) MDCRippleStyle rippleStyle; + +/** + The ripple's color. + */ +@property(nonatomic, strong, nonnull) UIColor *rippleColor; + +/** + The maximum radius the ripple can expand to. + + @note This property is ignored if @c rippleStyle is set to @c MDCRippleStyleBounded. + */ +@property(nonatomic, assign) CGFloat maximumRadius; + +/** + The ripple color of the currently active ripple. + */ +@property(nonatomic, strong, nonnull) UIColor *activeRippleColor; + +/** + When rippleStyle is MDCRippleStyleBounded, this flag affects whether the layer's mask will use + the super view's layer.shadowPath as the mask path. + + @note This behavior only takes effect if the ripple view's parent view has a non-nil shadowPath. + + @note This behavioral flag will eventually become NO by default and then be deleted. The YES + behavior is undesired because it assumes that the frame of the ripple view always matches the + bounds of the superview. When this assumption is false, such as when the ripple's origin is + non-zero, the ripple's mask tends to be bigger than it should be resulting in an incorrectly + clipped ripple effect. Consider disabling this behavior and explicitly setting a layer mask + instead. + + Changing this value to NO does not clear the mask if it was already set. + + Default value is YES. + */ +@property(nonatomic, assign) BOOL usesSuperviewShadowLayerAsMask; + +/** + A block that is invoked when the @c MDCRippleView receives a call to @c + traitCollectionDidChange:. The block is called after the call to the superclass. + */ +@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) + (MDCRippleView *_Nonnull ripple, UITraitCollection *_Nullable previousTraitCollection); + +/** + Cancels all the existing ripples. + + @param animated Whether to animate the cancellation of the ripples or not. + */ +- (void)cancelAllRipplesAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Fades the ripple in by changing its opacity. + + @param animated Whether or not the fade in should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)fadeInRippleAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Fades the ripple out by changing its opacity. + + @param animated Whether or not the fade in should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)fadeOutRippleAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Begins the ripple's touch down animation at the given point. This presents the ripple and leaves it + on the view. If animated, it animates the expanding ripple circle effect. + To then remove the ripple, `beginRippleTouchUpAnimated` needs to be called. + + @param point The point to start the ripple animation. + @param animated Whether or not the ripple should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)beginRippleTouchDownAtPoint:(CGPoint)point + animated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Begins the ripple's touch up animation. This remopves the ripple from the view. If animated, the + ripple dissolves using an animated opacity change. + + @param animated Whether or not the ripple should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)beginRippleTouchUpAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Enumerates the given view's subviews for an instance of MDCRippleView and returns it if found, or + creates and adds a new instance of MDCRippleView if not. + + This method is a convenience method for adding ripple to an arbitrary view without needing to + subclass the target view. Use this method in situations where you expect there to be many distinct + ripple views in existence for a single ripple touch controller. Example scenarios include: + + - Adding ripple to individual collection view/table view cells + + This method can be used in your MDCRippleTouchController delegate's + -rippleTouchController:rippleViewAtTouchLocation: implementation. + */ ++ (nonnull MDCRippleView *)injectedRippleViewForView:(nonnull UIView *)view; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m new file mode 100644 index 00000000..32124af8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m @@ -0,0 +1,349 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRippleView.h" +#import "private/MDCRippleLayer.h" + +#import "MaterialAvailability.h" +#import "MDCRippleViewDelegate.h" + +@interface MDCRippleView () + +@property(nonatomic, strong) MDCRippleLayer *activeRippleLayer; +@property(nonatomic, strong) CAShapeLayer *maskLayer; + +@end + +@interface MDCRipplePendingAnimation : NSObject + +@property(nonatomic, weak) CALayer *animationSourceLayer; +@property(nonatomic, strong) NSString *keyPath; +@property(nonatomic, strong) id fromValue; +@property(nonatomic, strong) id toValue; + +@end + +static const CGFloat kRippleDefaultAlpha = (CGFloat)0.12; +static const CGFloat kRippleFadeOutDelay = (CGFloat)0.15; + +@implementation MDCRippleView + +@synthesize activeRippleLayer = _activeRippleLayer; + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCRippleViewInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCRippleViewInit]; + } + return self; +} + +- (void)commonMDCRippleViewInit { + _usesSuperviewShadowLayerAsMask = YES; + self.userInteractionEnabled = NO; + self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + + static UIColor *defaultRippleColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultRippleColor = [[UIColor alloc] initWithWhite:0 alpha:kRippleDefaultAlpha]; + }); + _rippleColor = defaultRippleColor; + _rippleStyle = MDCRippleStyleBounded; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + self.activeRippleLayer.fillColor = self.activeRippleColor.CGColor; +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + + if (self.traitCollectionDidChangeBlock) { + self.traitCollectionDidChangeBlock(self, previousTraitCollection); + } +} + +- (void)layoutSublayersOfLayer:(CALayer *)layer { + [super layoutSublayersOfLayer:layer]; + + NSArray *sublayers = self.layer.sublayers; + if (sublayers.count > 0) { + [self updateRippleStyle]; + } + for (CALayer *sublayer in sublayers) { + sublayer.frame = CGRectStandardize(self.bounds); + [sublayer setNeedsLayout]; + } +} + +- (void)setRippleStyle:(MDCRippleStyle)rippleStyle { + _rippleStyle = rippleStyle; + [self updateRippleStyle]; +} + +- (void)setUsesSuperviewShadowLayerAsMask:(BOOL)usesSuperviewShadowLayerAsMask { + _usesSuperviewShadowLayerAsMask = usesSuperviewShadowLayerAsMask; + + if (_usesSuperviewShadowLayerAsMask) { + [self setNeedsLayout]; + } +} + +- (void)updateRippleStyle { + self.layer.masksToBounds = (self.rippleStyle == MDCRippleStyleBounded); + if (self.rippleStyle == MDCRippleStyleBounded) { + if (self.usesSuperviewShadowLayerAsMask && self.superview.layer.shadowPath) { + if (!self.maskLayer) { + // Use mask layer when the superview has a shadowPath. + self.maskLayer = [CAShapeLayer layer]; + self.maskLayer.delegate = self; + } + self.maskLayer.path = self.superview.layer.shadowPath; + self.layer.mask = _maskLayer; + } + } else { + self.layer.mask = nil; + } +} + +- (void)cancelAllRipplesAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { + NSArray *sublayers = [self.layer.sublayers copy]; + if (animated) { + CFTimeInterval latestBeginTouchDownRippleTime = DBL_MIN; + for (CALayer *layer in sublayers) { + if ([layer isKindOfClass:[MDCRippleLayer class]]) { + MDCRippleLayer *rippleLayer = (MDCRippleLayer *)layer; + latestBeginTouchDownRippleTime = + MAX(latestBeginTouchDownRippleTime, rippleLayer.rippleTouchDownStartTime); + } + } + dispatch_group_t group = dispatch_group_create(); + for (CALayer *layer in sublayers) { + if ([layer isKindOfClass:[MDCRippleLayer class]]) { + MDCRippleLayer *rippleLayer = (MDCRippleLayer *)layer; + if (!rippleLayer.isStartAnimationActive) { + rippleLayer.rippleTouchDownStartTime = + latestBeginTouchDownRippleTime + kRippleFadeOutDelay; + } + dispatch_group_enter(group); + [rippleLayer endRippleAnimated:animated + completion:^{ + dispatch_group_leave(group); + }]; + } + } + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + if (completion) { + completion(); + } + }); + } else { + for (CALayer *layer in sublayers) { + if ([layer isKindOfClass:[MDCRippleLayer class]]) { + MDCRippleLayer *rippleLayer = (MDCRippleLayer *)layer; + [rippleLayer removeFromSuperlayer]; + } + } + if (completion) { + completion(); + } + } +} + +- (MDCRippleLayer *)activeRippleLayer { + if (self.layer.sublayers.count < 1) { + return nil; + } + return _activeRippleLayer; +} + +- (void)setActiveRippleLayer:(MDCRippleLayer *)activeRippleLayer { + _activeRippleLayer = activeRippleLayer; + + // When the active ripple layer is set, a new ripple layer is created which takes + // its color from @c rippleColor. Therefore, @activeRippleColor now becomes that + // color. + self.activeRippleColor = self.rippleColor; +} + +- (void)setColorForRippleLayer:(MDCRippleLayer *)rippleLayer { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + if ([self.traitCollection respondsToSelector:@selector(performAsCurrentTraitCollection:)]) { + [self.traitCollection performAsCurrentTraitCollection:^{ + rippleLayer.fillColor = self.rippleColor.CGColor; + }]; + return; + } + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + rippleLayer.fillColor = self.rippleColor.CGColor; +} + +- (void)beginRippleTouchDownAtPoint:(CGPoint)point + animated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion { + MDCRippleLayer *rippleLayer = [MDCRippleLayer layer]; + rippleLayer.rippleLayerDelegate = self; + [self updateRippleStyle]; + [self setColorForRippleLayer:rippleLayer]; + rippleLayer.frame = self.bounds; + if (self.rippleStyle == MDCRippleStyleUnbounded) { + rippleLayer.maximumRadius = self.maximumRadius; + } + [self.layer addSublayer:rippleLayer]; + [rippleLayer startRippleAtPoint:point animated:animated completion:completion]; + self.activeRippleLayer = rippleLayer; +} + +- (void)beginRippleTouchUpAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion { + // If all ripple animations are already cancelled and removed from the superlayer call the + // short circuit and call the completion handler directly. + if (self.activeRippleLayer == nil) { + if (completion) { + completion(); + } + return; + } + [self.activeRippleLayer endRippleAnimated:animated completion:completion]; +} + +- (void)fadeInRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { + // If all ripple animations are already cancelled and removed from the superlayer call the + // short circuit and call the completion handler directly. + if (self.activeRippleLayer == nil) { + if (completion) { + completion(); + } + return; + } + [self.activeRippleLayer fadeInRippleAnimated:animated completion:completion]; +} + +- (void)fadeOutRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { + // If all ripple animations are already cancelled and removed from the superlayer call the + // short circuit and call the completion handler directly. + if (self.activeRippleLayer == nil) { + if (completion) { + completion(); + } + return; + } + [self.activeRippleLayer fadeOutRippleAnimated:animated completion:completion]; +} + +- (void)setActiveRippleColor:(UIColor *)activeRippleColor { + _activeRippleColor = activeRippleColor; + self.activeRippleLayer.fillColor = activeRippleColor.CGColor; +} + +#pragma mark - MDCRippleLayerDelegate + +- (void)rippleLayerTouchDownAnimationDidBegin:(MDCRippleLayer *)rippleLayer { + if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchDownAnimationDidBegin:)]) { + [self.rippleViewDelegate rippleTouchDownAnimationDidBegin:self]; + } +} + +- (void)rippleLayerTouchDownAnimationDidEnd:(MDCRippleLayer *)rippleLayer { + if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchDownAnimationDidEnd:)]) { + [self.rippleViewDelegate rippleTouchDownAnimationDidEnd:self]; + } +} + +- (void)rippleLayerTouchUpAnimationDidBegin:(MDCRippleLayer *)rippleLayer { + if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchUpAnimationDidBegin:)]) { + [self.rippleViewDelegate rippleTouchUpAnimationDidBegin:self]; + } +} + +- (void)rippleLayerTouchUpAnimationDidEnd:(MDCRippleLayer *)rippleLayer { + if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchUpAnimationDidEnd:)]) { + [self.rippleViewDelegate rippleTouchUpAnimationDidEnd:self]; + } +} + +#pragma mark - CALayerDelegate + +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { + if ([event isEqualToString:@"path"] || [event isEqualToString:@"shadowPath"]) { + // We have to create a pending animation because if we are inside a UIKit animation block we + // won't know any properties of the animation block until it is commited. + MDCRipplePendingAnimation *pendingAnim = [[MDCRipplePendingAnimation alloc] init]; + pendingAnim.animationSourceLayer = self.superview.layer; + pendingAnim.fromValue = [layer.presentationLayer valueForKey:event]; + pendingAnim.toValue = nil; + pendingAnim.keyPath = event; + + return pendingAnim; + } + return nil; +} + +#pragma mark - Convenience API + ++ (MDCRippleView *)injectedRippleViewForView:(UIView *)view { + for (MDCRippleView *subview in view.subviews) { + if ([subview isKindOfClass:[MDCRippleView class]]) { + return subview; + } + } + + MDCRippleView *newRippleView = [[MDCRippleView alloc] initWithFrame:view.bounds]; + newRippleView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [view addSubview:newRippleView]; + return newRippleView; +} + +@end + +@implementation MDCRipplePendingAnimation + +- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict { + if (![anObject isKindOfClass:[CAShapeLayer class]]) { + return; + } + + // In order to synchronize our animation with UIKit animations we have to fetch the resizing + // animation created by UIKit and copy the configuration to our custom animation. + CAShapeLayer *layer = (CAShapeLayer *)anObject; + CAAnimation *boundsAction = [self.animationSourceLayer animationForKey:@"bounds.size"]; + BOOL isBasicAnimation = [boundsAction isKindOfClass:[CABasicAnimation class]]; + if (!isBasicAnimation) { + NSAssert(isBasicAnimation || !boundsAction, + @"This animation synchronization does not support a bounds size change that " + @"isn't of a CABasicAnimation type."); + return; + } + CABasicAnimation *animation = (CABasicAnimation *)[boundsAction copy]; + animation.keyPath = self.keyPath; + animation.fromValue = self.fromValue; + animation.toValue = self.toValue; + + [layer addAnimation:animation forKey:event]; +} +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h new file mode 100644 index 00000000..7952b406 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h @@ -0,0 +1,55 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCRippleView; + +/** + The ripple view delegate protocol. Clients may implement this protocol to receive updates on + the ripple's animation lifecycle. + */ +@protocol MDCRippleViewDelegate + +@optional + +/** + Called when the ripple view began its touch down animation. + + @param rippleView The MDCRippleView. + */ +- (void)rippleTouchDownAnimationDidBegin:(nonnull MDCRippleView *)rippleView; + +/** + Called when the ripple view ended its touch down animation. + + @param rippleView The MDCRippleView. + */ +- (void)rippleTouchDownAnimationDidEnd:(nonnull MDCRippleView *)rippleView; + +/** + Called when the ripple view began its touch up animation. + + @param rippleView The MDCRippleView. + */ +- (void)rippleTouchUpAnimationDidBegin:(nonnull MDCRippleView *)rippleView; + +/** + Called when the ripple view ended its touch up animation. + + @param rippleView The MDCRippleView. + */ +- (void)rippleTouchUpAnimationDidEnd:(nonnull MDCRippleView *)rippleView; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h new file mode 100644 index 00000000..354517e1 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h @@ -0,0 +1,165 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRippleView.h" + +/** + Provides the current state of the ripple. The ripple is either in its normal state, or in the + selected state where the ripple remains spread on the view. + + - MDCRippleStateNormal: The ripple isn't currently presented. + - MDCRippleStateHighlighted: The ripple is activated and shown. + - MDCRippleStateSelected: The ripple is in the selected state. + - MDCRippleStateDragged: The ripple is in the dragged state. + */ +typedef NS_OPTIONS(NSInteger, MDCRippleState) { + MDCRippleStateNormal = 0, + MDCRippleStateHighlighted = 1 << 0, + MDCRippleStateSelected = 1 << 1, + MDCRippleStateDragged = 1 << 2, +}; + +/** + This class subclasses MDCRippleView which provides the Ripple functionality, and adds + to it support for states. By providing states, you can set the overlay color for each state, and + change the state of the RippleView by using its highlighted/selected/dragged API. + + This class, as opposed to MDCRippleView is less passive in its approach and listens to touches to + be able to record the last touch and initiate the Ripple from there, as opposed to receiving + the touch as input. It also is able to identify when a tap goes off/on the view, and fades in/out + the ripple accordingly. + + Lastly, the implementation for states follows Material Guidelines closely and is built to work + well with UICollectionViewCells, UITableViewCells, and UIControls. Therefore MDCStatefulRippleView + should be used when one wants to leverage the Material state system and should ideally be + configured/set alongside the UIKit APIs (i.e. UIControlState or cell's setSelected/setHighlighted). + */ +__attribute__((objc_subclassing_restricted)) @interface MDCStatefulRippleView : MDCRippleView + +/** + This BOOL is set to YES if the ripple is currently selected, or NO otherwise. + It only has significance if allowsSelection is set to YES. + + Defaults to NO. + */ +@property(nonatomic, getter=isSelected) BOOL selected; + +/** + This BOOL is set to YES if the ripple is currently highlighted, or NO otherwise. + + Note: The reason for not calling this property `highlighted` is due to UIKit's internal logic of + crawling and setting all subviews of a UICollectionViewCell to highlighted when a cell goes + into the selected state. Because we want the ripple view to imitate the state of the cell itself + when inserted into a cell, and not become the state of the cell's subviews, we have named the + property `rippleHighlighted` to make sure the state isn't altered by UIKit. + + Defaults to NO. + */ +@property(nonatomic, getter=isRippleHighlighted) BOOL rippleHighlighted; + +/** + This BOOL is set to YES if the ripple is currently dragged, or NO otherwise. + This state is only triggered manually by setting this property to YES. + + Defaults to NO. + */ +@property(nonatomic, getter=isDragged) BOOL dragged; + +/** + This BOOL is set to YES if the ripple allows selection, or NO otherwise. + + Note: If allowsSelection is set to NO, it will also set selected to NO if selected was YES prior. + + Defaults to NO. + */ +@property(nonatomic) BOOL allowsSelection; + +/** + Sets the color of the ripple for state. + + @param rippleColor The ripple color to set the ripple to. + @param state The state of the ripple in which to set the ripple color. + */ +- (void)setRippleColor:(nullable UIColor *)rippleColor forState:(MDCRippleState)state; + +/** + Gets the ripple color for the given state. + + @param state The ripple's state. + @return the color of the ripple for state. + */ +- (nullable UIColor *)rippleColorForState:(MDCRippleState)state; + +/** + The next three methods are important to get the correct behavior and functionality + for the stateful ripple. + The methods need to be invoked in the corresponding `touchesBegan`, `touchesMoved`, + `touchesEnded`, and `touchesCancelled` in the superview of this view. + More detailed information can be found for each method below. + */ +#pragma mark - Superview Touch Handling + +/** + The stateful ripple view should receive the initial touch so it knows where to initiate the + ripple effect from. It also lets the ripple view's `setHighlighted` know if it has been triggered + due to a touch. + + @param touches The touches, as provided by the superview's `touchesBegan:withEvent:`. + @param event The event, as provided by the superview's `touchesBegan:withEvent:`. + */ +- (void)touchesBegan:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; + +/** + The stateful ripple view should fade in an out as a held touch goes in and out of the view. + To identify that a touch is held and then moved we need to have the superview pass the touch to + the ripple so it can react appropriately. + + This class needs to be invoked in the `touchesMoved:withEvent:` of its superview before super is + called. This is because otherwise `setHighlighted` will be triggered prior to knowing if the + touch is outside the bounds or not and won't be able to act accordingly. + + @param touches The touches, as provided by the superview's `touchesMoved:withEvent:`. + @param event The event, as provided by the superview's `touchesMoved:withEvent:`. + */ +- (void)touchesMoved:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; + +/** + The stateful ripple view needs to identify an end of a touch for two reasons: + 1. To know the touch has ended so if `setHighlighted` isn't triggered by a touch, it shouldn't + animate the ripple. + 2. To dissolve the existing ripple if the touch is let go outside the hit target of the superview. + + This class needs to be invoked in the `touchesEnded:withEvent:` of its superview before super is + called. + + @param touches The touches, as provided by the superview's `touchesEnded:withEvent:`. + @param event The event, as provided by the superview's `touchesEnded:withEvent:`. + */ +- (void)touchesEnded:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; + +/** + The stateful ripple view needs to identify a cancellation of a touch for two reason: + 1. To know the touch has ended so if `setHighlighted` isn't triggered by a touch, it shouldn't + animate the ripple. + 2. To dissolve the existing ripple if the touch gets cancelled. + + This class needs to be invoked in the `touchesCancelled:withEvent:` of its superview before super + is called. + + @param touches The touches, as provided by the superview's `touchesCancelled:withEvent:`. + @param event The event, as provided by the superview's `touchesCancelled:withEvent:`. + */ +- (void)touchesCancelled:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m new file mode 100644 index 00000000..2f0342dd --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m @@ -0,0 +1,273 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCStatefulRippleView.h" +#import "private/MDCRippleLayer.h" + +static const CGFloat kDefaultRippleAlpha = (CGFloat)0.12; +static const CGFloat kDefaultRippleSelectedAlpha = (CGFloat)0.08; +static const CGFloat kDefaultRippleDraggedAlpha = (CGFloat)0.08; + +static UIColor *RippleSelectedColor(void) { + return [UIColor colorWithRed:(CGFloat)0.384 green:0 blue:(CGFloat)0.933 alpha:1]; +} + +@interface MDCStatefulRippleView () +@property(nonatomic, strong) MDCRippleLayer *activeRippleLayer; +@end + +@implementation MDCStatefulRippleView { + NSMutableDictionary *_rippleColors; + BOOL _tapWentOutsideOfBounds; + BOOL _tapWentInsideOfBounds; + BOOL _didReceiveTouch; + CGPoint _lastTouch; +} + +@dynamic activeRippleLayer; + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self commonMDCStatefulRippleViewInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self) { + [self commonMDCStatefulRippleViewInit]; + } + return self; +} + +- (void)commonMDCStatefulRippleViewInit { + if (_rippleColors == nil) { + _rippleColors = [NSMutableDictionary dictionary]; + UIColor *selectionColor = RippleSelectedColor(); + _rippleColors[@(MDCRippleStateNormal)] = [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha]; + _rippleColors[@(MDCRippleStateHighlighted)] = [UIColor colorWithWhite:0 + alpha:kDefaultRippleAlpha]; + _rippleColors[@(MDCRippleStateSelected)] = + [selectionColor colorWithAlphaComponent:kDefaultRippleSelectedAlpha]; + _rippleColors[@(MDCRippleStateSelected | MDCRippleStateHighlighted)] = + [selectionColor colorWithAlphaComponent:kDefaultRippleAlpha]; + _rippleColors[@(MDCRippleStateDragged)] = [UIColor colorWithWhite:0 + alpha:kDefaultRippleDraggedAlpha]; + _rippleColors[@(MDCRippleStateDragged | MDCRippleStateHighlighted)] = + [UIColor colorWithWhite:0 alpha:kDefaultRippleDraggedAlpha]; + _rippleColors[@(MDCRippleStateSelected | MDCRippleStateDragged)] = + [selectionColor colorWithAlphaComponent:kDefaultRippleDraggedAlpha]; + } +} + +- (UIColor *)rippleColorForState:(MDCRippleState)state { + UIColor *rippleColor = _rippleColors[@(state)]; + if (rippleColor == nil && (state & MDCRippleStateDragged) != 0) { + rippleColor = _rippleColors[@(MDCRippleStateDragged)]; + } else if (rippleColor == nil && (state & MDCRippleStateSelected) != 0) { + rippleColor = _rippleColors[@(MDCRippleStateSelected)]; + } + + if (rippleColor == nil) { + rippleColor = _rippleColors[@(MDCRippleStateNormal)]; + } + return rippleColor; +} + +- (void)updateRippleColor { + UIColor *rippleColor = [self rippleColorForState:self.state]; + [self setRippleColor:rippleColor]; +} + +- (void)updateActiveRippleColor { + UIColor *rippleColor = [self rippleColorForState:self.state]; + [self setActiveRippleColor:rippleColor]; +} + +- (void)setRippleColor:(UIColor *)rippleColor forState:(MDCRippleState)state { + _rippleColors[@(state)] = rippleColor; + + [self updateRippleColor]; +} + +- (MDCRippleState)state { + NSInteger state = 0; + if (self.selected) { + state |= MDCRippleStateSelected; + } + if (self.rippleHighlighted) { + state |= MDCRippleStateHighlighted; + } + if (self.dragged) { + state |= MDCRippleStateDragged; + } + return state; +} + +- (void)setAllowsSelection:(BOOL)allowsSelection { + if (!allowsSelection && self.selected) { + self.selected = NO; + } + _allowsSelection = allowsSelection; +} + +- (void)setSelected:(BOOL)selected { + if (!self.allowsSelection) { + // If we disallow selection we don't want to apply any visual or state changes for selection. + return; + } + if (_tapWentOutsideOfBounds) { + // If the tap goes outside of bounds when a selection state change is triggered, we want to + // return early and not issue the selection state change as guidelines dictate that if a tap is + // let go outside the bounds, it should not trigger an action like issuing + // a selection/deselection. + return; + } + if (selected == _selected && self.activeRippleLayer) { + // If selected is already set to YES, and there is already an active ripple layer apparent, + // we want to return early so we don't add multiple selected overlays, as there can be only one. + return; + } + _selected = selected; + // Go into the selected state visually. + if (selected) { + if (!self.activeRippleLayer) { + // If we go into the selected state but a ripple layer doesn't exist yet, it means we went + // into this state without initially creating the ripple overlay by going through the + // highlighted state. This usually occurs when cells are reused and the selected state is + // manually set to show the cell's existing state. + [self updateRippleColor]; + [self beginRippleTouchDownAtPoint:_lastTouch animated:NO completion:nil]; + } else { + [self updateActiveRippleColor]; + } + } else { + // If we are no longer selecting, we cancel all the ripples. + [self updateRippleColor]; + [self cancelAllRipplesAnimated:YES completion:nil]; + } +} + +- (void)setRippleHighlighted:(BOOL)rippleHighlighted { + if (rippleHighlighted == _rippleHighlighted) { + return; + } + _rippleHighlighted = rippleHighlighted; + // Go into the highlighted state visually. + if (rippleHighlighted && !_tapWentInsideOfBounds) { + // If ripple becomes highlighted we initiate a ripple with animation. + [self updateRippleColor]; + [self beginRippleTouchDownAtPoint:_lastTouch animated:_didReceiveTouch completion:nil]; + } else if (!rippleHighlighted) { + // In cases where the ripple stops being highlighted, we can only dissolve the ripple if we are + // not going into selection (as in that case it will stay and become a selected color and be an + // overlay), or when it is already selected and therefore it means it will stay selected and + // the ripple should act similarly to going in and out of highlighted and offer a standard + // ripple touch feedback on top of the selected overlay. + BOOL notAllowingSelectionOrAlreadySelected = !self.allowsSelection || self.selected; + + // We should dissolve the ripple in these cases: + // 1. where this is a normal tap going in and out of highlighted indicating a ripple effect, + // same goes to when there is already a selected overlay on top of it. + // 2. when also we aren't currently in dragged because in dragged we keep the overlay there and + // when dragged is set to NO it releases all the overlays. + // 3. lastly also when the tap isn't currently out of the bounds of the surface, as in that case + // the behavior of the ripple returns to its original state as releasing outside the bounds + // acts as "no action was done". + BOOL shouldDissolveRipple = + notAllowingSelectionOrAlreadySelected && !self.dragged && !_tapWentOutsideOfBounds; + + if (shouldDissolveRipple) { + // We dissolve the ripple when highlighted is NO, unless we are going into + // selection or dragging. + [self updateRippleColor]; + [self beginRippleTouchUpAnimated:YES completion:nil]; + } + } +} + +- (void)setDragged:(BOOL)dragged { + if (dragged == _dragged) { + return; + } + _dragged = dragged; + // Go into the dragged state visually. + if (dragged) { + if (!self.activeRippleLayer) { + // If we go into the dragged state manually, without coming from the highlighted state, + // We present the ripple overlay instantly without animation. + [self updateRippleColor]; + [self beginRippleTouchDownAtPoint:_lastTouch animated:NO completion:nil]; + } else { + [self updateActiveRippleColor]; + } + } else { + // If we are no longer dragging, we cancel all the ripples. + [self updateRippleColor]; + [self cancelAllRipplesAnimated:YES completion:nil]; + } +} + +#pragma mark - Superview Touch Handling + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + UITouch *touch = touches.anyObject; + CGPoint point = [touch locationInView:self]; + // Once we find that the tap is inside the hit area insets of the encapsulating view (superview + // of the ripple view), we would want to capture the touch from where to initiate the ripple, + // and also initialize the values to indicate there was a touch, and the held tap's location + // to fade the ripple in and out if the help tap goes inside or outside the hit area. + _lastTouch = point; + if (!_didReceiveTouch) { + _didReceiveTouch = YES; + _tapWentInsideOfBounds = NO; + _tapWentOutsideOfBounds = NO; + } +} + +- (BOOL)pointInsideSuperview:(CGPoint)point withEvent:(UIEvent *)event { + return [self.superview pointInside:point withEvent:event]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + // When the touch is held and moved outside and inside the bounds of the surface, + // the ripple should gracefully fade out and in accordingly. + UITouch *touch = touches.anyObject; + CGPoint point = [touch locationInView:self]; + BOOL pointContainedInSuperview = [self pointInsideSuperview:point withEvent:event]; + if (pointContainedInSuperview && _tapWentOutsideOfBounds) { + _tapWentInsideOfBounds = YES; + _tapWentOutsideOfBounds = NO; + [self fadeInRippleAnimated:YES completion:nil]; + } else if (!pointContainedInSuperview && !_tapWentOutsideOfBounds) { + _tapWentOutsideOfBounds = YES; + [self fadeOutRippleAnimated:YES completion:nil]; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + _didReceiveTouch = NO; + if (_tapWentOutsideOfBounds) { + [self beginRippleTouchUpAnimated:NO completion:nil]; + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + _didReceiveTouch = NO; + [self beginRippleTouchUpAnimated:YES completion:nil]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h new file mode 100644 index 00000000..55e16cbc --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h @@ -0,0 +1,19 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRippleTouchController.h" +#import "MDCRippleTouchControllerDelegate.h" +#import "MDCRippleView.h" +#import "MDCRippleViewDelegate.h" +#import "MDCStatefulRippleView.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h new file mode 100644 index 00000000..37d07dcb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h @@ -0,0 +1,96 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no +// longer import delegates as transitive dependencies. +#import "MDCRippleLayerDelegate.h" + +/** + Convenience naming for the completion blocks the ripple animation provides. + */ +typedef void (^MDCRippleCompletionBlock)(void); + +@protocol MDCRippleLayerDelegate; + +/** + The Ripple Layer presents and animates the ripple. There can be multiple Ripple Layers + as sublayers for MDCRippleView. The Ripple Layer subclasses CAShapeLayer to leverage the path + property so we can conveniently draw the ripple circle. + */ +@interface MDCRippleLayer : CAShapeLayer + +/** + The ripple layer delegate. + */ +@property(nonatomic, weak, nullable) id rippleLayerDelegate; + +/** + A bool indicating if the start animation is currently active for this ripple layer. + */ +@property(nonatomic, assign, readonly, getter=isStartAnimationActive) BOOL startAnimationActive; + +/** + The ripple's touch down animation start time. It is measured in seconds + as the current absolute time when the animation begins. + */ +@property(nonatomic, assign) CFTimeInterval rippleTouchDownStartTime; + +/** + The radius the ripple expands to when activated. + + @note This only impacts new ripples, if a ripple is already being animated this property will have + no impact. + */ +@property(nonatomic, assign) CGFloat maximumRadius; + +/** + Starts the ripple at the given point. + + @param point The point to start the ripple animation. + @param animated Whether or not the ripple should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)startRippleAtPoint:(CGPoint)point + animated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Ends the ripple. + + @param animated Whether or not the ripple should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)endRippleAnimated:(BOOL)animated completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Fades the ripple in by changing the layer's opacity. + + @param animated Whether or not the fade in should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)fadeInRippleAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; + +/** + Fades the ripple out by changing the layer's opacity. + + @param animated Whether or not the fade out should be animated or not. + @param completion A completion block called after the completion of the animation. + */ +- (void)fadeOutRippleAnimated:(BOOL)animated + completion:(nullable MDCRippleCompletionBlock)completion; +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m new file mode 100644 index 00000000..d8ef82d8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m @@ -0,0 +1,194 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRippleLayer.h" + +#import "MaterialAnimationTiming.h" +#import "MDCRippleLayerDelegate.h" + +static const CGFloat kExpandRippleBeyondSurface = 10; +static const CGFloat kRippleStartingScale = (CGFloat)0.6; +static const CGFloat kRippleTouchDownDuration = (CGFloat)0.225; +static const CGFloat kRippleTouchUpDuration = (CGFloat)0.15; +static const CGFloat kRippleFadeInDuration = (CGFloat)0.075; +static const CGFloat kRippleFadeOutDuration = (CGFloat)0.075; +static const CGFloat kRippleFadeOutDelay = (CGFloat)0.15; + +static NSString *const kRippleLayerOpacityString = @"opacity"; +static NSString *const kRippleLayerPositionString = @"position"; +static NSString *const kRippleLayerScaleString = @"transform.scale"; + +static CGFloat GetInitialRippleRadius(CGRect rect) { + return MAX(CGRectGetWidth(rect), CGRectGetHeight(rect)) * kRippleStartingScale / 2.f; +} + +static CGFloat GetFinalRippleRadius(CGRect rect) { + return (CGFloat)(hypot(CGRectGetMidX(rect), CGRectGetMidY(rect)) + kExpandRippleBeyondSurface); +} + +@implementation MDCRippleLayer + +- (void)setNeedsLayout { + [super setNeedsLayout]; + + [self calculateRadiusAndSetPath]; + self.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); +} + +- (void)calculateRadiusAndSetPath { + [self setPathFromRadii:[self calculateRadius]]; +} + +- (void)setPathFromRadii:(CGFloat)radius { + CGRect ovalRect = CGRectMake(CGRectGetMidX(self.bounds) - radius, + CGRectGetMidY(self.bounds) - radius, radius * 2, radius * 2); + UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:ovalRect]; + self.path = circlePath.CGPath; +} + +- (CGFloat)calculateRadius { + return self.maximumRadius > 0 ? self.maximumRadius : GetFinalRippleRadius(self.bounds); +} + +- (void)startRippleAtPoint:(CGPoint)point + animated:(BOOL)animated + completion:(MDCRippleCompletionBlock)completion { + [self.rippleLayerDelegate rippleLayerTouchDownAnimationDidBegin:self]; + CGFloat finalRadius = [self calculateRadius]; + [self setPathFromRadii:finalRadius]; + self.opacity = 1; + self.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + if (!animated) { + if (completion) { + completion(); + } + [self.rippleLayerDelegate rippleLayerTouchDownAnimationDidEnd:self]; + } else { + _startAnimationActive = YES; + + CGFloat startingScale = GetInitialRippleRadius(self.bounds) / finalRadius; + CABasicAnimation *scaleAnim = [[CABasicAnimation alloc] init]; + scaleAnim.keyPath = kRippleLayerScaleString; + scaleAnim.fromValue = @(startingScale); + scaleAnim.toValue = @1; + scaleAnim.timingFunction = + [CAMediaTimingFunction mdc_functionWithType:MDCAnimationTimingFunctionStandard]; + + UIBezierPath *centerPath = [UIBezierPath bezierPath]; + CGPoint startPoint = point; + CGPoint endPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + [centerPath moveToPoint:startPoint]; + [centerPath addLineToPoint:endPoint]; + [centerPath closePath]; + + CAKeyframeAnimation *positionAnim = [[CAKeyframeAnimation alloc] init]; + positionAnim.keyPath = kRippleLayerPositionString; + positionAnim.path = centerPath.CGPath; + positionAnim.keyTimes = @[ @0, @1 ]; + positionAnim.values = @[ @0, @1 ]; + positionAnim.timingFunction = + [CAMediaTimingFunction mdc_functionWithType:MDCAnimationTimingFunctionStandard]; + + CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; + fadeInAnim.keyPath = kRippleLayerOpacityString; + fadeInAnim.fromValue = @0; + fadeInAnim.toValue = @1; + fadeInAnim.duration = kRippleFadeInDuration; + fadeInAnim.timingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + + [CATransaction begin]; + CAAnimationGroup *animGroup = [[CAAnimationGroup alloc] init]; + animGroup.animations = @[ scaleAnim, positionAnim, fadeInAnim ]; + animGroup.duration = kRippleTouchDownDuration; + [CATransaction setCompletionBlock:^{ + self->_startAnimationActive = NO; + if (completion) { + completion(); + } + [self.rippleLayerDelegate rippleLayerTouchDownAnimationDidEnd:self]; + }]; + [self addAnimation:animGroup forKey:nil]; + _rippleTouchDownStartTime = CACurrentMediaTime(); + [CATransaction commit]; + } +} + +- (void)fadeInRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { + [CATransaction begin]; + CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; + fadeInAnim.keyPath = kRippleLayerOpacityString; + fadeInAnim.fromValue = @0; + fadeInAnim.toValue = @1; + fadeInAnim.duration = animated ? kRippleFadeInDuration : 0; + fadeInAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + fadeInAnim.fillMode = kCAFillModeForwards; + fadeInAnim.removedOnCompletion = NO; + [CATransaction setCompletionBlock:^{ + if (completion) { + completion(); + } + }]; + [self addAnimation:fadeInAnim forKey:nil]; + [CATransaction commit]; +} + +- (void)fadeOutRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { + [CATransaction begin]; + CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; + fadeInAnim.keyPath = kRippleLayerOpacityString; + fadeInAnim.fromValue = @1; + fadeInAnim.toValue = @0; + fadeInAnim.duration = animated ? kRippleFadeOutDuration : 0; + fadeInAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + fadeInAnim.fillMode = kCAFillModeForwards; + fadeInAnim.removedOnCompletion = NO; + [CATransaction setCompletionBlock:^{ + if (completion) { + completion(); + } + }]; + [self addAnimation:fadeInAnim forKey:nil]; + [CATransaction commit]; +} + +- (void)endRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { + CGFloat delay = 0; + if (self.startAnimationActive) { + delay = kRippleFadeOutDelay; + } + [self.rippleLayerDelegate rippleLayerTouchUpAnimationDidBegin:self]; + [CATransaction begin]; + CABasicAnimation *fadeOutAnim = [[CABasicAnimation alloc] init]; + fadeOutAnim.keyPath = kRippleLayerOpacityString; + fadeOutAnim.fromValue = @1; + fadeOutAnim.toValue = @0; + fadeOutAnim.duration = animated ? kRippleTouchUpDuration : 0; + fadeOutAnim.beginTime = [self convertTime:_rippleTouchDownStartTime + delay fromLayer:nil]; + fadeOutAnim.timingFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + fadeOutAnim.fillMode = kCAFillModeForwards; + fadeOutAnim.removedOnCompletion = NO; + [CATransaction setCompletionBlock:^{ + if (completion) { + completion(); + } + [self.rippleLayerDelegate rippleLayerTouchUpAnimationDidEnd:self]; + [self removeFromSuperlayer]; + }]; + [self addAnimation:fadeOutAnim forKey:nil]; + [CATransaction commit]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h new file mode 100644 index 00000000..eb2b08b3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h @@ -0,0 +1,53 @@ +// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCRippleLayer; + +/** + The ripple layer delegate protocol to let MDCRippleView know of the layer's + ripple animation timeline. + */ +@protocol MDCRippleLayerDelegate + +/** + Called when the ripple layer began its touch down animation. + + @param rippleLayer The MDCRippleLayer. + */ +- (void)rippleLayerTouchDownAnimationDidBegin:(nonnull MDCRippleLayer *)rippleLayer; + +/** + Called when the ripple layer ended its touch down animation. + + @param rippleLayer The MDCRippleLayer. + */ +- (void)rippleLayerTouchDownAnimationDidEnd:(nonnull MDCRippleLayer *)rippleLayer; + +/** + Called when the ripple layer began its touch up animation. + + @param rippleLayer The MDCRippleLayer. + */ +- (void)rippleLayerTouchUpAnimationDidBegin:(nonnull MDCRippleLayer *)rippleLayer; + +/** + Called when the ripple layer ended its touch up animation. + + @param rippleLayer The MDCRippleLayer. + */ +- (void)rippleLayerTouchUpAnimationDidEnd:(nonnull MDCRippleLayer *)rippleLayer; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h new file mode 100644 index 00000000..deaa46c4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h @@ -0,0 +1,59 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + An immutable shadow object that consists of shadow properties (opacity, radius, offset). + + To generate a shadow instance, please use the MDCShadowBuilder APIs. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShadow : NSObject + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** CALayer.shadowOpacity */ +@property(nonatomic, readonly) CGFloat opacity; + +/** CALayer.shadowRadius */ +@property(nonatomic, readonly) CGFloat radius; + +/** CALayer.shadowOffset */ +@property(nonatomic, readonly) CGSize offset; + +@end + +/** + Mutable builder to construct immutable `MDCShadow` objects. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShadowBuilder : NSObject + +/** CALayer.shadowOpacity */ +@property(nonatomic) CGFloat opacity; + +/** CALayer.shadowRadius */ +@property(nonatomic) CGFloat radius; + +/** CALayer.shadowOffset */ +@property(nonatomic) CGSize offset; + +/** Returns an immutable value type containing a snapshot of the values in this object. */ +- (nonnull MDCShadow *)build; + +/** Returns a builder with the provided opacity, radius, and offset properties. */ ++ (nonnull MDCShadowBuilder *)builderWithOpacity:(CGFloat)opacity + radius:(CGFloat)radius + offset:(CGSize)offset; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m new file mode 100644 index 00000000..94a61441 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m @@ -0,0 +1,69 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadow.h" + +@implementation MDCShadow + +- (instancetype)initWithOpacity:(CGFloat)opacity radius:(CGFloat)radius offset:(CGSize)offset { + self = [super init]; + if (self) { + _opacity = opacity; + _radius = radius; + _offset = offset; + } + return self; +} + +- (BOOL)isEqual:(id)other { + if (self == other) { + return YES; + } + if (![other isKindOfClass:[MDCShadow class]]) { + return NO; + } + MDCShadow *otherShadow = other; + return _opacity == otherShadow.opacity && _radius == otherShadow.radius && + CGSizeEqualToSize(_offset, otherShadow.offset); +} + +- (NSUInteger)hash { + const NSUInteger kPrime = 31; + NSUInteger result = 1; + result = result * kPrime + (NSUInteger)_opacity; + result = result * kPrime + (NSUInteger)_radius; + result = result * kPrime + (NSUInteger)(_offset.width); + result = result * kPrime + (NSUInteger)(_offset.height); + return result; +} + +@end + +@implementation MDCShadowBuilder + +- (MDCShadow *)build { + return [[MDCShadow alloc] initWithOpacity:self.opacity radius:self.radius offset:self.offset]; +} + ++ (MDCShadowBuilder *)builderWithOpacity:(CGFloat)opacity + radius:(CGFloat)radius + offset:(CGSize)offset { + MDCShadowBuilder *builder = [[MDCShadowBuilder alloc] init]; + builder.opacity = opacity; + builder.radius = radius; + builder.offset = offset; + return builder; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h new file mode 100644 index 00000000..d69730cb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h @@ -0,0 +1,145 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCShadow.h" + +/** + An immutable shadows collection object that encapsulates a storage of shadows correlating to + elevation values. + + To apply a shadow on a view, please see and use the C methods @c MDCConfigureShadowForView and + @c MDCConfigureShadowForViewWithPath. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShadowsCollection : NSObject + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Returns an MDCShadow instance representing the shadow properties for the given elevation (in + points) by fetching from the @c shadowsCollection container holding elevation to MDCShadow instance + bindings. + + Note: If the provided elevation is not stored, the shadow properties that are set are of the + nearest elevation above the provided elevation. i.e. if elevations 1 and 3 are set, and an + elevation of 2 is provided as input, the shadow properties set will be of elevation 3. If the + provided elevation is above the highest elevation value that is set, then the shadow properties set + will be of the highest elevation. + */ +- (nonnull MDCShadow *)shadowForElevation:(CGFloat)elevation; + +@end + +/** + A shadows collection object builder that generates an MDCShadowsCollection instance using the @c + build method. The object allows to add shadows for a given elevation. + + To ensure no nullability situations, please instantiate the builder using @c + builderWithShadow:forElevation and provide an initial MDCShadow instance. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShadowsCollectionBuilder : NSObject + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Adds a shadow for the given elevation. + + @param shadow An MDCShadow object consisting of shadow properties such as radius, opacity, and + offset. + @param elevation The elevation provided in points (dp). + */ +- (void)addShadow:(MDCShadow *_Nonnull)shadow forElevation:(CGFloat)elevation; + +/** + Adds a dictionary of shadows with their correlating elevations to the shadows collection. + + @param shadowsForElevations A dictionary holding NSNumber elevation value keys, and their values + are MDCShadow objects correlating to their elevation key. + */ +- (void)addShadowsForElevations: + (NSDictionary *_Nonnull)shadowsForElevations; + +/** + Creates an initial MDCShadowsCollectionBuilder by providing it an initial MDCShadow object and its + correlating elevation. + + @param shadow An MDCShadow object consisting of shadow properties such as radius, opacity, and + offset. + @param elevation The elevation provided in points (dp). + */ ++ (nonnull MDCShadowsCollectionBuilder *)builderWithShadow:(MDCShadow *_Nonnull)shadow + forElevation:(CGFloat)elevation; + +/** + Builds and returns an MDCShadowsCollection instance given the provided shadows using the + @c addShadow:forElevation and @c addShadowsForElevations: APIs. + */ +- (nonnull MDCShadowsCollection *)build; + +@end + +/** + Given a view, MDCShadow instance, and a shadow color (e.g. `MDCShadowColor()`), updates the shadow + properties of `view.layer`: + + * shadowColor + * shadowOpacity + * shadowRadius + * shadowOffset + * shadowPath + + `shadowPath` will be set to the current bounds of the given view (including rounded + corners if set on view.layer). + + TODO(b/182581383): maskedCorners, cornerCurve, and cornerCurveExpansionFactor are not + yet supported. + + Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` + whenever the view's bounds change. + */ +FOUNDATION_EXTERN void MDCConfigureShadowForView(UIView *_Nonnull view, MDCShadow *_Nonnull shadow, + UIColor *_Nonnull shadowColor) + NS_SWIFT_NAME(MDCConfigureShadow(for:shadow:color:)); + +/** + Given a view, MDCShadow instance, a shadow color (e.g. `MDCShadowColor()`), and a `path` in the + view's coordinate space representing the shape of the view, updates the shadow properties of + `view.layer`: + + * shadowColor + * shadowOpacity + * shadowRadius + * shadowOffset + * shadowPath + + Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` + whenever the view's bounds or shape changes. + */ +FOUNDATION_EXTERN void MDCConfigureShadowForViewWithPath(UIView *_Nonnull view, + MDCShadow *_Nonnull shadow, + UIColor *_Nonnull shadowColor, + CGPathRef _Nonnull path) + NS_SWIFT_NAME(MDCConfigureShadow(for:shadow:color:path:)); + +/** + Default color for a Material shadow. On iOS >= 13, this is a dynamic color. + */ +FOUNDATION_EXTERN UIColor *_Nonnull MDCShadowColor(void); + +/** + Returns an MDCShadowsCollection instance with predefined defaults of shadow properties (opacity, + radius, offset) for elevations. + */ +FOUNDATION_EXTERN MDCShadowsCollection *_Nonnull MDCShadowsCollectionDefault(void); diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m new file mode 100644 index 00000000..5b22fca3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m @@ -0,0 +1,165 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadowsCollection.h" + +#import "MaterialAvailability.h" +#import "MDCShadow.h" + +@implementation MDCShadowsCollection { + NSDictionary *_shadowValuesForElevation; + NSArray *_orderedKeys; +} + +- (instancetype)initWithShadowValuesForElevation: + (NSDictionary *)shadowValuesForElevation { + self = [super init]; + if (self) { + _shadowValuesForElevation = [shadowValuesForElevation copy]; + _orderedKeys = [shadowValuesForElevation.allKeys sortedArrayUsingSelector:@selector(compare:)]; + } + return self; +} + +- (MDCShadow *)shadowForElevation:(CGFloat)elevation { + NSUInteger lookupIndex = [self indexInOrderedKeysOfGivenElevation:elevation]; + // If the value is larger than the largest value in the array, we will return the highest value in + // the array. + if (lookupIndex >= _orderedKeys.count) { + lookupIndex = _orderedKeys.count - 1; + } + + NSNumber *key = _orderedKeys[lookupIndex]; + return [_shadowValuesForElevation objectForKey:key]; +} + +- (NSUInteger)indexInOrderedKeysOfGivenElevation:(CGFloat)elevation { + NSNumber *num = @(elevation); + NSUInteger index = + [_orderedKeys indexOfObject:num + inSortedRange:NSMakeRange(0, _orderedKeys.count) + options:NSBinarySearchingInsertionIndex + usingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) { + return [num1 compare:num2]; + }]; + return index; +} + +@end + +@implementation MDCShadowsCollectionBuilder { + NSMutableDictionary *_shadowValuesForElevation; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _shadowValuesForElevation = [[NSMutableDictionary alloc] init]; + } + return self; +} + ++ (MDCShadowsCollectionBuilder *)builderWithShadow:(MDCShadow *)shadow + forElevation:(CGFloat)elevation { + MDCShadowsCollectionBuilder *builder = [[self alloc] init]; + [builder addShadow:shadow forElevation:elevation]; + return builder; +} + +- (void)addShadow:(MDCShadow *)shadow forElevation:(CGFloat)elevation { + [_shadowValuesForElevation setObject:shadow forKey:@(elevation)]; +} + +- (void)addShadowsForElevations:(NSDictionary *)shadowsForElevations { + [_shadowValuesForElevation addEntriesFromDictionary:shadowsForElevations]; +} + +- (MDCShadowsCollection *)build { + return [[MDCShadowsCollection alloc] initWithShadowValuesForElevation:_shadowValuesForElevation]; +} + +@end + +static UIColor *LightStyleShadowColor(void) { + static UIColor *lightStyleShadowColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lightStyleShadowColor = [UIColor colorWithRed:0.235 green:0.251 blue:0.263 alpha:1]; + }); + return lightStyleShadowColor; +} + +UIColor *MDCShadowColor(void) { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + return [UIColor colorWithDynamicProvider:^(UITraitCollection *traitCollection) { + switch (traitCollection.userInterfaceStyle) { + case UIUserInterfaceStyleUnspecified: + /* FALLTHROUGH - TODO(b/185199658): Migrate to proper fallthrough logic */ + case UIUserInterfaceStyleLight: + return LightStyleShadowColor(); + case UIUserInterfaceStyleDark: + return UIColor.blackColor; + } + __builtin_unreachable(); + }]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + return LightStyleShadowColor(); +} + +MDCShadowsCollection *MDCShadowsCollectionDefault(void) { + static MDCShadowsCollection *shadowsCollection; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + MDCShadow *shadow = [[MDCShadowBuilder builderWithOpacity:0 radius:0 + offset:CGSizeMake(0, 0)] build]; + MDCShadowsCollectionBuilder *shadowsBuilder = + [MDCShadowsCollectionBuilder builderWithShadow:shadow forElevation:0]; + NSDictionary *shadowValuesForElevation = @{ + @1 : [[MDCShadowBuilder builderWithOpacity:0.43 radius:2.5 offset:CGSizeMake(0, 1)] build], + @3 : [[MDCShadowBuilder builderWithOpacity:0.4 radius:3.25 offset:CGSizeMake(0, 1.25)] build], + @6 : [[MDCShadowBuilder builderWithOpacity:0.34 radius:4.75 + offset:CGSizeMake(0, 2.25)] build], + @8 : [[MDCShadowBuilder builderWithOpacity:0.42 radius:6 offset:CGSizeMake(0, 3)] build], + @12 : [[MDCShadowBuilder builderWithOpacity:0.4 radius:7.25 offset:CGSizeMake(0, 5)] build], + }; + [shadowsBuilder addShadowsForElevations:shadowValuesForElevation]; + shadowsCollection = [shadowsBuilder build]; + }); + return shadowsCollection; +} + +void MDCConfigureShadowForView(UIView *view, MDCShadow *shadow, UIColor *shadowColor) { + // The bezierPathWithRoundedRect API supports both a cornerRadius of 0 (created just a square + // path) and also rounded corners where the cornerRadius is >0. + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:view.bounds + cornerRadius:view.layer.cornerRadius]; + + MDCConfigureShadowForViewWithPath(view, shadow, shadowColor, path.CGPath); +} + +void MDCConfigureShadowForViewWithPath(UIView *view, MDCShadow *shadow, UIColor *shadowColor, + CGPathRef path) { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(ios 13.0, *)) { + shadowColor = [shadowColor resolvedColorWithTraitCollection:view.traitCollection]; + } +#endif // MDC_AVAILABLE_SDK_IOS(13_0) + view.layer.shadowColor = shadowColor.CGColor; + view.layer.shadowOpacity = (float)shadow.opacity; + view.layer.shadowRadius = shadow.radius; + view.layer.shadowOffset = shadow.offset; + view.layer.shadowPath = path; +} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h new file mode 100644 index 00000000..c66cffe4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h @@ -0,0 +1,17 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights +// Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadow.h" +#import "MDCShadowsCollection.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h new file mode 100644 index 00000000..3f1a13d5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h @@ -0,0 +1,106 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#ifdef NS_TYPED_EXTENSIBLE_ENUM // This macro is introduced in Xcode 9. +#define MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM NS_TYPED_EXTENSIBLE_ENUM +#elif __has_attribute(swift_wrapper) // Backwards compatibility for Xcode 8. +#define MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM __attribute__((swift_wrapper(struct))) +#else +#define MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM +#endif + +/** + Constants for elevation: the relative depth, or distance, between two surfaces along the z-axis. + https://material.io/go/design-elevation + */ +NS_SWIFT_NAME(ShadowElevation) +typedef CGFloat MDCShadowElevation MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM; + +/** The shadow elevation of the app bar. */ +static const MDCShadowElevation MDCShadowElevationAppBar = (CGFloat)4.0; + +/** The shadow elevation of the Bottom App Bar. */ +static const MDCShadowElevation MDCShadowElevationBottomAppBar = (CGFloat)8.0; + +/** The shadow elevation of the Bottom App Bar. */ +static const MDCShadowElevation MDCShadowElevationBottomNavigationBar = (CGFloat)8.0; + +/** The shadow elevation of a card in its picked up state. */ +static const MDCShadowElevation MDCShadowElevationCardPickedUp = (CGFloat)8.0; + +/** The shadow elevation of a card in its resting state. */ +static const MDCShadowElevation MDCShadowElevationCardResting = (CGFloat)2.0; + +/** The shadow elevation of dialogs. */ +static const MDCShadowElevation MDCShadowElevationDialog = (CGFloat)24.0; + +/** The shadow elevation of the floating action button in its pressed state. */ +static const MDCShadowElevation MDCShadowElevationFABPressed = (CGFloat)12.0; + +/** The shadow elevation of the floating action button in its resting state. */ +static const MDCShadowElevation MDCShadowElevationFABResting = (CGFloat)6.0; + +/** The shadow elevation of a menu. */ +static const MDCShadowElevation MDCShadowElevationMenu = (CGFloat)8.0; + +/** The shadow elevation of a modal bottom sheet. */ +static const MDCShadowElevation MDCShadowElevationModalActionSheet = (CGFloat)8.0; + +/** The shadow elevation of a modal bottom sheet. */ +static const MDCShadowElevation MDCShadowElevationModalBottomSheet = (CGFloat)16.0; + +/** The shadow elevation of the navigation drawer. */ +static const MDCShadowElevation MDCShadowElevationNavDrawer = (CGFloat)16.0; + +/** No shadow elevation at all. */ +static const MDCShadowElevation MDCShadowElevationNone = (CGFloat)0.0; + +/** The shadow elevation of a picker. */ +static const MDCShadowElevation MDCShadowElevationPicker = (CGFloat)24.0; + +/** The shadow elevation of the quick entry in the scrolled state. */ +static const MDCShadowElevation MDCShadowElevationQuickEntry = (CGFloat)3.0; + +/** The shadow elevation of the quick entry in the resting state. */ +static const MDCShadowElevation MDCShadowElevationQuickEntryResting = (CGFloat)2.0; + +/** The shadow elevation of a raised button in the pressed state. */ +static const MDCShadowElevation MDCShadowElevationRaisedButtonPressed = (CGFloat)8.0; + +/** The shadow elevation of a raised button in the resting state. */ +static const MDCShadowElevation MDCShadowElevationRaisedButtonResting = (CGFloat)2.0; + +/** The shadow elevation of a refresh indicator. */ +static const MDCShadowElevation MDCShadowElevationRefresh = (CGFloat)3.0; + +/** The shadow elevation of the right drawer. */ +static const MDCShadowElevation MDCShadowElevationRightDrawer = (CGFloat)16.0; + +/** The shadow elevation of the search bar in the resting state. */ +static const MDCShadowElevation MDCShadowElevationSearchBarResting = (CGFloat)2.0; + +/** The shadow elevation of the search bar in the scrolled state. */ +static const MDCShadowElevation MDCShadowElevationSearchBarScrolled = (CGFloat)3.0; + +/** The shadow elevation of the snackbar. */ +static const MDCShadowElevation MDCShadowElevationSnackbar = (CGFloat)6.0; + +/** The shadow elevation of a sub menu (+1 for each additional sub menu). */ +static const MDCShadowElevation MDCShadowElevationSubMenu = (CGFloat)9.0; + +/** The shadow elevation of a switch. */ +static const MDCShadowElevation MDCShadowElevationSwitch = (CGFloat)1.0; diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h new file mode 100644 index 00000000..becb2bce --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h @@ -0,0 +1,15 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadowElevations.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m new file mode 100644 index 00000000..75cef75c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m @@ -0,0 +1,17 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + This file exists to keep pod lib lint passing + */ diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h new file mode 100644 index 00000000..9657f1d1 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h @@ -0,0 +1,106 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "MaterialShadowElevations.h" + +/** + Metrics of the Material shadow effect. + + These can be used if you require your own shadow implementation but want to match the material + spec. + */ +@interface MDCShadowMetrics : NSObject +@property(nonatomic, readonly) CGFloat topShadowRadius; +@property(nonatomic, readonly) CGSize topShadowOffset; +@property(nonatomic, readonly) float topShadowOpacity; +@property(nonatomic, readonly) CGFloat bottomShadowRadius; +@property(nonatomic, readonly) CGSize bottomShadowOffset; +@property(nonatomic, readonly) float bottomShadowOpacity; + +/** + The shadow metrics for manually creating shadows given an elevation. + + @param elevation The shadow's elevation in points. + @return The shadow metrics. + */ ++ (nonnull MDCShadowMetrics *)metricsWithElevation:(CGFloat)elevation; +@end + +/** + The Material shadow effect. + + @see + https://material.io/guidelines/what-is-material/elevation-shadows.html#elevation-shadows-shadows + + Consider rasterizing your MDCShadowLayer if your view will not generally be animating or + changing size. If you need to animate a rasterized MDCShadowLayer, disable rasterization first. + + For example, if self's layerClass is MDCShadowLayer, you might introduce the following code: + + self.layer.shouldRasterize = YES; + self.layer.rasterizationScale = [UIScreen mainScreen].scale; + */ +@interface MDCShadowLayer : CALayer + +/** + The elevation of the layer in points. + + The higher the elevation, the more spread out the shadow is. This is distinct from the layer's + zPosition which can be used to order overlapping layers, but will have no affect on the size of + the shadow. + + Negative values act as if zero were specified. + + The default value is 0. + */ +@property(nonatomic, assign) MDCShadowElevation elevation; + +/** + Whether to apply the "cutout" shadow layer mask. + + If enabled, then a mask is created to ensure the interior, non-shadow part of the layer is visible. + + Default is YES. Not animatable. + */ +@property(nonatomic, getter=isShadowMaskEnabled, assign) BOOL shadowMaskEnabled; + +/** + Animates the layer's corner radius + + @note At the end of the animation the corner radius is set to your desired corner radius. + + @param cornerRadius The desired corner radius at the end of the animation + @param timingFunction The timing function you desire for the animation + @param duration The duration of the animation + */ +- (void)animateCornerRadius:(CGFloat)cornerRadius + withTimingFunction:(nonnull CAMediaTimingFunction *)timingFunction + duration:(NSTimeInterval)duration; + +@end + +/** + Subclasses can depend on MDCShadowLayer implementing CALayerDelegate actionForLayer:forKey: in + order to implicitly animate 'path' or 'shadowPath' on sublayers. + */ +@interface MDCShadowLayer (Subclassing) + +/** + Override point. + Called by the shadow layer before the instance lays out its sublayers. + */ +- (void)prepareShadowPath; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m new file mode 100644 index 00000000..19de7d65 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m @@ -0,0 +1,471 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadowLayer.h" + +static const CGFloat kShadowElevationDialog = 24.0; +static const float kKeyShadowOpacity = (float)0.26; +static const float kAmbientShadowOpacity = (float)0.08; + +@interface MDCPendingAnimation : NSObject +@property(nonatomic, weak) CALayer *animationSourceLayer; +@property(nonatomic, strong) NSString *keyPath; +@property(nonatomic, strong) id fromValue; +@property(nonatomic, strong) id toValue; +@end + +@implementation MDCShadowMetrics + ++ (MDCShadowMetrics *)metricsWithElevation:(CGFloat)elevation { + if (0.0 < elevation) { + return [[MDCShadowMetrics alloc] initWithElevation:elevation]; + } else { + return [MDCShadowMetrics emptyShadowMetrics]; + } +} + +- (MDCShadowMetrics *)initWithElevation:(CGFloat)elevation { + self = [super init]; + if (self) { + _topShadowRadius = [MDCShadowMetrics ambientShadowBlur:elevation]; + _topShadowOffset = CGSizeMake(0.0, 0.0); + _topShadowOpacity = kAmbientShadowOpacity; + _bottomShadowRadius = [MDCShadowMetrics keyShadowBlur:elevation]; + _bottomShadowOffset = CGSizeMake(0.0, [MDCShadowMetrics keyShadowYOff:elevation]); + _bottomShadowOpacity = kKeyShadowOpacity; + } + return self; +} + ++ (MDCShadowMetrics *)emptyShadowMetrics { + static MDCShadowMetrics *emptyShadowMetrics; + static dispatch_once_t once; + dispatch_once(&once, ^{ + emptyShadowMetrics = [[MDCShadowMetrics alloc] init]; + emptyShadowMetrics->_topShadowRadius = (CGFloat)0.0; + emptyShadowMetrics->_topShadowOffset = CGSizeMake(0.0, 0.0); + emptyShadowMetrics->_topShadowOpacity = 0; + emptyShadowMetrics->_bottomShadowRadius = (CGFloat)0.0; + emptyShadowMetrics->_bottomShadowOffset = CGSizeMake(0.0, 0.0); + emptyShadowMetrics->_bottomShadowOpacity = 0; + }); + + return emptyShadowMetrics; +} + ++ (CGFloat)ambientShadowBlur:(CGFloat)points { + CGFloat blur = (CGFloat)0.889544 * points - (CGFloat)0.003701; + return blur; +} + ++ (CGFloat)keyShadowBlur:(CGFloat)points { + CGFloat blur = (CGFloat)0.666920 * points - (CGFloat)0.001648; + return blur; +} + ++ (CGFloat)keyShadowYOff:(CGFloat)points { + CGFloat yOff = (CGFloat)1.23118 * points - (CGFloat)0.03933; + return yOff; +} + +@end + +@interface MDCShadowLayer () + +@property(nonatomic, strong) CAShapeLayer *topShadow; +@property(nonatomic, strong) CAShapeLayer *bottomShadow; +@property(nonatomic, strong) CAShapeLayer *topShadowMask; +@property(nonatomic, strong) CAShapeLayer *bottomShadowMask; + +@end + +@implementation MDCShadowLayer { + BOOL _shadowPathIsInvalid; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _elevation = 0; + _shadowMaskEnabled = YES; + _shadowPathIsInvalid = YES; + + [self commonMDCShadowLayerInit]; + } + return self; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCShadowLayerInit]; + } + return self; +} + +- (instancetype)initWithLayer:(id)layer { + if (self = [super initWithLayer:layer]) { + if ([layer isKindOfClass:[MDCShadowLayer class]]) { + MDCShadowLayer *otherLayer = (MDCShadowLayer *)layer; + _elevation = otherLayer.elevation; + _shadowMaskEnabled = otherLayer.isShadowMaskEnabled; + _bottomShadow = [[CAShapeLayer alloc] initWithLayer:otherLayer.bottomShadow]; + _topShadow = [[CAShapeLayer alloc] initWithLayer:otherLayer.topShadow]; + _topShadowMask = [[CAShapeLayer alloc] initWithLayer:otherLayer.topShadowMask]; + _bottomShadowMask = [[CAShapeLayer alloc] initWithLayer:otherLayer.bottomShadowMask]; + [self commonMDCShadowLayerInit]; + } + } + return self; +} + +/** + commonMDCShadowLayerInit creates additional layers based on the values of _elevation and + _shadowMaskEnabled. + */ +- (void)commonMDCShadowLayerInit { + if (!_bottomShadow) { + _bottomShadow = [CAShapeLayer layer]; + _bottomShadow.backgroundColor = [UIColor clearColor].CGColor; + _bottomShadow.shadowColor = [UIColor blackColor].CGColor; + _bottomShadow.delegate = self; + [self addSublayer:_bottomShadow]; + } + + if (!_topShadow) { + _topShadow = [CAShapeLayer layer]; + _topShadow.backgroundColor = [UIColor clearColor].CGColor; + _topShadow.shadowColor = [UIColor blackColor].CGColor; + _topShadow.delegate = self; + [self addSublayer:_topShadow]; + } + + // Setup shadow layer state based off _elevation and _shadowMaskEnabled + MDCShadowMetrics *shadowMetrics = [MDCShadowMetrics metricsWithElevation:_elevation]; + _topShadow.shadowOffset = shadowMetrics.topShadowOffset; + _topShadow.shadowRadius = shadowMetrics.topShadowRadius; + _topShadow.shadowOpacity = shadowMetrics.topShadowOpacity; + _bottomShadow.shadowOffset = shadowMetrics.bottomShadowOffset; + _bottomShadow.shadowRadius = shadowMetrics.bottomShadowRadius; + _bottomShadow.shadowOpacity = shadowMetrics.bottomShadowOpacity; + + if (!_topShadowMask) { + _topShadowMask = [CAShapeLayer layer]; + _topShadowMask.delegate = self; + } + if (!_bottomShadowMask) { + _bottomShadowMask = [CAShapeLayer layer]; + _bottomShadowMask.delegate = self; + } + + // TODO(#1021): We shouldn't be calling property accessors in an init method. + if (_shadowMaskEnabled) { + [self configureShadowLayerMaskForLayer:_topShadowMask]; + [self configureShadowLayerMaskForLayer:_bottomShadowMask]; + _topShadow.mask = _topShadowMask; + _bottomShadow.mask = _bottomShadowMask; + } +} + +- (void)layoutSublayers { + [super layoutSublayers]; + + [self prepareShadowPath]; + [self commonLayoutSublayers]; +} + +- (void)setBounds:(CGRect)bounds { + BOOL sizeChanged = !CGSizeEqualToSize(self.bounds.size, bounds.size); + [super setBounds:bounds]; + if (sizeChanged) { + _shadowPathIsInvalid = YES; + [self setNeedsLayout]; + } +} + +- (void)prepareShadowPath { + // This method is meant to be overriden by its subclasses. +} + +#pragma mark - CALayer change monitoring. + +/** Returns a shadowPath based on the layer properties. */ +- (UIBezierPath *)defaultShadowPath { + CGFloat cornerRadius = self.cornerRadius; + if (0.0 < cornerRadius) { + return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; + } + return [UIBezierPath bezierPathWithRect:self.bounds]; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + super.cornerRadius = cornerRadius; + + _topShadow.cornerRadius = cornerRadius; + _bottomShadow.cornerRadius = cornerRadius; + if (_shadowMaskEnabled) { + [self configureShadowLayerMaskForLayer:_topShadowMask]; + [self configureShadowLayerMaskForLayer:_bottomShadowMask]; + _topShadow.mask = _topShadowMask; + _bottomShadow.mask = _bottomShadowMask; + } +} + +- (void)setShadowPath:(CGPathRef)shadowPath { + super.shadowPath = shadowPath; + _topShadow.shadowPath = shadowPath; + _bottomShadow.shadowPath = shadowPath; + if (_shadowMaskEnabled) { + [self configureShadowLayerMaskForLayer:_topShadowMask]; + [self configureShadowLayerMaskForLayer:_bottomShadowMask]; + } +} + +- (void)setShadowColor:(CGColorRef)shadowColor { + super.shadowColor = shadowColor; + _topShadow.shadowColor = shadowColor; + _bottomShadow.shadowColor = shadowColor; +} + +#pragma mark - shouldRasterize forwarding + +- (void)setShouldRasterize:(BOOL)shouldRasterize { + [super setShouldRasterize:shouldRasterize]; + _topShadow.shouldRasterize = shouldRasterize; + _bottomShadow.shouldRasterize = shouldRasterize; +} + +#pragma mark - Shadow Spread + +// Returns how far aware the shadow is spread from the edge of the layer. ++ (CGSize)shadowSpreadForElevation:(CGFloat)elevation { + MDCShadowMetrics *metrics = [MDCShadowMetrics metricsWithElevation:elevation]; + + CGSize shadowSpread = CGSizeZero; + shadowSpread.width = MAX(metrics.topShadowRadius, metrics.bottomShadowRadius) + + MAX(metrics.topShadowOffset.width, metrics.bottomShadowOffset.width); + shadowSpread.height = MAX(metrics.topShadowRadius, metrics.bottomShadowRadius) + + MAX(metrics.topShadowOffset.height, metrics.bottomShadowOffset.height); + + return shadowSpread; +} + +#pragma mark - Pseudo Shadow Masks + +- (void)setShadowMaskEnabled:(BOOL)shadowMaskEnabled { + _shadowMaskEnabled = shadowMaskEnabled; + if (_shadowMaskEnabled) { + [self configureShadowLayerMaskForLayer:_topShadowMask]; + [self configureShadowLayerMaskForLayer:_bottomShadowMask]; + _topShadow.mask = _topShadowMask; + _bottomShadow.mask = _bottomShadowMask; + } else { + _topShadow.mask = nil; + _bottomShadow.mask = nil; + } +} + +// Creates a layer mask that has a hole cut inside so that the original contents +// of the view is no obscured by the shadow the top/bottom pseudo shadow layers +// cast. +- (void)configureShadowLayerMaskForLayer:(CAShapeLayer *)maskLayer { + UIBezierPath *path = [self outerMaskPath]; + UIBezierPath *innerPath = nil; + if (self.shadowPath != nil) { + innerPath = [UIBezierPath bezierPathWithCGPath:(_Nonnull CGPathRef)self.shadowPath]; + } else if (self.cornerRadius > 0) { + innerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.cornerRadius]; + } else { + innerPath = [UIBezierPath bezierPathWithRect:self.bounds]; + } + [path appendPath:innerPath]; + [path setUsesEvenOddFillRule:YES]; + + maskLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + maskLayer.bounds = [self maskRect]; + maskLayer.path = path.CGPath; + maskLayer.fillRule = kCAFillRuleEvenOdd; + maskLayer.fillColor = [UIColor blackColor].CGColor; +} + +- (CGRect)maskRect { + CGSize shadowSpread = [MDCShadowLayer shadowSpreadForElevation:kShadowElevationDialog]; + CGRect bounds = self.bounds; + return CGRectInset(bounds, -shadowSpread.width * 2, -shadowSpread.height * 2); +} + +- (UIBezierPath *)outerMaskPath { + return [UIBezierPath bezierPathWithRect:[self maskRect]]; +} + +- (void)setElevation:(CGFloat)elevation { + _elevation = elevation; + + MDCShadowMetrics *shadowMetrics = [MDCShadowMetrics metricsWithElevation:elevation]; + + _topShadow.shadowOffset = shadowMetrics.topShadowOffset; + _topShadow.shadowRadius = shadowMetrics.topShadowRadius; + _topShadow.shadowOpacity = shadowMetrics.topShadowOpacity; + _bottomShadow.shadowOffset = shadowMetrics.bottomShadowOffset; + _bottomShadow.shadowRadius = shadowMetrics.bottomShadowRadius; + _bottomShadow.shadowOpacity = shadowMetrics.bottomShadowOpacity; +} + +#pragma mark - CALayerDelegate + +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { + if ([event isEqualToString:@"path"] || [event isEqualToString:@"shadowPath"]) { + // We have to create a pending animation because if we are inside a UIKit animation block we + // won't know any properties of the animation block until it is commited. + MDCPendingAnimation *pendingAnim = [[MDCPendingAnimation alloc] init]; + pendingAnim.animationSourceLayer = self; + pendingAnim.fromValue = [layer.presentationLayer valueForKey:event]; + pendingAnim.toValue = nil; + pendingAnim.keyPath = event; + + return pendingAnim; + } + return nil; +} + +#pragma mark - Private + +- (void)commonLayoutSublayers { + CGRect bounds = self.bounds; + + _bottomShadow.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + _bottomShadow.bounds = bounds; + _topShadow.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + _topShadow.bounds = bounds; + + if (_shadowMaskEnabled) { + [self configureShadowLayerMaskForLayer:_topShadowMask]; + [self configureShadowLayerMaskForLayer:_bottomShadowMask]; + } + // Enforce shadowPaths because otherwise no shadows can be drawn. If a shadowPath + // is already set, use that, otherwise fallback to just a regular rect because path. + if (!_bottomShadow.shadowPath || _shadowPathIsInvalid) { + if (self.shadowPath) { + _bottomShadow.shadowPath = self.shadowPath; + } else { + _bottomShadow.shadowPath = [self defaultShadowPath].CGPath; + } + } + if (!_topShadow.shadowPath || _shadowPathIsInvalid) { + if (self.shadowPath) { + _topShadow.shadowPath = self.shadowPath; + } else { + _topShadow.shadowPath = [self defaultShadowPath].CGPath; + } + } + _shadowPathIsInvalid = NO; +} + +- (void)animateCornerRadius:(CGFloat)cornerRadius + withTimingFunction:(CAMediaTimingFunction *)timingFunction + duration:(NSTimeInterval)duration { + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + CGFloat currentCornerRadius = (self.cornerRadius <= 0) ? (CGFloat)0.001 : self.cornerRadius; + CGFloat newCornerRadius = (cornerRadius <= 0) ? (CGFloat)0.001 : cornerRadius; + // Create the paths + UIBezierPath *currentLayerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds + cornerRadius:currentCornerRadius]; + UIBezierPath *newLayerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds + cornerRadius:newCornerRadius]; + + UIBezierPath *currentMaskPath = [self outerMaskPath]; + [currentMaskPath appendPath:currentLayerPath]; + currentMaskPath.usesEvenOddFillRule = YES; + + UIBezierPath *newMaskPath = [self outerMaskPath]; + [newMaskPath appendPath:newLayerPath]; + newMaskPath.usesEvenOddFillRule = YES; + + // Animate the top layers + NSString *shadowPathKey = @"shadowPath"; + CABasicAnimation *topLayerAnimation = [CABasicAnimation animationWithKeyPath:shadowPathKey]; + topLayerAnimation.fromValue = (__bridge id)currentLayerPath.CGPath; + topLayerAnimation.toValue = (__bridge id)newLayerPath.CGPath; + topLayerAnimation.duration = duration; + topLayerAnimation.timingFunction = timingFunction; + self.topShadow.shadowPath = newLayerPath.CGPath; + [self.topShadow addAnimation:topLayerAnimation forKey:shadowPathKey]; + CABasicAnimation *bottomLayerAnimation = [CABasicAnimation animationWithKeyPath:shadowPathKey]; + bottomLayerAnimation.fromValue = (__bridge id)currentLayerPath.CGPath; + bottomLayerAnimation.toValue = (__bridge id)newLayerPath.CGPath; + bottomLayerAnimation.duration = duration; + bottomLayerAnimation.timingFunction = timingFunction; + self.bottomShadow.shadowPath = newLayerPath.CGPath; + [self.bottomShadow addAnimation:bottomLayerAnimation forKey:shadowPathKey]; + + // Animate the masks + if (self.shadowMaskEnabled) { + NSString *pathKey = @"path"; + CABasicAnimation *topMaskLayerAnimation = [CABasicAnimation animationWithKeyPath:pathKey]; + topMaskLayerAnimation.fromValue = (__bridge id)currentMaskPath.CGPath; + topMaskLayerAnimation.toValue = (__bridge id)newMaskPath.CGPath; + topMaskLayerAnimation.duration = duration; + topMaskLayerAnimation.timingFunction = timingFunction; + self.topShadowMask.path = newMaskPath.CGPath; + [self.topShadowMask addAnimation:topMaskLayerAnimation forKey:pathKey]; + CABasicAnimation *bottomMaskLayerAnimation = [CABasicAnimation animationWithKeyPath:pathKey]; + bottomMaskLayerAnimation.fromValue = (__bridge id)currentMaskPath.CGPath; + bottomMaskLayerAnimation.toValue = (__bridge id)newMaskPath.CGPath; + bottomMaskLayerAnimation.duration = duration; + bottomMaskLayerAnimation.timingFunction = timingFunction; + self.bottomShadowMask.path = newMaskPath.CGPath; + [self.bottomShadowMask addAnimation:bottomMaskLayerAnimation forKey:pathKey]; + } + + // Animate the corner radius + CABasicAnimation *cornerRadiusAnimation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"]; + cornerRadiusAnimation.fromValue = @((CGFloat)currentCornerRadius); + cornerRadiusAnimation.toValue = @((CGFloat)newCornerRadius); + cornerRadiusAnimation.duration = duration; + cornerRadiusAnimation.timingFunction = timingFunction; + self.cornerRadius = cornerRadius; + [self addAnimation:cornerRadiusAnimation forKey:@"cornerRadius"]; + [CATransaction commit]; +} + +@end + +@implementation MDCPendingAnimation + +- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict { + if ([anObject isKindOfClass:[CAShapeLayer class]]) { + CAShapeLayer *layer = (CAShapeLayer *)anObject; + + // In order to synchronize our animation with UIKit animations we have to fetch the resizing + // animation created by UIKit and copy the configuration to our custom animation. + CAAnimation *boundsAction = [self.animationSourceLayer animationForKey:@"bounds.size"]; + if (!boundsAction) { + // Headless layers will animate bounds directly instead of decomposing + // bounds.size/bounds.position. A headless layer is a CALayer without a delegate (usually + // would be a UIView). + boundsAction = [self.animationSourceLayer animationForKey:@"bounds"]; + } + if ([boundsAction isKindOfClass:[CABasicAnimation class]]) { + CABasicAnimation *animation = (CABasicAnimation *)[boundsAction copy]; + animation.keyPath = self.keyPath; + animation.fromValue = self.fromValue; + animation.toValue = self.toValue; + + [layer addAnimation:animation forKey:event]; + } + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h new file mode 100644 index 00000000..a03c2166 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h @@ -0,0 +1,15 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShadowLayer.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h new file mode 100644 index 00000000..9a2a094c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h @@ -0,0 +1,80 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MaterialShapes.h" + +#import "MDCCurvedCornerTreatment.h" +#import "MDCCutCornerTreatment.h" +#import "MDCRoundedCornerTreatment.h" + +@interface MDCCornerTreatment (CornerTypeInitalizer) + +/** + Initialize and return an MDCCornerTreatment as an MDCRoundedCornerTreatment. + + @param value The radius to set the rounded corner to. + @return an MDCRoundedCornerTreatment. + */ ++ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value; + +/** + Initialize and return an MDCCornerTreatment as an MDCRoundedCornerTreatment. + + @param value The radius to set the rounded corner to. + @param valueType The value type in which the value is set as. It can be sent either as an + absolute value, or a percentage value (0.0 - 1.0) of the height of the surface. + @return an MDCRoundedCornerTreatment. + */ ++ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value + valueType:(MDCCornerTreatmentValueType)valueType; + +/** + Initialize and return an MDCCornerTreatment as an MDCCutCornerTreatment. + + @param value The cut to set the cut corner to. + @return an MDCCutCornerTreatment. + */ ++ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value; + +/** + Initialize and return an MDCCornerTreatment as an MDCRoundedCornerTreatment. + + @param value The cut to set the cut corner to. + @param valueType The value type in which the value is set as. It can be sent either as an + absolute value, or a percentage value (0.0 - 1.0) of the height of the surface. + @return an MDCCutCornerTreatment. + */ ++ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value + valueType:(MDCCornerTreatmentValueType)valueType; + +/** + Initialize and return an MDCCornerTreatment as an MDCCurvedCornerTreatment. + + @param value The size to set the curved corner to. + @return an MDCCurvedCornerTreatment. + */ ++ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value; + +/** + Initialize and return an MDCCornerTreatment as an MDCCurvedCornerTreatment. + + @param value The curve to set the curved corner to. + @param valueType The value type in which the value is set as. It can be sent either as an + absolute value, or a percentage value (0.0 - 1.0) of the height of the surface. + @return an MDCCurvedCornerTreatment. + */ ++ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value + valueType:(MDCCornerTreatmentValueType)valueType; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m new file mode 100644 index 00000000..a9664576 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m @@ -0,0 +1,60 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCornerTreatment+CornerTypeInitalizer.h" + +#import "MDCCurvedCornerTreatment.h" + +#import "MDCCutCornerTreatment.h" + +#import "MDCRoundedCornerTreatment.h" + +@implementation MDCCornerTreatment (CornerTypeInitalizer) + ++ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value { + return [[MDCRoundedCornerTreatment alloc] initWithRadius:value]; +} + ++ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value + valueType:(MDCCornerTreatmentValueType)valueType { + MDCRoundedCornerTreatment *roundedCornerTreatment = + [MDCRoundedCornerTreatment cornerWithRadius:value]; + roundedCornerTreatment.valueType = valueType; + return roundedCornerTreatment; +} + ++ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value { + return [[MDCCutCornerTreatment alloc] initWithCut:value]; +} + ++ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value + valueType:(MDCCornerTreatmentValueType)valueType { + MDCCutCornerTreatment *cutCornerTreatment = [MDCCutCornerTreatment cornerWithCut:value]; + cutCornerTreatment.valueType = valueType; + return cutCornerTreatment; +} + ++ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value { + return [[MDCCurvedCornerTreatment alloc] initWithSize:value]; +} + ++ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value + valueType:(MDCCornerTreatmentValueType)valueType { + MDCCurvedCornerTreatment *curvedCornerTreatment = + [MDCCurvedCornerTreatment cornerWithCurve:value]; + curvedCornerTreatment.valueType = valueType; + return curvedCornerTreatment; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h new file mode 100644 index 00000000..c6b02073 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h @@ -0,0 +1,40 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialShapes.h" + +/** + A curved corner treatment. Distinct from MDCRoundedCornerTreatment in that MDCurvedCornerTreatment + also supports asymmetric curved corners. + */ +@interface MDCCurvedCornerTreatment : MDCCornerTreatment + +/** + The size of the curve. + */ +@property(nonatomic, assign) CGSize size; + +/** + Initializes an MDCCurvedCornerTreatment instance with a given corner size. + */ +- (nonnull instancetype)initWithSize:(CGSize)size NS_DESIGNATED_INITIALIZER; + +/** + Initializes an MDCCurvedCornerTreatment instance with a corner size of zero. + */ +- (nonnull instancetype)init; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m new file mode 100644 index 00000000..363d248c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m @@ -0,0 +1,72 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCurvedCornerTreatment.h" + +#import "MaterialShapes.h" + +@implementation MDCCurvedCornerTreatment + +- (instancetype)init { + return [self initWithSize:CGSizeZero]; +} + +- (instancetype)initWithSize:(CGSize)size { + if (self = [super init]) { + _size = size; + } + return self; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle { + return [self pathGeneratorForCornerWithAngle:angle andCurve:_size]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle forViewSize:(CGSize)viewSize { + CGSize normalizedCurve = + CGSizeMake(_size.width * viewSize.height, _size.height * viewSize.height); + return [self pathGeneratorForCornerWithAngle:angle andCurve:normalizedCurve]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle andCurve:(CGSize)curve { + MDCPathGenerator *path = + [MDCPathGenerator pathGeneratorWithStartPoint:CGPointMake(0, curve.height)]; + [path addQuadCurveWithControlPoint:CGPointZero toPoint:CGPointMake(curve.width, 0)]; + return path; +} + +- (id)copyWithZone:(NSZone *)zone { + MDCCurvedCornerTreatment *copy = [super copyWithZone:zone]; + copy.size = _size; + return copy; +} + +- (BOOL)isEqual:(id)object { + if (object == self) { + return YES; + } else if (![super isEqual:object]) { + return NO; + } + if (!object || ![[object class] isEqual:[self class]]) { + return NO; + } + MDCCurvedCornerTreatment *otherCurvedCorner = (MDCCurvedCornerTreatment *)object; + return CGSizeEqualToSize(self.size, otherCurvedCorner.size); +} + +- (NSUInteger)hash { + return @(self.size.height).hash ^ @(self.size.width).hash ^ (NSUInteger)self.valueType; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h new file mode 100644 index 00000000..8fce5cf3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h @@ -0,0 +1,35 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "MaterialShapes.h" + +/** + A curved rectangle shape generator. + */ +@interface MDCCurvedRectShapeGenerator : NSObject + +/** + The size of the curved corner. + */ +@property(nonatomic, assign) CGSize cornerSize; + +/** + Initializes an MDCCurvedRectShapeGenerator instance with a given cornerSize. + */ +- (instancetype)initWithCornerSize:(CGSize)cornerSize NS_DESIGNATED_INITIALIZER; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m new file mode 100644 index 00000000..ea2df710 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m @@ -0,0 +1,68 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCurvedRectShapeGenerator.h" + +#import "MDCCurvedCornerTreatment.h" +#import "MaterialShapes.h" + +@implementation MDCCurvedRectShapeGenerator { + MDCRectangleShapeGenerator *_rectGenerator; + MDCCurvedCornerTreatment *_widthHeightCorner; + MDCCurvedCornerTreatment *_heightWidthCorner; +} + +- (instancetype)init { + return [self initWithCornerSize:CGSizeMake(0, 0)]; +} + +- (instancetype)initWithCornerSize:(CGSize)cornerSize { + if (self = [super init]) { + [self commonInit]; + + self.cornerSize = cornerSize; + } + return self; +} + +- (void)commonInit { + _rectGenerator = [[MDCRectangleShapeGenerator alloc] init]; + + _widthHeightCorner = [[MDCCurvedCornerTreatment alloc] init]; + _heightWidthCorner = [[MDCCurvedCornerTreatment alloc] init]; + + _rectGenerator.topLeftCorner = _widthHeightCorner; + _rectGenerator.topRightCorner = _heightWidthCorner; + _rectGenerator.bottomRightCorner = _widthHeightCorner; + _rectGenerator.bottomLeftCorner = _heightWidthCorner; +} + +- (void)setCornerSize:(CGSize)cornerSize { + _cornerSize = cornerSize; + + _widthHeightCorner.size = _cornerSize; + _heightWidthCorner.size = CGSizeMake(cornerSize.height, cornerSize.width); +} + +- (id)copyWithZone:(nullable NSZone *)__unused zone { + MDCCurvedRectShapeGenerator *copy = [[[self class] alloc] init]; + copy.cornerSize = self.cornerSize; + return copy; +} + +- (CGPathRef)pathForSize:(CGSize)size { + return [_rectGenerator pathForSize:size]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h new file mode 100644 index 00000000..dcacfcb6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h @@ -0,0 +1,57 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialShapes.h" + +/** + A cut corner treatment subclassing MDCCornerTreatment. + This can be used to set corners in MDCRectangleShapeGenerator. + */ +@interface MDCCutCornerTreatment : MDCCornerTreatment + +/** + The cut of the corner. + + The value of the cut defines by how many UI points starting from the edge of the corner and going + equal distance on the X axis and the Y axis will the corner be cut. + + As an example if the shape is a square with a size of 100x100, and we have all its corners set + with MDCCutCornerTreatment and a cut value of 50 then the final result will be a diamond with a + size of 50x50. + +--------------+ /\ + | | / \ 50 + | | / \ + | | 100 ---> / \ + | | \ / + | | \ / + | | \ / 50 + +--------------+ \/ + 100 + + */ +@property(nonatomic, assign) CGFloat cut; + +/** + Initializes an MDCCutCornerTreatment instance with a given cut. + */ +- (nonnull instancetype)initWithCut:(CGFloat)cut NS_DESIGNATED_INITIALIZER; + +/** + Initializes an MDCCutCornerTreatment instance with a cut of zero. + */ +- (nonnull instancetype)init; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m new file mode 100644 index 00000000..fbd5d16f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m @@ -0,0 +1,72 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCutCornerTreatment.h" + +#import "MaterialShapes.h" + +static NSString *const MDCCutCornerTreatmentCutKey = @"MDCCutCornerTreatmentCutKey"; + +@implementation MDCCutCornerTreatment + +- (instancetype)init { + return [self initWithCut:0]; +} + +- (instancetype)initWithCut:(CGFloat)cut { + if (self = [super init]) { + _cut = cut; + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + MDCCutCornerTreatment *copy = [super copyWithZone:zone]; + copy.cut = _cut; + return copy; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle { + return [self pathGeneratorForCornerWithAngle:angle andCut:_cut]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle forViewSize:(CGSize)viewSize { + CGFloat normalizedCut = _cut * viewSize.height; + return [self pathGeneratorForCornerWithAngle:angle andCut:normalizedCut]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle andCut:(CGFloat)cut { + MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointMake(0, cut)]; + [path addLineToPoint:CGPointMake(cut, 0)]; + return path; +} + +- (BOOL)isEqual:(id)object { + if (object == self) { + return YES; + } else if (![super isEqual:object]) { + return NO; + } + if (!object || ![[object class] isEqual:[self class]]) { + return NO; + } + MDCCutCornerTreatment *otherCutCorner = (MDCCutCornerTreatment *)object; + return self.cut == otherCutCorner.cut; +} + +- (NSUInteger)hash { + return @(self.cut).hash ^ (NSUInteger)self.valueType; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h new file mode 100644 index 00000000..3aa0fefd --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h @@ -0,0 +1,24 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialShapes.h" + +/** + A pill shape generator. Rounds the corners such that the shorter sides of the generated shape are + entirely rounded. + */ +@interface MDCPillShapeGenerator : NSObject +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m new file mode 100644 index 00000000..dbcaceaf --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m @@ -0,0 +1,52 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCPillShapeGenerator.h" + +#import "MDCRoundedCornerTreatment.h" +#import "MaterialShapes.h" + +@implementation MDCPillShapeGenerator { + MDCRectangleShapeGenerator *_rectangleGenerator; + MDCRoundedCornerTreatment *_cornerShape; +} + +- (instancetype)init { + if (self = [super init]) { + [self commonInit]; + } + return self; +} + +- (id)copyWithZone:(NSZone *)__unused zone { + return [[[self class] alloc] init]; +} + +- (void)commonInit { + _cornerShape = [[MDCRoundedCornerTreatment alloc] init]; + _rectangleGenerator = [[MDCRectangleShapeGenerator alloc] init]; + [_rectangleGenerator setCorners:_cornerShape]; +} + +- (CGPathRef)pathForSize:(CGSize)size { + CGFloat radius = (CGFloat)0.5 * MIN(fabs(size.width), fabs(size.height)); + if (radius > 0) { + [_rectangleGenerator setCorners:[[MDCRoundedCornerTreatment alloc] initWithRadius:radius]]; + } + return [_rectangleGenerator pathForSize:size]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h new file mode 100644 index 00000000..55cf6c7b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h @@ -0,0 +1,38 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#import + +#import "MaterialShapes.h" + +/** + A rounded corner treatment. + */ +@interface MDCRoundedCornerTreatment : MDCCornerTreatment + +/** + The radius of the corner. + */ +@property(nonatomic, assign) CGFloat radius; + +/** + Initializes an MDCRoundedCornerTreatment instance with a given radius. + */ +- (nonnull instancetype)initWithRadius:(CGFloat)radius NS_DESIGNATED_INITIALIZER; + +/** + Initializes an MDCRoundedCornerTreatment instance with a radius of zero. + */ +- (nonnull instancetype)init; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m new file mode 100644 index 00000000..e6390806 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m @@ -0,0 +1,72 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRoundedCornerTreatment.h" + +#import "MaterialShapes.h" + +@implementation MDCRoundedCornerTreatment + +- (instancetype)init { + return [self initWithRadius:0]; +} + +- (instancetype)initWithRadius:(CGFloat)radius { + if (self = [super init]) { + _radius = radius; + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + MDCRoundedCornerTreatment *copy = [super copyWithZone:zone]; + copy.radius = _radius; + return copy; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle { + return [self pathGeneratorForCornerWithAngle:angle andRadius:_radius]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle forViewSize:(CGSize)viewSize { + CGFloat normalizedRadius = _radius * viewSize.height; + return [self pathGeneratorForCornerWithAngle:angle andRadius:normalizedRadius]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle andRadius:(CGFloat)radius { + MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointMake(0, radius)]; + [path addArcWithTangentPoint:CGPointZero + toPoint:CGPointMake(sin(angle) * radius, cos(angle) * radius) + radius:radius]; + return path; +} + +- (BOOL)isEqual:(id)object { + if (object == self) { + return YES; + } else if (![super isEqual:object]) { + return NO; + } + if (!object || ![[object class] isEqual:[self class]]) { + return NO; + } + MDCRoundedCornerTreatment *otherRoundedCorner = (MDCRoundedCornerTreatment *)object; + return self.radius == otherRoundedCorner.radius; +} + +- (NSUInteger)hash { + return @(self.radius).hash ^ (NSUInteger)self.valueType; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h new file mode 100644 index 00000000..7230e3d8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h @@ -0,0 +1,31 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#import +#import + +#import "MaterialShapes.h" + +/** + A slanted rectangle shape generator. + + Creates rectangles with the vertical edges at a slant. + */ +@interface MDCSlantedRectShapeGenerator : NSObject + +/** + The horizontal offset of the corners. + */ +@property(nonatomic, assign) CGFloat slant; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m new file mode 100644 index 00000000..2dbca16b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m @@ -0,0 +1,53 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCSlantedRectShapeGenerator.h" + +#import "MaterialShapes.h" + +@implementation MDCSlantedRectShapeGenerator { + MDCRectangleShapeGenerator *_rectangleGenerator; +} + +- (instancetype)init { + if (self = [super init]) { + [self commonMDCSlantedRectShapeGeneratorInit]; + } + return self; +} + +- (void)commonMDCSlantedRectShapeGeneratorInit { + _rectangleGenerator = [[MDCRectangleShapeGenerator alloc] init]; +} + +- (id)copyWithZone:(NSZone *)__unused zone { + MDCSlantedRectShapeGenerator *copy = [[[self class] alloc] init]; + copy.slant = self.slant; + return copy; +} + +- (void)setSlant:(CGFloat)slant { + _slant = slant; + + _rectangleGenerator.topLeftCornerOffset = CGPointMake(slant, 0); + _rectangleGenerator.topRightCornerOffset = CGPointMake(slant, 0); + _rectangleGenerator.bottomLeftCornerOffset = CGPointMake(-slant, 0); + _rectangleGenerator.bottomRightCornerOffset = CGPointMake(-slant, 0); +} + +- (CGPathRef)pathForSize:(CGSize)size { + return [_rectangleGenerator pathForSize:size]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h new file mode 100644 index 00000000..1e4a0d1e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h @@ -0,0 +1,47 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialShapes.h" + +typedef enum : NSUInteger { + MDCTriangleEdgeStyleHandle, + MDCTriangleEdgeStyleCut, +} MDCTriangleEdgeStyle; + +/** + An edge treatment that adds a triangle-shaped cut or handle to the edge. + */ +@interface MDCTriangleEdgeTreatment : MDCEdgeTreatment + +/** + The size of the triangle shape. + */ +@property(nonatomic, assign) CGFloat size; + +/** + The style of the triangle shape. + */ +@property(nonatomic, assign) MDCTriangleEdgeStyle style; + +/** + Initializes an MDCTriangleEdgeTreatment with a given size and style. + */ +- (nonnull instancetype)initWithSize:(CGFloat)size + style:(MDCTriangleEdgeStyle)style NS_DESIGNATED_INITIALIZER; + +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m new file mode 100644 index 00000000..08123125 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m @@ -0,0 +1,43 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCTriangleEdgeTreatment.h" + +#import "MaterialShapes.h" + +@implementation MDCTriangleEdgeTreatment + +- (instancetype)initWithSize:(CGFloat)size style:(MDCTriangleEdgeStyle)style { + if (self = [super init]) { + _size = size; + _style = style; + } + return self; +} + +- (MDCPathGenerator *)pathGeneratorForEdgeWithLength:(CGFloat)length { + BOOL isCut = (self.style == MDCTriangleEdgeStyleCut); + MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; + [path addLineToPoint:CGPointMake(length / 2 - _size, 0)]; + [path addLineToPoint:CGPointMake(length / 2, isCut ? _size : -_size)]; + [path addLineToPoint:CGPointMake(length / 2 + _size, 0)]; + [path addLineToPoint:CGPointMake(length, 0)]; + return path; +} + +- (id)copyWithZone:(NSZone *)__unused zone { + return [[[self class] alloc] initWithSize:_size style:_style]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h new file mode 100644 index 00000000..1dc2f630 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h @@ -0,0 +1,22 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCornerTreatment+CornerTypeInitalizer.h" +#import "MDCCurvedCornerTreatment.h" +#import "MDCCurvedRectShapeGenerator.h" +#import "MDCCutCornerTreatment.h" +#import "MDCPillShapeGenerator.h" +#import "MDCRoundedCornerTreatment.h" +#import "MDCSlantedRectShapeGenerator.h" +#import "MDCTriangleEdgeTreatment.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h new file mode 100644 index 00000000..ea5cc277 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h @@ -0,0 +1,73 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCPathGenerator; + +/** + This enum consists of the different types of shape values that can be provided. + + - MDCCornerTreatmentValueTypeAbsolute: If an absolute corner value is provided. + - MDCCornerTreatmentValueTypePercentage: If a relative corner value is provided. + + See MDCShapeCorner's @c size property for additional details. + */ +typedef NS_ENUM(NSInteger, MDCCornerTreatmentValueType) { + MDCCornerTreatmentValueTypeAbsolute, + MDCCornerTreatmentValueTypePercentage, +}; + +/** + MDCCornerTreatment is a factory for creating MDCPathGenerators that represent + the path of a corner. + + MDCCornerTreatments should only generate corners in the top-left quadrant (i.e. + the top-left corner of a rectangle). MDCShapeModel will translate the generated + MDCPathGenerator to the expected position and rotation. + */ +@interface MDCCornerTreatment : NSObject + +/** + The value type of our corner treatment. + + When MDCCornerTreatmentValueType is MDCCornerTreatmentValueTypeAbsolute, then the accepted corner + values are an absolute size. + When MDCShapeSizeType is MDCCornerTreatmentValueTypePercentage, values are expected to be in the + range of 0 to 1 (0% - 100%). These values are percentages based on the height of the surface. + */ +@property(assign, nonatomic) MDCCornerTreatmentValueType valueType; + +- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER; + +/** + Creates an MDCPathGenerator object for a corner with the provided angle. + + @param angle The internal angle of the corner in radians. Typically M_PI/2. + */ +- (nonnull MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle; + +/** + Creates an MDCPathGenerator object for a corner with the provided angle. + Given that the provided valueType is MDCCornerTreatmentValueTypePercentage, we also need + the size of the view to calculate the corner size percentage relative to the view height. + + @param angle the internal angle of the corner in radius. Typically M_PI/2. + @param size the size of the view. + @return returns an MDCPathGenerator. + */ +- (nonnull MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle + forViewSize:(CGSize)size; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m new file mode 100644 index 00000000..bc8934aa --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m @@ -0,0 +1,56 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCornerTreatment.h" + +#import "MDCPathGenerator.h" + +@implementation MDCCornerTreatment + +- (instancetype)init { + _valueType = MDCCornerTreatmentValueTypeAbsolute; + return [super init]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)__unused angle { + return [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; +} + +- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)__unused angle + forViewSize:(CGSize)__unused viewSize { + return [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; +} + +- (id)copyWithZone:(nullable NSZone *)__unused zone { + MDCCornerTreatment *copy = [[[self class] alloc] init]; + copy.valueType = _valueType; + return copy; +} + +- (BOOL)isEqual:(id)object { + if (object == self) { + return YES; + } + if (!object || ![[object class] isEqual:[self class]]) { + return NO; + } + MDCCornerTreatment *otherCorner = (MDCCornerTreatment *)object; + return self.valueType == otherCorner.valueType; +} + +- (NSUInteger)hash { + return (NSUInteger)self.valueType; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h new file mode 100644 index 00000000..be646ff0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h @@ -0,0 +1,38 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class MDCPathGenerator; + +/** + MDCEdgeTreatment is a factory for creating MDCPathGenerators that represent the + path of a edge. + + MDCEdgeTreaments only generate in the top quadrant (i.e. the top edge of a + rectangle). MDCShapeModel will transform the generated MDCPathGenerator to the + expected position and rotation. + */ +@interface MDCEdgeTreatment : NSObject + +- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER; + +/** + Generates an MDCPathGenerator object for an edge with the provided length. + + @param length The length of the edge. + */ +- (nonnull MDCPathGenerator *)pathGeneratorForEdgeWithLength:(CGFloat)length; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m new file mode 100644 index 00000000..26d24f11 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m @@ -0,0 +1,35 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCEdgeTreatment.h" + +#import "MDCPathGenerator.h" + +@implementation MDCEdgeTreatment + +- (instancetype)init { + return [super init]; +} + +- (MDCPathGenerator *)pathGeneratorForEdgeWithLength:(CGFloat)length { + MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; + [path addLineToPoint:CGPointMake(length, 0)]; + return path; +} + +- (id)copyWithZone:(nullable NSZone *)__unused zone { + return [[[self class] alloc] init]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h new file mode 100644 index 00000000..d8f204b9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h @@ -0,0 +1,116 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +/** + MDCPathGenerator is a factory for creating CGPaths. Describe your path with the + lineTo and addArc... methods, then call appendToCGPath to append them to a + CGPath. + + @note MDCPathGenerators always start at (0, 0) and end at @c endPoint. + */ +@interface MDCPathGenerator : NSObject + +/** + The start point of the generated paths. + */ +@property(nonatomic, readonly) CGPoint startPoint; + +/** + The end point of the generated paths. + */ +@property(nonatomic, readonly) CGPoint endPoint; + +/** + Returns an initialized MDCPathGenerator instance with a startPoint of CGPointZero. + */ ++ (nonnull instancetype)pathGenerator; + +/** + Returns an initialized MDCPathGenerator instance. + + @param startPoint The start point of the generated paths. + */ ++ (nonnull instancetype)pathGeneratorWithStartPoint:(CGPoint)startPoint; + +/** + Appends a straight line segment to the path generator. Analogous to + CGPathAddLineToPoint. + + @param point The end point of the line segment. + */ +- (void)addLineToPoint:(CGPoint)point; + +/** + Appends an arc to the path generator, possibly preceded by a straight line + segment. Analogous to CGPathAddArc. + + @param center The center of the arc. + @param radius The radius of the arc. + @param startAngle The start angle of the arc. + @param endAngle The end angle of the arc. + @param clockwise Whether the arc is clockwise (YES) or counterclockwise (NO). + */ +- (void)addArcWithCenter:(CGPoint)center + radius:(CGFloat)radius + startAngle:(CGFloat)startAngle + endAngle:(CGFloat)endAngle + clockwise:(BOOL)clockwise; + +/** + Appends an arc to the path generator, possibly preceded by a straight line + segment. Analogous to CGPathAddArcToPoint. The arc will be drawn inside the + corner created by the start of the arc, tangentPoint and toPoint. + + @param tangentPoint The corner of the arc. + @param toPoint The final point of the arc. + @param radius The radius inside of the arc. + */ +- (void)addArcWithTangentPoint:(CGPoint)tangentPoint + toPoint:(CGPoint)toPoint + radius:(CGFloat)radius; + +/** + Appends a cubic Bézier curve to the path generator. + + @param controlPoint1 The first control point + @param controlPoint2 The second control point + @param toPoint The end of the curve + */ +- (void)addCurveWithControlPoint1:(CGPoint)controlPoint1 + controlPoint2:(CGPoint)controlPoint2 + toPoint:(CGPoint)toPoint; + +/** + Appends a quadratic Bézier curve to the path generator. + + @param controlPoint The control point + @param toPoint The end of the curve + */ +- (void)addQuadCurveWithControlPoint:(CGPoint)controlPoint toPoint:(CGPoint)toPoint; + +/** + Appends the recorded path operations to a CGPath using the provided transform. + + @param cgPath A mutable CGPath to which the saved path operations will be + appended. + @param transform The transform applied ot each path operation before appending + them to the CGPath. + */ +- (void)appendToCGPath:(nonnull CGMutablePathRef)cgPath + transform:(nullable CGAffineTransform *)transform; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m new file mode 100644 index 00000000..095a2ad5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m @@ -0,0 +1,190 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCPathGenerator.h" + + +@interface MDCPathCommand : NSObject +- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform; +@end + +@interface MDCPathLineCommand : MDCPathCommand +@property(nonatomic, assign) CGPoint point; +@end + +@interface MDCPathArcCommand : MDCPathCommand +@property(nonatomic, assign) CGPoint point; +@property(nonatomic, assign) CGFloat radius; +@property(nonatomic, assign) CGFloat startAngle; +@property(nonatomic, assign) CGFloat endAngle; +@property(nonatomic, assign) BOOL clockwise; +@end + +@interface MDCPathArcToCommand : MDCPathCommand +@property(nonatomic, assign) CGPoint start; +@property(nonatomic, assign) CGPoint end; +@property(nonatomic, assign) CGFloat radius; +@end + +@interface MDCPathCurveCommand : MDCPathCommand +@property(nonatomic, assign) CGPoint control1; +@property(nonatomic, assign) CGPoint control2; +@property(nonatomic, assign) CGPoint end; +@end + +@interface MDCPathQuadCurveCommand : MDCPathCommand +@property(nonatomic, assign) CGPoint control; +@property(nonatomic, assign) CGPoint end; +@end + +@implementation MDCPathGenerator { + NSMutableArray *_operations; + CGPoint _startPoint; + CGPoint _endPoint; +} + ++ (nonnull instancetype)pathGenerator { + return [[self alloc] initWithStartPoint:CGPointZero]; +} + ++ (instancetype)pathGeneratorWithStartPoint:(CGPoint)start { + return [[self alloc] initWithStartPoint:start]; +} + +- (instancetype)initWithStartPoint:(CGPoint)start { + if (self = [super init]) { + _operations = [NSMutableArray array]; + + _startPoint = start; + _endPoint = start; + } + return self; +} + +- (void)addLineToPoint:(CGPoint)point { + MDCPathLineCommand *op = [[MDCPathLineCommand alloc] init]; + op.point = point; + [_operations addObject:op]; + + _endPoint = point; +} + +- (void)addArcWithCenter:(CGPoint)center + radius:(CGFloat)radius + startAngle:(CGFloat)startAngle + endAngle:(CGFloat)endAngle + clockwise:(BOOL)clockwise { + MDCPathArcCommand *op = [[MDCPathArcCommand alloc] init]; + op.point = center; + op.radius = radius; + op.startAngle = startAngle; + op.endAngle = endAngle; + op.clockwise = clockwise; + [_operations addObject:op]; + + _endPoint = + CGPointMake(center.x + radius * cos(endAngle), center.y + radius * sin(endAngle)); +} + +- (void)addArcWithTangentPoint:(CGPoint)tangentPoint + toPoint:(CGPoint)toPoint + radius:(CGFloat)radius { + MDCPathArcToCommand *op = [[MDCPathArcToCommand alloc] init]; + op.start = tangentPoint; + op.end = toPoint; + op.radius = radius; + [_operations addObject:op]; + + _endPoint = toPoint; +} + +- (void)addCurveWithControlPoint1:(CGPoint)controlPoint1 + controlPoint2:(CGPoint)controlPoint2 + toPoint:(CGPoint)toPoint { + MDCPathCurveCommand *op = [[MDCPathCurveCommand alloc] init]; + op.control1 = controlPoint1; + op.control2 = controlPoint2; + op.end = toPoint; + [_operations addObject:op]; + + _endPoint = toPoint; +} + +- (void)addQuadCurveWithControlPoint:(CGPoint)controlPoint toPoint:(CGPoint)toPoint { + MDCPathQuadCurveCommand *op = [[MDCPathQuadCurveCommand alloc] init]; + op.control = controlPoint; + op.end = toPoint; + [_operations addObject:op]; + + _endPoint = toPoint; +} + +- (void)appendToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { + for (MDCPathCommand *op in _operations) { + [op applyToCGPath:cgPath transform:transform]; + } +} + +@end + +@implementation MDCPathCommand + +- (void)applyToCGPath:(CGMutablePathRef)__unused cgPath + transform:(CGAffineTransform *)__unused transform { + // no-op +} + +@end + +@implementation MDCPathLineCommand + +- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { + CGPathAddLineToPoint(cgPath, transform, self.point.x, self.point.y); +} + +@end + +@implementation MDCPathArcCommand +- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { + CGPathAddArc(cgPath, transform, self.point.x, self.point.y, self.radius, self.startAngle, + self.endAngle, self.clockwise); +} +@end + +@implementation MDCPathArcToCommand + +- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { + CGPathAddArcToPoint(cgPath, transform, self.start.x, self.start.y, self.end.x, self.end.y, + self.radius); +} + +@end + +@implementation MDCPathCurveCommand + +- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { + CGPathAddCurveToPoint(cgPath, transform, self.control1.x, self.control1.y, self.control2.x, + self.control2.y, self.end.x, self.end.y); +} + +@end + +@implementation MDCPathQuadCurveCommand + +- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { + CGPathAddQuadCurveToPoint(cgPath, transform, self.control.x, self.control.y, self.end.x, + self.end.y); +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h new file mode 100644 index 00000000..100338c6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h @@ -0,0 +1,64 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCShapeGenerating.h" + +@class MDCCornerTreatment; +@class MDCEdgeTreatment; + +/** + An MDCShapeGenerating for creating shaped rectanglular CGPaths. + + By default MDCRectangleShapeGenerator creates rectanglular CGPaths. Set the corner and edge + treatments to shape parts of the generated path. + */ +@interface MDCRectangleShapeGenerator : NSObject + +/** + The corner treatments to apply to each corner. + */ +@property(nonatomic, strong) MDCCornerTreatment *topLeftCorner; +@property(nonatomic, strong) MDCCornerTreatment *topRightCorner; +@property(nonatomic, strong) MDCCornerTreatment *bottomLeftCorner; +@property(nonatomic, strong) MDCCornerTreatment *bottomRightCorner; + +/** + The offsets to apply to each corner. + */ +@property(nonatomic, assign) CGPoint topLeftCornerOffset; +@property(nonatomic, assign) CGPoint topRightCornerOffset; +@property(nonatomic, assign) CGPoint bottomLeftCornerOffset; +@property(nonatomic, assign) CGPoint bottomRightCornerOffset; + +/** + The edge treatments to apply to each edge. + */ +@property(nonatomic, strong) MDCEdgeTreatment *topEdge; +@property(nonatomic, strong) MDCEdgeTreatment *rightEdge; +@property(nonatomic, strong) MDCEdgeTreatment *bottomEdge; +@property(nonatomic, strong) MDCEdgeTreatment *leftEdge; + +/** + Convenience to set all corners to the same MDCCornerTreatment instance. + */ +- (void)setCorners:(MDCCornerTreatment *)cornerShape; + +/** + Conveninece to set all edge treatments to the same MDCEdgeTreatment instance. + */ +- (void)setEdges:(MDCEdgeTreatment *)edgeShape; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m new file mode 100644 index 00000000..41ee45e2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m @@ -0,0 +1,251 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCRectangleShapeGenerator.h" + +#import "MDCCornerTreatment.h" +#import "MDCEdgeTreatment.h" +#import "MDCPathGenerator.h" + +static inline CGFloat CGPointDistanceToPoint(CGPoint a, CGPoint b) { + return hypot(a.x - b.x, a.y - b.y); +} + +// Edges in clockwise order +typedef enum : NSUInteger { + MDCShapeEdgeTop = 0, + MDCShapeEdgeRight, + MDCShapeEdgeBottom, + MDCShapeEdgeLeft, +} MDCShapeEdgePosition; + +// Corners in clockwise order +typedef enum : NSUInteger { + MDCShapeCornerTopLeft = 0, + MDCShapeCornerTopRight, + MDCShapeCornerBottomRight, + MDCShapeCornerBottomLeft, +} MDCShapeCornerPosition; + +@implementation MDCRectangleShapeGenerator + +- (instancetype)init { + if (self = [super init]) { + [self setEdges:[[MDCEdgeTreatment alloc] init]]; + [self setCorners:[[MDCCornerTreatment alloc] init]]; + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + MDCRectangleShapeGenerator *copy = [[[self class] alloc] init]; + + copy.topLeftCorner = [copy.topLeftCorner copyWithZone:zone]; + copy.topRightCorner = [copy.topRightCorner copyWithZone:zone]; + copy.bottomRightCorner = [copy.bottomRightCorner copyWithZone:zone]; + copy.bottomLeftCorner = [copy.bottomLeftCorner copyWithZone:zone]; + + copy.topLeftCornerOffset = copy.topLeftCornerOffset; + copy.topRightCornerOffset = copy.topRightCornerOffset; + copy.bottomRightCornerOffset = copy.bottomRightCornerOffset; + copy.bottomLeftCornerOffset = copy.bottomLeftCornerOffset; + + copy.topEdge = [copy.topEdge copyWithZone:zone]; + copy.rightEdge = [copy.rightEdge copyWithZone:zone]; + copy.bottomEdge = [copy.bottomEdge copyWithZone:zone]; + copy.leftEdge = [copy.leftEdge copyWithZone:zone]; + + return copy; +} + +- (void)setCorners:(MDCCornerTreatment *)cornerShape { + self.topLeftCorner = [cornerShape copy]; + self.topRightCorner = [cornerShape copy]; + self.bottomRightCorner = [cornerShape copy]; + self.bottomLeftCorner = [cornerShape copy]; +} + +- (void)setEdges:(MDCEdgeTreatment *)edgeShape { + self.topEdge = [edgeShape copy]; + self.rightEdge = [edgeShape copy]; + self.bottomEdge = [edgeShape copy]; + self.leftEdge = [edgeShape copy]; +} + +- (MDCCornerTreatment *)cornerTreatmentForPosition:(MDCShapeCornerPosition)position { + switch (position) { + case MDCShapeCornerTopLeft: + return self.topLeftCorner; + case MDCShapeCornerTopRight: + return self.topRightCorner; + case MDCShapeCornerBottomLeft: + return self.bottomLeftCorner; + case MDCShapeCornerBottomRight: + return self.bottomRightCorner; + } +} + +- (CGPoint)cornerOffsetForPosition:(MDCShapeCornerPosition)position { + switch (position) { + case MDCShapeCornerTopLeft: + return self.topLeftCornerOffset; + case MDCShapeCornerTopRight: + return self.topRightCornerOffset; + case MDCShapeCornerBottomLeft: + return self.bottomLeftCornerOffset; + case MDCShapeCornerBottomRight: + return self.bottomRightCornerOffset; + } +} + +- (MDCEdgeTreatment *)edgeTreatmentForPosition:(MDCShapeEdgePosition)position { + switch (position) { + case MDCShapeEdgeTop: + return self.topEdge; + case MDCShapeEdgeLeft: + return self.leftEdge; + case MDCShapeEdgeRight: + return self.rightEdge; + case MDCShapeEdgeBottom: + return self.bottomEdge; + } +} + +- (CGPathRef)pathForSize:(CGSize)size { + CGMutablePathRef path = CGPathCreateMutable(); + MDCPathGenerator *cornerPaths[4]; + CGAffineTransform cornerTransforms[4]; + CGAffineTransform edgeTransforms[4]; + CGFloat edgeAngles[4]; + CGFloat edgeLengths[4]; + + // Start by getting the path of each corner and calculating edge angles. + for (NSInteger i = 0; i < 4; i++) { + MDCCornerTreatment *cornerShape = [self cornerTreatmentForPosition:i]; + CGFloat cornerAngle = [self angleOfCorner:i forViewSize:size]; + if (cornerShape.valueType == MDCCornerTreatmentValueTypeAbsolute) { + cornerPaths[i] = [cornerShape pathGeneratorForCornerWithAngle:cornerAngle]; + } else if (cornerShape.valueType == MDCCornerTreatmentValueTypePercentage) { + cornerPaths[i] = [cornerShape pathGeneratorForCornerWithAngle:cornerAngle forViewSize:size]; + } + edgeAngles[i] = [self angleOfEdge:i forViewSize:size]; + } + + // Create transformation matrices for each corner and edge + for (NSInteger i = 0; i < 4; i++) { + CGPoint cornerCoords = [self cornerCoordsForPosition:i forViewSize:size]; + CGAffineTransform cornerTransform = + CGAffineTransformMakeTranslation(cornerCoords.x, cornerCoords.y); + CGFloat prevEdgeAngle = edgeAngles[(i + 4 - 1) % 4]; + // We add 90 degrees (M_PI_2) here because the corner starts rotated from the edge. + cornerTransform = CGAffineTransformRotate(cornerTransform, prevEdgeAngle + (CGFloat)M_PI_2); + cornerTransforms[i] = cornerTransform; + + CGPoint edgeStartPoint = + CGPointApplyAffineTransform(cornerPaths[i].endPoint, cornerTransforms[i]); + CGAffineTransform edgeTransform = + CGAffineTransformMakeTranslation(edgeStartPoint.x, edgeStartPoint.y); + CGFloat edgeAngle = edgeAngles[i]; + edgeTransform = CGAffineTransformRotate(edgeTransform, edgeAngle); + edgeTransforms[i] = edgeTransform; + } + + // Calculate the length of each edge using the transformed corner paths. + for (NSInteger i = 0; i < 4; i++) { + NSInteger next = (i + 1) % 4; + CGPoint edgeStartPoint = + CGPointApplyAffineTransform(cornerPaths[i].endPoint, cornerTransforms[i]); + CGPoint edgeEndPoint = + CGPointApplyAffineTransform(cornerPaths[next].startPoint, cornerTransforms[next]); + edgeLengths[i] = CGPointDistanceToPoint(edgeStartPoint, edgeEndPoint); + } + + // Draw the first corner manually because we have to MoveToPoint to start the path. + CGPathMoveToPoint(path, &cornerTransforms[0], cornerPaths[0].startPoint.x, + cornerPaths[0].startPoint.y); + [cornerPaths[0] appendToCGPath:path transform:&cornerTransforms[0]]; + + // Draw the remaining three corners joined by edges. + for (NSInteger i = 1; i < 4; i++) { + // draw the edge from the previous point to the current point + MDCEdgeTreatment *edge = [self edgeTreatmentForPosition:(i - 1)]; + MDCPathGenerator *edgePath = [edge pathGeneratorForEdgeWithLength:edgeLengths[i - 1]]; + [edgePath appendToCGPath:path transform:&edgeTransforms[i - 1]]; + + MDCPathGenerator *cornerPath = cornerPaths[i]; + [cornerPath appendToCGPath:path transform:&cornerTransforms[i]]; + } + + // Draw final edge back to first point. + MDCEdgeTreatment *edge = [self edgeTreatmentForPosition:3]; + MDCPathGenerator *edgePath = [edge pathGeneratorForEdgeWithLength:edgeLengths[3]]; + [edgePath appendToCGPath:path transform:&edgeTransforms[3]]; + + CGPathCloseSubpath(path); + + return CFAutorelease(path); +} + +- (CGFloat)angleOfCorner:(MDCShapeCornerPosition)cornerPosition forViewSize:(CGSize)size { + CGPoint prevCornerCoord = [self cornerCoordsForPosition:(cornerPosition - 1 + 4) % 4 + forViewSize:size]; + CGPoint nextCornerCoord = [self cornerCoordsForPosition:(cornerPosition + 1) % 4 + forViewSize:size]; + CGPoint cornerCoord = [self cornerCoordsForPosition:cornerPosition forViewSize:size]; + CGPoint prevVector = + CGPointMake(prevCornerCoord.x - cornerCoord.x, prevCornerCoord.y - cornerCoord.y); + CGPoint nextVector = + CGPointMake(nextCornerCoord.x - cornerCoord.x, nextCornerCoord.y - cornerCoord.y); + CGFloat prevAngle = atan2(prevVector.y, prevVector.x); + CGFloat nextAngle = atan2(nextVector.y, nextVector.x); + CGFloat angle = prevAngle - nextAngle; + if (angle < 0) + angle += (CGFloat)(2 * M_PI); + return angle; +} + +- (CGFloat)angleOfEdge:(MDCShapeEdgePosition)edgePosition forViewSize:(CGSize)size { + MDCShapeCornerPosition startCornerPosition = (MDCShapeCornerPosition)edgePosition; + MDCShapeCornerPosition endCornerPosition = (startCornerPosition + 1) % 4; + CGPoint startCornerCoord = [self cornerCoordsForPosition:startCornerPosition forViewSize:size]; + CGPoint endCornerCoord = [self cornerCoordsForPosition:endCornerPosition forViewSize:size]; + + CGPoint edgeVector = + CGPointMake(endCornerCoord.x - startCornerCoord.x, endCornerCoord.y - startCornerCoord.y); + return atan2(edgeVector.y, edgeVector.x); +} + +- (CGPoint)cornerCoordsForPosition:(MDCShapeCornerPosition)cornerPosition + forViewSize:(CGSize)viewSize { + CGPoint offset = [self cornerOffsetForPosition:cornerPosition]; + CGPoint translation; + switch (cornerPosition) { + case MDCShapeCornerTopLeft: + translation = CGPointMake(0, 0); + break; + case MDCShapeCornerTopRight: + translation = CGPointMake(viewSize.width, 0); + break; + case MDCShapeCornerBottomLeft: + translation = CGPointMake(0, viewSize.height); + break; + case MDCShapeCornerBottomRight: + translation = CGPointMake(viewSize.width, viewSize.height); + break; + } + + return CGPointMake(offset.x + translation.x, offset.y + translation.y); +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h new file mode 100644 index 00000000..be235c10 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h @@ -0,0 +1,30 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + A protocol for objects that create closed CGPaths of varying sizes. + */ +@protocol MDCShapeGenerating + +/** + Creates a CGPath for the given size. + + @param size The expected size of the generated path. + @return CGPathRef A closed path of the provided size. If size is empty, may return NULL. + */ +- (nullable CGPathRef)pathForSize:(CGSize)size; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h new file mode 100644 index 00000000..37685471 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h @@ -0,0 +1,116 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +@protocol MDCShapeGenerating; + +/* + Shape support addition to a layer. + + This class is used to add Shape support to a given view by providing it with the view's main layer. + Having shape support means that by setting a shapeGenerator, you are able to have a custom shape + using a custom path for that view. + + To have your instantiating view's backgroundColor, borderColor, and borderWidth also follow the + custom shape, your view must have these APIs internally set and get from MDCShapeMediator's + corresponding shapedBackgroundColor, shapedBorderColor, and shapedBorderWidth properties. + + This class exists to supersede MDCShapedShadowLayer which previously added shape support by + overriding the layerClass and subclassing MDCShadowLayer, which are both behaviors we want to no + longer support due to their complexity and constraints. + */ +__attribute__((objc_subclassing_restricted)) @interface MDCShapeMediator : NSObject + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Designated initializer for a view requiring shape support to initialize with the view’s Core + Animation layer used for rendering (view.layer). + + The MDCShapeMediator class then keeps a weak reference to the layer and adds the required sublayers + in order to support custom shapes using custom paths. It also updates the layer's properties to + have the border and background properties as well as its shadow path be up to date with the shape's + custom path it gets through the shapeGenerator API. + */ +- (nonnull instancetype)initWithViewLayer:(nonnull CALayer *)viewLayer NS_DESIGNATED_INITIALIZER; + +/** + Call this function in layoutSubviews of the viewLayer's view to allow the shape generator to + generate the proper shape path when the bounds are now apparent. + */ +- (void)layoutShapedSublayers; + +/** + The layer which the shape logic sits on. Assign the parent layer using @c initWithLayer: + */ +@property(nonatomic, readonly, weak, nullable) CALayer *viewLayer; + +/* + Sets the shaped background color of the layer. + + Use shapedBackgroundColor instead of backgroundColor to ensure the background appears correct with + or without a valid shape. + + @note If you set shapedBackgroundColor, you should not manually write to backgroundColor or + fillColor. + */ +@property(nonatomic, copy, nullable) UIColor *shapedBackgroundColor; + +/* + Sets the shaped border color of the layer. + + Use shapedBorderColor instead of borderColor to ensure the border appears correct with or without + a valid shape. + + @note If you set shapedBorderColor, you should not manually write to borderColor. + */ +@property(nonatomic, copy, nullable) UIColor *shapedBorderColor; + +/* + Sets the shaped border width of the layer. + + Use shapedBorderWidth instead of borderWidth to ensure the border appears correct with or without + a valid shape. + + @note If you set shapedBorderWidth, you should not manually write to borderWidth. + */ +@property(nonatomic, assign) CGFloat shapedBorderWidth; + +/* + The MDCShapeGenerating object used to set the shape's path and shadow path. + + The path will be set upon assignment of this property and whenever layoutSublayers is called. + */ +@property(nonatomic, strong, nullable) id shapeGenerator; + +/* + The created CAShapeLayer representing the generated shape path for the implementing UIView + from the shapeGenerator. + + This layer is exposed to easily mask subviews of the implementing UIView so they won't spill + outside the layer to fit the bounds. + */ +@property(nonatomic, strong, nonnull) CAShapeLayer *shapeLayer; + +/* + A sublayer of @c shapeLayer that is responsible for the background color of the shape layer. + + The colorLayer imitates the path of shapeLayer and is added as a sublayer. It is updated when + shapedBackgroundColor is set on the layer. + */ +@property(nonatomic, strong, nonnull) CAShapeLayer *colorLayer; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m new file mode 100644 index 00000000..b057bb35 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m @@ -0,0 +1,197 @@ +// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShapeMediator.h" + +#import "MDCShapeGenerating.h" +#import "MaterialColor.h" + +// An epsilon for use with width/height values. +static const CGFloat kDimensionalEpsilon = 0.001; + +@implementation MDCShapeMediator + +- (instancetype)initWithViewLayer:(CALayer *)viewLayer { + self = [super init]; + if (self) { + _viewLayer = viewLayer; + _viewLayer.backgroundColor = [UIColor clearColor].CGColor; + _colorLayer = [CAShapeLayer layer]; + _colorLayer.delegate = (id)_viewLayer; + _shapeLayer = [CAShapeLayer layer]; + [_viewLayer insertSublayer:_colorLayer atIndex:0]; + } + return self; +} + +- (void)layoutShapedSublayers { + CGRect bounds = _viewLayer.bounds; + + CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + _colorLayer.position = center; + _colorLayer.bounds = bounds; + + [self prepareShadowPath]; +} + +- (void)prepareShadowPath { + if (self.shapeGenerator) { + CGRect standardizedBounds = CGRectStandardize(_viewLayer.bounds); + self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; + } +} + +- (void)setShapeGenerator:(id)shapeGenerator { + _shapeGenerator = shapeGenerator; + + CGRect standardizedBounds = CGRectStandardize(_viewLayer.bounds); + self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; +} + +- (void)setPath:(CGPathRef)path { + _viewLayer.shadowPath = path; + _colorLayer.path = path; + _shapeLayer.path = path; + + if (CGPathIsEmpty(path)) { + _viewLayer.backgroundColor = self.shapedBackgroundColor.CGColor; + _viewLayer.borderColor = self.shapedBorderColor.CGColor; + _viewLayer.borderWidth = self.shapedBorderWidth; + + _colorLayer.fillColor = nil; + _colorLayer.strokeColor = nil; + _colorLayer.lineWidth = 0; + } else { + _viewLayer.backgroundColor = nil; + _viewLayer.borderColor = nil; + _viewLayer.borderWidth = 0; + + _colorLayer.fillColor = self.shapedBackgroundColor.CGColor; + _colorLayer.strokeColor = self.shapedBorderColor.CGColor; + _colorLayer.lineWidth = self.shapedBorderWidth; + [self generateColorPathGivenLineWidth]; + } +} + +- (void)generateColorPathGivenLineWidth { + if (CGPathIsEmpty(self.path) || _colorLayer.lineWidth <= 0) { + _colorLayer.path = _viewLayer.shadowPath; + _shapeLayer.path = _viewLayer.shadowPath; + return; + } + CGFloat halfOfBorderWidth = self.shapedBorderWidth / 2.f; + CGAffineTransform colorLayerTransform = [self generateTransformInsetByValue:halfOfBorderWidth]; + CGPathRef colorLayerPath = + CGPathCreateCopyByTransformingPath(_viewLayer.shadowPath, &colorLayerTransform); + _colorLayer.path = colorLayerPath; + CGPathRelease(colorLayerPath); + // The shape layer is used to provide the user a mask for their content, which means also + // show the full border. Because the border is shown half outside and half inside + // the color layer path, we must inset the shape layer by the full border width. + CGAffineTransform shapeLayerTransform = + [self generateTransformInsetByValue:self.shapedBorderWidth]; + CGPathRef shapeLayerPath = + CGPathCreateCopyByTransformingPath(_viewLayer.shadowPath, &shapeLayerTransform); + _shapeLayer.path = shapeLayerPath; + CGPathRelease(shapeLayerPath); +} + +- (CGAffineTransform)generateTransformInsetByValue:(CGFloat)value { + // Use the identitfy transfrom when inset is less than Epsilon. + if (value < kDimensionalEpsilon) { + return CGAffineTransformIdentity; + } + + // Use the path's boundingBox to get the proportion of inset value, + // because this tranform is expected to be applied on a CGPath. + CGRect pathBoundingBox = CGPathGetPathBoundingBox(_viewLayer.shadowPath); + CGRect pathStandardizedBounds = CGRectStandardize(pathBoundingBox); + + if (CGRectGetWidth(pathStandardizedBounds) < kDimensionalEpsilon || + CGRectGetHeight(pathStandardizedBounds) < kDimensionalEpsilon) { + return CGAffineTransformIdentity; + } + + CGRect insetBounds = CGRectInset(pathStandardizedBounds, value, value); + CGFloat width = CGRectGetWidth(pathStandardizedBounds); + CGFloat height = CGRectGetHeight(pathStandardizedBounds); + CGFloat pathCenterX = CGRectGetMidX(pathStandardizedBounds); + CGFloat pathCenterY = CGRectGetMidY(pathStandardizedBounds); + // Calculate the shifted center and re-center it by applying a translation transform. + // value * 2 represents the accumulated borderWidth on each side, value * 2 / width + // represents the proportion of accumulated borderWidth in path bounds, which is also + // the value used for scale transform. + // The shiftWidth represents the shifted length horizontally on the center. + CGFloat shiftWidth = value * 2 / width * pathCenterX; + // Same calculation for height. + CGFloat shiftHeight = value * 2 / height * pathCenterY; + CGAffineTransform transform = CGAffineTransformMakeTranslation(shiftWidth, shiftHeight); + transform = CGAffineTransformScale(transform, CGRectGetWidth(insetBounds) / width, + CGRectGetHeight(insetBounds) / height); + return transform; +} + +- (CGPathRef)path { + return _colorLayer.path; +} + +- (void)setShapedBackgroundColor:(UIColor *)shapedBackgroundColor { + _shapedBackgroundColor = shapedBackgroundColor; + + if ([_viewLayer.delegate isKindOfClass:[UIView class]]) { + UIView *view = (UIView *)_viewLayer.delegate; + _shapedBackgroundColor = + [_shapedBackgroundColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; + } + + if (CGPathIsEmpty(self.path)) { + _viewLayer.backgroundColor = _shapedBackgroundColor.CGColor; + _colorLayer.fillColor = nil; + } else { + _viewLayer.backgroundColor = nil; + _colorLayer.fillColor = _shapedBackgroundColor.CGColor; + } +} + +- (void)setShapedBorderColor:(UIColor *)shapedBorderColor { + _shapedBorderColor = shapedBorderColor; + + if ([_viewLayer.delegate isKindOfClass:[UIView class]]) { + UIView *view = (UIView *)_viewLayer.delegate; + _shapedBorderColor = + [_shapedBorderColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; + } + if (CGPathIsEmpty(self.path)) { + _viewLayer.borderColor = _shapedBorderColor.CGColor; + _colorLayer.strokeColor = nil; + } else { + _viewLayer.borderColor = nil; + _colorLayer.strokeColor = _shapedBorderColor.CGColor; + } +} + +- (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth { + _shapedBorderWidth = shapedBorderWidth; + + if (CGPathIsEmpty(self.path)) { + _viewLayer.borderWidth = _shapedBorderWidth; + _colorLayer.lineWidth = 0; + } else { + _viewLayer.borderWidth = 0; + _colorLayer.lineWidth = _shapedBorderWidth; + [self generateColorPathGivenLineWidth]; + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h new file mode 100644 index 00000000..056583b5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h @@ -0,0 +1,81 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialShadowLayer.h" + +@protocol MDCShapeGenerating; + +/* + A shaped and Material-shadowed layer. + */ +@interface MDCShapedShadowLayer : MDCShadowLayer + +/* + Sets the shaped background color of the layer. + + Use shapedBackgroundColor instead of backgroundColor to ensure the background appears correct with + or without a valid shape. + + @note If you set shapedBackgroundColor, you should not manually write to backgroundColor or + fillColor. + */ +@property(nonatomic, copy, nullable) UIColor *shapedBackgroundColor; + +/* + Sets the shaped border color of the layer. + + Use shapedBorderColor instead of borderColor to ensure the border appears correct with or without + a valid shape. + + @note If you set shapedBorderColor, you should not manually write to borderColor. + */ +@property(nonatomic, copy, nullable) UIColor *shapedBorderColor; + +/* + Sets the shaped border width of the layer. + + Use shapedBorderWidth instead of borderWidth to ensure the border appears correct with or without + a valid shape. + + @note If you set shapedBorderWidth, you should not manually write to borderWidth. + */ +@property(nonatomic, assign) CGFloat shapedBorderWidth; + +/* + The MDCShapeGenerating object used to set the shape's path and shadow path. + + The path will be set upon assignment of this property and whenever layoutSublayers is called. + */ +@property(nonatomic, strong, nullable) id shapeGenerator; + +/* + The created CAShapeLayer representing the generated shape path for the implementing UIView + from the shapeGenerator. + + This layer is exposed to easily mask subviews of the implementing UIView so they won't spill + outside the layer to fit the bounds. + */ +@property(nonatomic, strong, nonnull) CAShapeLayer *shapeLayer; + +/* + A sublayer of @c shapeLayer that is responsible for the background color of the shape layer. + + The colorLayer imitates the path of shapeLayer and is added as a sublayer. It is updated when + shapedBackgroundColor is set on the layer. + */ +@property(nonatomic, strong, nonnull) CAShapeLayer *colorLayer; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m new file mode 100644 index 00000000..09628f65 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m @@ -0,0 +1,224 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShapedShadowLayer.h" + +#import "MDCShapeGenerating.h" +#import "MaterialColor.h" + +// An epsilon for use with width/height values. +static const CGFloat kDimensionalEpsilon = 0.001; + +@implementation MDCShapedShadowLayer + +- (instancetype)init { + self = [super init]; + if (self) { + [self commonMDCShapedShadowLayerInit]; + } + return self; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonMDCShapedShadowLayerInit]; + } + return self; +} + +- (instancetype)initWithLayer:(id)layer { + self = [super initWithLayer:layer]; + if (self && [self isKindOfClass:[MDCShapedShadowLayer class]]) { + MDCShapedShadowLayer *otherLayer = (MDCShapedShadowLayer *)layer; + + _shapeGenerator = [otherLayer.shapeGenerator copyWithZone:NULL]; + // We don't need to copy fillColor because that gets copied by [super initWithLayer:]. + + // [CALayer initWithLayer:] copies all sublayers, so we have to manually fetch our CAShapeLayer. + CALayer *sublayer = [[self sublayers] firstObject]; + if ([sublayer isKindOfClass:[CAShapeLayer class]]) { + _colorLayer = (CAShapeLayer *)sublayer; + } + } + return self; +} + +- (void)commonMDCShapedShadowLayerInit { + self.backgroundColor = [UIColor clearColor].CGColor; + _colorLayer = [CAShapeLayer layer]; + _colorLayer.delegate = self; + _shapeLayer = [CAShapeLayer layer]; + [self addSublayer:_colorLayer]; +} + +- (void)layoutSublayers { + [super layoutSublayers]; + + CGRect bounds = self.bounds; + CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + _colorLayer.position = center; + _colorLayer.bounds = bounds; +} + +- (void)prepareShadowPath { + if (self.shapeGenerator) { + CGRect standardizedBounds = CGRectStandardize(self.bounds); + self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; + } +} + +- (void)setShapeGenerator:(id)shapeGenerator { + _shapeGenerator = shapeGenerator; + + CGRect standardizedBounds = CGRectStandardize(self.bounds); + self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; +} + +- (void)setPath:(CGPathRef)path { + self.shadowPath = path; + _colorLayer.path = path; + _shapeLayer.path = path; + + if (CGPathIsEmpty(path)) { + self.backgroundColor = self.shapedBackgroundColor.CGColor; + self.borderColor = self.shapedBorderColor.CGColor; + self.borderWidth = self.shapedBorderWidth; + + _colorLayer.fillColor = nil; + _colorLayer.strokeColor = nil; + _colorLayer.lineWidth = 0; + } else { + self.backgroundColor = nil; + self.borderColor = nil; + self.borderWidth = 0; + + _colorLayer.fillColor = self.shapedBackgroundColor.CGColor; + _colorLayer.strokeColor = self.shapedBorderColor.CGColor; + _colorLayer.lineWidth = self.shapedBorderWidth; + [self generateColorPathGivenLineWidth]; + } +} + +- (void)generateColorPathGivenLineWidth { + if (CGPathIsEmpty(self.path) || _colorLayer.lineWidth <= 0) { + _colorLayer.path = self.shadowPath; + _shapeLayer.path = self.shadowPath; + return; + } + CGFloat halfOfBorderWidth = self.shapedBorderWidth / 2.f; + CGAffineTransform colorLayerTransform = [self generateTransformInsetByValue:halfOfBorderWidth]; + CGPathRef colorLayerPath = + CGPathCreateCopyByTransformingPath(self.shadowPath, &colorLayerTransform); + _colorLayer.path = colorLayerPath; + CGPathRelease(colorLayerPath); + // The shape layer is used to provide the user a mask for their content, which means also + // show the full border. Because the border is shown half outside and half inside + // the color layer path, we must inset the shape layer by the full border width. + CGAffineTransform shapeLayerTransform = + [self generateTransformInsetByValue:self.shapedBorderWidth]; + CGPathRef shapeLayerPath = + CGPathCreateCopyByTransformingPath(self.shadowPath, &shapeLayerTransform); + _shapeLayer.path = shapeLayerPath; + CGPathRelease(shapeLayerPath); +} + +- (CGAffineTransform)generateTransformInsetByValue:(CGFloat)value { + // Use the identitfy transfrom when inset is less than Epsilon. + if (value < kDimensionalEpsilon) { + return CGAffineTransformIdentity; + } + + // Use the path's boundingBox to get the proportion of inset value, + // because this tranform is expected to be applied on a CGPath. + CGRect pathBoundingBox = CGPathGetPathBoundingBox(self.shadowPath); + CGRect pathStandardizedBounds = CGRectStandardize(pathBoundingBox); + + if (CGRectGetWidth(pathStandardizedBounds) < kDimensionalEpsilon || + CGRectGetHeight(pathStandardizedBounds) < kDimensionalEpsilon) { + return CGAffineTransformIdentity; + } + + CGRect insetBounds = CGRectInset(pathStandardizedBounds, value, value); + CGFloat width = CGRectGetWidth(pathStandardizedBounds); + CGFloat height = CGRectGetHeight(pathStandardizedBounds); + CGFloat pathCenterX = CGRectGetMidX(pathStandardizedBounds); + CGFloat pathCenterY = CGRectGetMidY(pathStandardizedBounds); + // Calculate the shifted center and re-center it by applying a translation transform. + // value * 2 represents the accumulated borderWidth on each side, value * 2 / width + // represents the proportion of accumulated borderWidth in path bounds, which is also + // the value used for scale transform. + // The shiftWidth represents the shifted length horizontally on the center. + CGFloat shiftWidth = value * 2 / width * pathCenterX; + // Same calculation for height. + CGFloat shiftHeight = value * 2 / height * pathCenterY; + CGAffineTransform transform = CGAffineTransformMakeTranslation(shiftWidth, shiftHeight); + transform = CGAffineTransformScale(transform, CGRectGetWidth(insetBounds) / width, + CGRectGetHeight(insetBounds) / height); + return transform; +} + +- (CGPathRef)path { + return _colorLayer.path; +} + +- (void)setShapedBackgroundColor:(UIColor *)shapedBackgroundColor { + _shapedBackgroundColor = shapedBackgroundColor; + + if ([self.delegate isKindOfClass:[UIView class]]) { + UIView *view = (UIView *)self.delegate; + _shapedBackgroundColor = + [_shapedBackgroundColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; + } + + if (CGPathIsEmpty(self.path)) { + self.backgroundColor = _shapedBackgroundColor.CGColor; + _colorLayer.fillColor = nil; + } else { + self.backgroundColor = nil; + _colorLayer.fillColor = _shapedBackgroundColor.CGColor; + } +} + +- (void)setShapedBorderColor:(UIColor *)shapedBorderColor { + _shapedBorderColor = shapedBorderColor; + + if ([self.delegate isKindOfClass:[UIView class]]) { + UIView *view = (UIView *)self.delegate; + _shapedBorderColor = + [_shapedBorderColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; + } + if (CGPathIsEmpty(self.path)) { + self.borderColor = _shapedBorderColor.CGColor; + _colorLayer.strokeColor = nil; + } else { + self.borderColor = nil; + _colorLayer.strokeColor = _shapedBorderColor.CGColor; + } +} + +- (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth { + _shapedBorderWidth = shapedBorderWidth; + + if (CGPathIsEmpty(self.path)) { + self.borderWidth = _shapedBorderWidth; + _colorLayer.lineWidth = 0; + } else { + self.borderWidth = 0; + _colorLayer.lineWidth = _shapedBorderWidth; + [self generateColorPathGivenLineWidth]; + } +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h new file mode 100644 index 00000000..82036c3d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h @@ -0,0 +1,70 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialShadowElevations.h" + +@protocol MDCShapeGenerating; + +/** + MDCShapedView is a primitive view class which makes it easy to style the shape + of the view's edges and corners. + + MDCShapedView manages mapping the paths generated by the shapeGenerator to the + backing CAShapeLayer whenever the view is resized. + */ +@interface MDCShapedView : UIView + +/** + The elevation of the layer in points. + + The higher the elevation, the more spread out the shadow is. This is distinct from the layer's + zPosition which can be used to order overlapping layers, but will have no affect on the size of + the shadow. + + Negative values act as if zero were specified. + */ +@property(nonatomic, assign) MDCShadowElevation elevation; + +/** + The shape generator used to generate a new CGPath whenever the view is resized. + */ +@property(nonatomic, strong, nullable) IBOutlet id shapeGenerator; + +/** + The stroke color of the shape generated by shapeGenerator. + + The default is nil, which results in no stroke being drawn. + */ +@property(nonatomic, copy, nullable) UIColor *shapedBorderColor; + +/** + The stroke width of the shape generated by shapeGenerator. + + The default is 0, which results in no stroke being drawn. + */ +@property(nonatomic, assign) CGFloat shapedBorderWidth; + +/** + Initializes an MDCShapedView. + + @param frame The frame of the shaped view. + @param shapeGenerator The shape generator used to set the shape of the view. + */ +- (nonnull instancetype)initWithFrame:(CGRect)frame + shapeGenerator:(nullable id)shapeGenerator + NS_DESIGNATED_INITIALIZER; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m new file mode 100644 index 00000000..2774d26c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m @@ -0,0 +1,97 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCShapedView.h" + +#import "MDCShapedShadowLayer.h" + +@interface MDCShapedView () +@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; +@property(nonatomic, readonly) CGSize pathSize; +@end + +@implementation MDCShapedView + +@dynamic layer; + ++ (Class)layerClass { + return [MDCShapedShadowLayer class]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +// https://stackoverflow.com/questions/24458608/convenience-initializer-missing-a-self-call-to-another-initializer +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [super initWithCoder:aDecoder]; +} +#pragma clang diagnostic pop + +- (nonnull instancetype)initWithFrame:(CGRect)frame { + return [self initWithFrame:frame shapeGenerator:nil]; +} + +- (nonnull instancetype)initWithFrame:(CGRect)frame + shapeGenerator:(nullable id)shapeGenerator { + if (self = [super initWithFrame:frame]) { + self.layer.shapeGenerator = shapeGenerator; + } + return self; +} + +- (void)setElevation:(CGFloat)elevation { + self.layer.elevation = elevation; +} + +- (CGFloat)elevation { + return self.layer.elevation; +} + +- (void)setShapeGenerator:(id)shapeGenerator { + self.layer.shapeGenerator = shapeGenerator; +} + +- (id)shapeGenerator { + return self.layer.shapeGenerator; +} + +- (UIColor *)shapedBorderColor { + return self.layer.shapedBorderColor; +} + +- (void)setShapedBorderColor:(UIColor *)shapedBorderColor { + self.layer.shapedBorderColor = shapedBorderColor; +} + +- (CGFloat)shapedBorderWidth { + return self.layer.shapedBorderWidth; +} + +- (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth { + self.layer.shapedBorderWidth = shapedBorderWidth; +} + +// MDCShapedView captures backgroundColor assigments so that they can be set to the +// MDCShapedShadowLayer fillColor. If we don't do this the background of the layer will obscure any +// shapes drawn by the shape layer. +- (void)setBackgroundColor:(UIColor *)backgroundColor { + // We intentionally capture this and don't send it to super so that the UIView backgroundColor is + // fixed to [UIColor clearColor]. + self.layer.shapedBackgroundColor = backgroundColor; +} + +- (UIColor *)backgroundColor { + return self.layer.shapedBackgroundColor; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h new file mode 100644 index 00000000..c68ff07e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h @@ -0,0 +1,22 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCCornerTreatment.h" +#import "MDCEdgeTreatment.h" +#import "MDCPathGenerator.h" +#import "MDCRectangleShapeGenerator.h" +#import "MDCShapeGenerating.h" +#import "MDCShapeMediator.h" +#import "MDCShapedShadowLayer.h" +#import "MDCShapedView.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h new file mode 100644 index 00000000..c1c69055 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h @@ -0,0 +1,80 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + Set of constants that describe Material's text styles. + + These are similar, but not quite equivalent, to Apple's UIFontTextStyle. + */ +typedef NSString *_Nonnull MDCTextStyle NS_TYPED_EXTENSIBLE_ENUM; + +UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline1; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline2; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline3; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline4; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline5; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline6; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleSubtitle1; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleSubtitle2; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleBody1; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleBody2; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleButton; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleCaption; +UIKIT_EXTERN MDCTextStyle const MDCTextStyleOverline; + +/** + MDCFontScaler attaches a scaling curve to a UIFont via an associated object on that font instance. + + Instances of fonts processed through MDCFontScaler will have an associated dictionary that maps + UIFontTextStyle to Font Size. Category methods on UIFont allow clients to get instances of + resized fonts based on this associated dictionary. Note that an instance of MDCFontScaler is + NOT attached to the processed font. + + This interface is similar to UIFontMetrics, but the fonts returned from MDCFontScaler do *not* + automatically adjust when the device's text size / content size category is changed. + */ +@interface MDCFontScaler : NSObject + +/** + Initializes a font scaler object with the specified text style. + + @param textStyle The style that will be used to determine the scaling curver associated with the + returned font. For example, MaterialTextStyleBody1. + @return An initialized font scaler object. + */ +- (nonnull instancetype)initForMaterialTextStyle:(MDCTextStyle)textStyle NS_DESIGNATED_INITIALIZER; + +/** + Creates and returns a font scaler object with the specified text style. + + @param textStyle The style that will be used to determine the scaling curver associated with the + returned font. For example, MaterialTextStyleBody1. + @return An initialized font scaler object. + */ ++ (nonnull instancetype)scalerForMaterialTextStyle:(MDCTextStyle)textStyle; + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + Returns an instance of the specified font with an associated scaling curve. + + @param font The base font to use when applying the scaling curve. + @return An instance of the specified font with an associated scaling curve, and scaled to the + current Dynamic Type setting. + */ +- (nonnull UIFont *)scaledFontWithFont:(nonnull UIFont *)font; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m new file mode 100644 index 00000000..9da0813b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m @@ -0,0 +1,293 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFontScaler.h" + +#import + +#import "UIFont+MaterialScalable.h" +#import "private/MDCTypographyUtilities.h" + +MDCTextStyle const MDCTextStyleHeadline1 = @"MDC.TextStyle.Headline1"; +MDCTextStyle const MDCTextStyleHeadline2 = @"MDC.TextStyle.Headline2"; +MDCTextStyle const MDCTextStyleHeadline3 = @"MDC.TextStyle.Headline3"; +MDCTextStyle const MDCTextStyleHeadline4 = @"MDC.TextStyle.Headline4"; +MDCTextStyle const MDCTextStyleHeadline5 = @"MDC.TextStyle.Headline5"; +MDCTextStyle const MDCTextStyleHeadline6 = @"MDC.TextStyle.Headline6"; +MDCTextStyle const MDCTextStyleSubtitle1 = @"MDC.TextStyle.Subtitle1"; +MDCTextStyle const MDCTextStyleSubtitle2 = @"MDC.TextStyle.Subtitle2"; +MDCTextStyle const MDCTextStyleBody1 = @"MDC.TextStyle.Body1"; +MDCTextStyle const MDCTextStyleBody2 = @"MDC.TextStyle.Body2"; +MDCTextStyle const MDCTextStyleButton = @"MDC.TextStyle.Button"; +MDCTextStyle const MDCTextStyleCaption = @"MDC.TextStyle.Caption"; +MDCTextStyle const MDCTextStyleOverline = @"MDC.TextStyle.Overline"; + +@implementation MDCFontScaler { + NSDictionary *_scalingCurve; + MDCTextStyle _textStyle; +} + ++ (instancetype)scalerForMaterialTextStyle:(MDCTextStyle)textStyle { + return [[MDCFontScaler alloc] initForMaterialTextStyle:textStyle]; +} + +- (instancetype)initForMaterialTextStyle:(MDCTextStyle)textStyle { + self = [super init]; + if (self) { + _textStyle = [textStyle copy]; + + // NOTE: All scaling curves MUST include a full set of values for ALL UIContentSizeCategory + // values. This values must not decrease as the category size increases. To put it another + // way, the value for UIContentSizeCategoryLarge must not be smaller than the value for + // UIContentSizeCategoryMedium. + if ([MDCTextStyleHeadline1 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @84, + UIContentSizeCategorySmall : @88, + UIContentSizeCategoryMedium : @92, + UIContentSizeCategoryLarge : @96, + UIContentSizeCategoryExtraLarge : @100, + UIContentSizeCategoryExtraExtraLarge : @104, + UIContentSizeCategoryExtraExtraExtraLarge : @108, + UIContentSizeCategoryAccessibilityMedium : @108, + UIContentSizeCategoryAccessibilityLarge : @108, + UIContentSizeCategoryAccessibilityExtraLarge : @108, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @108, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @108 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleHeadline2 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @54, + UIContentSizeCategorySmall : @56, + UIContentSizeCategoryMedium : @58, + UIContentSizeCategoryLarge : @60, + UIContentSizeCategoryExtraLarge : @62, + UIContentSizeCategoryExtraExtraLarge : @64, + UIContentSizeCategoryExtraExtraExtraLarge : @66, + UIContentSizeCategoryAccessibilityMedium : @66, + UIContentSizeCategoryAccessibilityLarge : @66, + UIContentSizeCategoryAccessibilityExtraLarge : @66, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @66, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @66 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleHeadline3 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @42, + UIContentSizeCategorySmall : @44, + UIContentSizeCategoryMedium : @46, + UIContentSizeCategoryLarge : @48, + UIContentSizeCategoryExtraLarge : @50, + UIContentSizeCategoryExtraExtraLarge : @52, + UIContentSizeCategoryExtraExtraExtraLarge : @54, + UIContentSizeCategoryAccessibilityMedium : @54, + UIContentSizeCategoryAccessibilityLarge : @54, + UIContentSizeCategoryAccessibilityExtraLarge : @54, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @54, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @54 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleHeadline4 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @28, + UIContentSizeCategorySmall : @30, + UIContentSizeCategoryMedium : @32, + UIContentSizeCategoryLarge : @34, + UIContentSizeCategoryExtraLarge : @36, + UIContentSizeCategoryExtraExtraLarge : @38, + UIContentSizeCategoryExtraExtraExtraLarge : @40, + UIContentSizeCategoryAccessibilityMedium : @42, + UIContentSizeCategoryAccessibilityLarge : @42, + UIContentSizeCategoryAccessibilityExtraLarge : @42, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @42, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleHeadline5 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @21, + UIContentSizeCategorySmall : @22, + UIContentSizeCategoryMedium : @23, + UIContentSizeCategoryLarge : @24, + UIContentSizeCategoryExtraLarge : @26, + UIContentSizeCategoryExtraExtraLarge : @28, + UIContentSizeCategoryExtraExtraExtraLarge : @30, + UIContentSizeCategoryAccessibilityMedium : @32, + UIContentSizeCategoryAccessibilityLarge : @32, + UIContentSizeCategoryAccessibilityExtraLarge : @32, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @32, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @32 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleHeadline6 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @17, + UIContentSizeCategorySmall : @18, + UIContentSizeCategoryMedium : @19, + UIContentSizeCategoryLarge : @20, + UIContentSizeCategoryExtraLarge : @22, + UIContentSizeCategoryExtraExtraLarge : @24, + UIContentSizeCategoryExtraExtraExtraLarge : @26, + UIContentSizeCategoryAccessibilityMedium : @28, + UIContentSizeCategoryAccessibilityLarge : @28, + UIContentSizeCategoryAccessibilityExtraLarge : @28, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @28, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @28 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleSubtitle1 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @13, + UIContentSizeCategorySmall : @14, + UIContentSizeCategoryMedium : @15, + UIContentSizeCategoryLarge : @16, + UIContentSizeCategoryExtraLarge : @18, + UIContentSizeCategoryExtraExtraLarge : @20, + UIContentSizeCategoryExtraExtraExtraLarge : @22, + UIContentSizeCategoryAccessibilityMedium : @25, + UIContentSizeCategoryAccessibilityLarge : @30, + UIContentSizeCategoryAccessibilityExtraLarge : @37, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @44, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @52 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleSubtitle2 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @11, + UIContentSizeCategorySmall : @12, + UIContentSizeCategoryMedium : @13, + UIContentSizeCategoryLarge : @14, + UIContentSizeCategoryExtraLarge : @16, + UIContentSizeCategoryExtraExtraLarge : @18, + UIContentSizeCategoryExtraExtraExtraLarge : @20, + UIContentSizeCategoryAccessibilityMedium : @22, + UIContentSizeCategoryAccessibilityLarge : @25, + UIContentSizeCategoryAccessibilityExtraLarge : @30, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @36, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleBody2 isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @11, + UIContentSizeCategorySmall : @12, + UIContentSizeCategoryMedium : @13, + UIContentSizeCategoryLarge : @14, + UIContentSizeCategoryExtraLarge : @16, + UIContentSizeCategoryExtraExtraLarge : @18, + UIContentSizeCategoryExtraExtraExtraLarge : @20, + UIContentSizeCategoryAccessibilityMedium : @22, + UIContentSizeCategoryAccessibilityLarge : @25, + UIContentSizeCategoryAccessibilityExtraLarge : @30, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @36, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleButton isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @11, + UIContentSizeCategorySmall : @12, + UIContentSizeCategoryMedium : @13, + UIContentSizeCategoryLarge : @14, + UIContentSizeCategoryExtraLarge : @16, + UIContentSizeCategoryExtraExtraLarge : @18, + UIContentSizeCategoryExtraExtraExtraLarge : @20, + UIContentSizeCategoryAccessibilityMedium : @22, + UIContentSizeCategoryAccessibilityLarge : @24, + UIContentSizeCategoryAccessibilityExtraLarge : @26, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @28, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @30 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleCaption isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @11, + UIContentSizeCategorySmall : @11, + UIContentSizeCategoryMedium : @11, + UIContentSizeCategoryLarge : @12, + UIContentSizeCategoryExtraLarge : @14, + UIContentSizeCategoryExtraExtraLarge : @16, + UIContentSizeCategoryExtraExtraExtraLarge : @18, + UIContentSizeCategoryAccessibilityMedium : @20, + UIContentSizeCategoryAccessibilityLarge : @22, + UIContentSizeCategoryAccessibilityExtraLarge : @24, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @26, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @28 + }; + _scalingCurve = scalingCurve; + } else if ([MDCTextStyleOverline isEqualToString:textStyle]) { + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @8, + UIContentSizeCategorySmall : @8, + UIContentSizeCategoryMedium : @9, + UIContentSizeCategoryLarge : @10, + UIContentSizeCategoryExtraLarge : @12, + UIContentSizeCategoryExtraExtraLarge : @14, + UIContentSizeCategoryExtraExtraExtraLarge : @16, + UIContentSizeCategoryAccessibilityMedium : @18, + UIContentSizeCategoryAccessibilityLarge : @20, + UIContentSizeCategoryAccessibilityExtraLarge : @22, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @24, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @26 + }; + _scalingCurve = scalingCurve; + } else { + // If nothing matches, return the metrics for MDCTextStyleBody1 + _textStyle = [MDCTextStyleBody1 copy]; + NSDictionary *scalingCurve = @{ + UIContentSizeCategoryExtraSmall : @13, + UIContentSizeCategorySmall : @14, + UIContentSizeCategoryMedium : @15, + UIContentSizeCategoryLarge : @16, + UIContentSizeCategoryExtraLarge : @18, + UIContentSizeCategoryExtraExtraLarge : @20, + UIContentSizeCategoryExtraExtraExtraLarge : @22, + UIContentSizeCategoryAccessibilityMedium : @26, + UIContentSizeCategoryAccessibilityLarge : @30, + UIContentSizeCategoryAccessibilityExtraLarge : @34, + UIContentSizeCategoryAccessibilityExtraExtraLarge : @38, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 + }; + _scalingCurve = scalingCurve; + } + } + + return self; +} + +- (UIFont *)scaledFontWithFont:(UIFont *)font { + // If it is available, query the preferredContentSizeCategory. + UIContentSizeCategory sizeCategory = GetCurrentSizeCategory(); + + // We create a new font to ensure we have a complete set of font traits. + // They we apply our new scaling curve before returning a scaled font. + UIFont *templateFont = [UIFont fontWithDescriptor:font.fontDescriptor size:0.0]; + templateFont.mdc_scalingCurve = _scalingCurve; + UIFont *scaledFont = [templateFont mdc_scaledFontForSizeCategory:sizeCategory]; + + return scaledFont; +} + +- (NSString *)description { + NSString *superDescription = [super description]; + NSString *styleDescription = @"No Attached Style"; + if (_textStyle) { + styleDescription = _textStyle; + } + + return [NSString stringWithFormat:@"%@ %@", superDescription, styleDescription]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h new file mode 100644 index 00000000..2cbe5a69 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h @@ -0,0 +1,38 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + Material font text styles + + These styles are defined in: + https://material.io/go/design-typography + This enumeration is a set of semantic descriptions intended to describe the fonts returned by + + [UIFont mdc_preferredFontForMaterialTextStyle:] + + [UIFontDescriptor mdc_preferredFontDescriptorForMaterialTextStyle:] + */ +typedef NS_ENUM(NSInteger, MDCFontTextStyle) { + MDCFontTextStyleBody1, + MDCFontTextStyleBody2, + MDCFontTextStyleCaption, + MDCFontTextStyleHeadline, + MDCFontTextStyleSubheadline, + MDCFontTextStyleTitle, + MDCFontTextStyleDisplay1, + MDCFontTextStyleDisplay2, + MDCFontTextStyleDisplay3, + MDCFontTextStyleDisplay4, + MDCFontTextStyleButton, +}; diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h new file mode 100644 index 00000000..8ac239ac --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h @@ -0,0 +1,196 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#pragma mark - Soon to be deprecated + +/** + MDCTypography uses this protocol to delegate responsibility of loading the custom fonts. + + The spec defines the Roboto font family and uses three fonts in the named styles. Use this + protocol to define your own fonts if there is a brand need. + + @warning This protocol will soon be deprecated. Consider using MDCTypographyScheme from the + schemes/Typography component instead. + + @see https://material.io/go/design-typography#typography-styles + */ +@protocol MDCTypographyFontLoading +@required + +/** Asks the receiver to return a font with a light weight. FontSize must be larger tha 0. */ +- (nullable UIFont *)lightFontOfSize:(CGFloat)fontSize; + +/** Asks the receiver to return a font with a normal weight. FontSize must be larger tha 0. */ +- (nonnull UIFont *)regularFontOfSize:(CGFloat)fontSize; + +/** Asks the receiver to return a font with a medium weight. FontSize must be larger tha 0. */ +- (nullable UIFont *)mediumFontOfSize:(CGFloat)fontSize; + +@optional + +/** Asks the receiver to return a font with a bold weight. FontSize must be larger tha 0. */ +- (nonnull UIFont *)boldFontOfSize:(CGFloat)fontSize; + +/** Asks the receiver to return an italic font. FontSize must be larger tha 0. */ +- (nonnull UIFont *)italicFontOfSize:(CGFloat)fontSize; + +/** Asks the receiver to return a font with an italic bold weight. FontSize must be larger tha 0. */ +- (nullable UIFont *)boldItalicFontOfSize:(CGFloat)fontSize; + +/** Returns a bold version of the specified font. */ +- (nonnull UIFont *)boldFontFromFont:(nonnull UIFont *)font; + +/** Returns an italic version of the specified font. */ +- (nonnull UIFont *)italicFontFromFont:(nonnull UIFont *)font; +/** + Asks the receiver to determine if a particular font would be considered "large" for the purposes of + calculating contrast ratios. + + Large fonts are defined as greater than 18pt normal or 14pt bold. + For more see: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html + + @param font The font to examine. + @return YES if the font is considered "large". + */ +- (BOOL)isLargeForContrastRatios:(nonnull UIFont *)font; + +@end + +/** + Typographic constants and helpers. + + To use these fonts, you must add MaterialTypography.bundle to your target. + + @warning This class will soon be deprecated. Consider using MDCTypographyScheme from the + schemes/Typography component instead. + + @see https://material.io/go/design-typography#typography-styles + */ +@interface MDCTypography : NSObject + +#pragma mark - Font loader access + +/** Set the font loader in order to use a non-system font. */ ++ (void)setFontLoader:(nonnull id)fontLoader; + +/** Get the current font loader. */ ++ (nonnull id)fontLoader; + +#pragma mark - Display fonts (extra large fonts) + +/** Returns the display 4 font. (largest of the display font sizes) */ ++ (nonnull UIFont *)display4Font; + +/** Returns the recommended opacity of black text for the display fonts 4. */ ++ (CGFloat)display4FontOpacity; + +/** Returns the display 3 font. (second largest of the display font sizes) */ ++ (nonnull UIFont *)display3Font; + +/** Returns the recommended opacity of black text for the display fonts 3. */ ++ (CGFloat)display3FontOpacity; + +/** Returns the display 2 font. (third largest of the display font sizes) */ ++ (nonnull UIFont *)display2Font; + +/** Returns the recommended opacity of black text for the display fonts 2. */ ++ (CGFloat)display2FontOpacity; + +/** Returns the display 1 font. (smallest of the display font sizes) */ ++ (nonnull UIFont *)display1Font; + +/** Returns the recommended opacity of black text for the display fonts 1. */ ++ (CGFloat)display1FontOpacity; + +#pragma mark - Common UI fonts + +/** Returns the headline font. */ ++ (nonnull UIFont *)headlineFont; + +/** Returns the recommended opacity of black text for the headline font. */ ++ (CGFloat)headlineFontOpacity; + +/** Returns the title font. */ ++ (nonnull UIFont *)titleFont; + +/** Returns the recommended opacity of black text for the title font. */ ++ (CGFloat)titleFontOpacity; + +/** Returns the subhead font. (subtitle) */ ++ (nonnull UIFont *)subheadFont; + +/** Returns the recommended opacity of black text for the subhead font. */ ++ (CGFloat)subheadFontOpacity; + +/** Returns the body 2 text font. (bold text) */ ++ (nonnull UIFont *)body2Font; + +/** Returns the recommended opacity of black text for the body 2 font. */ ++ (CGFloat)body2FontOpacity; + +/** Returns the body 1 text font. (normal text) */ ++ (nonnull UIFont *)body1Font; + +/** Returns the recommended opacity of black text for the body 1 font. */ ++ (CGFloat)body1FontOpacity; + +/** Returns the caption font. (a small font for image captions) */ ++ (nonnull UIFont *)captionFont; + +/** Returns the recommended opacity of black text for the caption font. */ ++ (CGFloat)captionFontOpacity; + +/** Returns a font for buttons. */ ++ (nonnull UIFont *)buttonFont; + +/** Returns the recommended opacity of black text for the button font. */ ++ (CGFloat)buttonFontOpacity; + +/** Returns a bold version of the specified font. */ ++ (nonnull UIFont *)boldFontFromFont:(nonnull UIFont *)font; + +/** Returns an italic version of the specified font. */ ++ (nonnull UIFont *)italicFontFromFont:(nonnull UIFont *)font; + +/** + Asks the receiver to determine if a particular font would be considered "large" for the purposes of + calculating contrast ratios. + + Large fonts are defined as greater than 18pt normal or 14pt bold. If the passed font is nil, then + this method returns NO. + For more see: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html + + @param font The font to examine. + @return YES if the font is non-nil and is considered "large". + */ ++ (BOOL)isLargeForContrastRatios:(nonnull UIFont *)font; + +@end + +/** + MDCSystemFontLoader allows you to use the system font for @c MDCTypography. + + @warning This class will soon be deprecated. Consider using MDCTypographyScheme from the + schemes/Typography component instead. + + #### Example + + ``` + [MDCTypography setFontLoader:[[MDCSystemFontLoader alloc] init]]; + ``` + */ +@interface MDCSystemFontLoader : NSObject +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m new file mode 100644 index 00000000..0052ecc6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m @@ -0,0 +1,324 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCTypography.h" + +#import "private/UIFont+MaterialTypographyPrivate.h" +#import + +static id gFontLoader = nil; +const CGFloat MDCTypographyStandardOpacity = (CGFloat)0.87; +const CGFloat MDCTypographySecondaryOpacity = (CGFloat)0.54; + +@implementation MDCTypography + +#pragma mark - Font loader access + ++ (void)setFontLoader:(id)fontLoader { + if (gFontLoader && fontLoader != gFontLoader) { + [[NSNotificationCenter defaultCenter] removeObserver:gFontLoader]; + } + gFontLoader = fontLoader; + NSAssert(gFontLoader, + @"Font loader can't be null. The font loader will be reset to the default font loader."); + if (!gFontLoader) { + gFontLoader = [self defaultFontLoader]; + } +} + ++ (id)fontLoader { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (!gFontLoader) { + gFontLoader = [self defaultFontLoader]; + } + }); + return gFontLoader; +} + +#pragma mark - Display fonts (extra large fonts) + ++ (UIFont *)display4Font { + return [[self fontLoader] lightFontOfSize:112]; +} + ++ (CGFloat)display4FontOpacity { + return MDCTypographySecondaryOpacity; +} + ++ (UIFont *)display3Font { + return [[self fontLoader] regularFontOfSize:56]; +} + ++ (CGFloat)display3FontOpacity { + return MDCTypographySecondaryOpacity; +} + ++ (UIFont *)display2Font { + return [[self fontLoader] regularFontOfSize:45]; +} + ++ (CGFloat)display2FontOpacity { + return MDCTypographySecondaryOpacity; +} + ++ (UIFont *)display1Font { + return [[self fontLoader] regularFontOfSize:34]; +} + ++ (CGFloat)display1FontOpacity { + return MDCTypographySecondaryOpacity; +} + +#pragma mark - Common UI fonts. + ++ (UIFont *)headlineFont { + return [[self fontLoader] regularFontOfSize:24]; +} + ++ (CGFloat)headlineFontOpacity { + return MDCTypographyStandardOpacity; +} + ++ (UIFont *)titleFont { + return [[self fontLoader] mediumFontOfSize:20]; +} + ++ (CGFloat)titleFontOpacity { + return MDCTypographyStandardOpacity; +} + ++ (UIFont *)subheadFont { + return [[self fontLoader] regularFontOfSize:16]; +} + ++ (CGFloat)subheadFontOpacity { + return MDCTypographyStandardOpacity; +} + ++ (UIFont *)body2Font { + return [[self fontLoader] mediumFontOfSize:14]; +} + ++ (CGFloat)body2FontOpacity { + return MDCTypographyStandardOpacity; +} + ++ (UIFont *)body1Font { + return [[self fontLoader] regularFontOfSize:14]; +} + ++ (CGFloat)body1FontOpacity { + return MDCTypographyStandardOpacity; +} + ++ (UIFont *)captionFont { + return [[self fontLoader] regularFontOfSize:12]; +} + ++ (CGFloat)captionFontOpacity { + return MDCTypographySecondaryOpacity; +} + ++ (UIFont *)buttonFont { + return [[self fontLoader] mediumFontOfSize:14]; +} + ++ (CGFloat)buttonFontOpacity { + return MDCTypographyStandardOpacity; +} + ++ (BOOL)isLargeForContrastRatios:(nonnull UIFont *)font { + id fontLoader = [self fontLoader]; + + if ([fontLoader respondsToSelector:@selector(isLargeForContrastRatios:)]) { + return [fontLoader isLargeForContrastRatios:font]; + } + + return [MDFTextAccessibility isLargeForContrastRatios:font]; +} + ++ (UIFont *)italicFontFromFont:(UIFont *)font { + SEL selector = @selector(italicFontFromFont:); + if ([self.fontLoader respondsToSelector:selector]) { + return [self.fontLoader italicFontFromFont:font]; + } + UIFontDescriptor *fontDescriptor = + [font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic]; + UIFont *fontFromDescriptor = [UIFont fontWithDescriptor:fontDescriptor size:0]; + return fontFromDescriptor ? fontFromDescriptor : [UIFont italicSystemFontOfSize:font.pointSize]; +} + ++ (UIFont *)boldFontFromFont:(UIFont *)font { + SEL selector = @selector(boldFontFromFont:); + if ([self.fontLoader respondsToSelector:selector]) { + return [self.fontLoader boldFontFromFont:font]; + } + UIFontDescriptorSymbolicTraits traits = UIFontDescriptorTraitBold; + if (font.mdc_slant != 0) { + traits = traits | UIFontDescriptorTraitItalic; + } + UIFontDescriptor *fontDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:traits]; + UIFont *fontFromDescriptor = [UIFont fontWithDescriptor:fontDescriptor size:0]; + return fontFromDescriptor ? fontFromDescriptor : [UIFont boldSystemFontOfSize:font.pointSize]; +} + +#pragma mark - Private + ++ (id)defaultFontLoader { + return [[MDCSystemFontLoader alloc] init]; +} + +@end + +@interface MDCSystemFontLoader () + +/* + In collectionView scrolling tests, manually caching UIFonts performs around 4.5 times better + (e.g. 230 ms vs. 1,080 ms in one test) than calling [UIFont systemFontForSize:weight:] every time. + */ +@property(nonatomic, strong) NSCache *fontCache; + +@end + +@implementation MDCSystemFontLoader + +- (instancetype)init { + self = [super init]; + if (self) { + _fontCache = [[NSCache alloc] init]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didChangeContentSizeCategory) + name:UIContentSizeCategoryDidChangeNotification + object:nil]; + } + return self; +} + +- (void)didChangeContentSizeCategory { + [_fontCache removeAllObjects]; +} + +- (nullable UIFont *)lightFontOfSize:(CGFloat)fontSize { + NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; + UIFont *font = [self.fontCache objectForKey:cacheKey]; + if (font) { + return font; + } + + font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightLight]; + if (font) { + [self.fontCache setObject:font forKey:cacheKey]; + } + return font; +} + +- (UIFont *)regularFontOfSize:(CGFloat)fontSize { + NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; + UIFont *font = [self.fontCache objectForKey:cacheKey]; + if (font) { + return font; + } + + font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightRegular]; + [self.fontCache setObject:font forKey:cacheKey]; + + return (UIFont *)font; +} + +- (nullable UIFont *)mediumFontOfSize:(CGFloat)fontSize { + NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; + UIFont *font = [self.fontCache objectForKey:cacheKey]; + if (font) { + return font; + } + + font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightMedium]; + if (font) { + [self.fontCache setObject:font forKey:cacheKey]; + } + return font; +} + +- (UIFont *)boldFontOfSize:(CGFloat)fontSize { + NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; + UIFont *font = [self.fontCache objectForKey:cacheKey]; + if (font) { + return font; + } + + font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightSemibold]; + + [self.fontCache setObject:font forKey:cacheKey]; + + return font; +} + +- (UIFont *)italicFontOfSize:(CGFloat)fontSize { + NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; + UIFont *font = [self.fontCache objectForKey:cacheKey]; + if (font) { + return font; + } + + font = [UIFont italicSystemFontOfSize:fontSize]; + + [self.fontCache setObject:font forKey:cacheKey]; + + return font; +} + +- (nullable UIFont *)boldItalicFontOfSize:(CGFloat)fontSize { + NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; + UIFont *font = [self.fontCache objectForKey:cacheKey]; + if (font) { + return font; + } + + UIFont *regular = [self regularFontOfSize:fontSize]; + UIFontDescriptor *_Nullable descriptor = [regular.fontDescriptor + fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic]; + if (!descriptor) { + return nil; + } + UIFontDescriptor *nonnullDescriptor = descriptor; + font = [UIFont fontWithDescriptor:nonnullDescriptor size:fontSize]; + + [self.fontCache setObject:font forKey:cacheKey]; + + return font; +} + +- (BOOL)isLargeForContrastRatios:(UIFont *)font { + if (font.pointSize >= 18) { + return YES; + } + if (font.pointSize < 14) { + return NO; + } + + UIFontDescriptor *fontDescriptor = font.fontDescriptor; + if ((fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) == UIFontDescriptorTraitBold) { + return YES; + } + + // We treat system font medium as large for accessibility when larger than 14. + if (font.mdc_weight >= UIFontWeightMedium) { + return YES; + } + + return NO; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h new file mode 100644 index 00000000..94fe5bb4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h @@ -0,0 +1,30 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + The Typography component provides methods for getting sized fonts and opacities following Material + style guidelines. + + This header is the umbrella header for the component and should be imported by consumers of the + Typography component. Please do not directly import other headers. This will allow the componet to + expand or contract the header file space without consumer modifications. + */ + +#import "MDCFontScaler.h" +#import "MDCFontTextStyle.h" +#import "MDCTypography.h" +#import "UIFont+MaterialScalable.h" +#import "UIFont+MaterialSimpleEquality.h" +#import "UIFont+MaterialTypography.h" +#import "UIFontDescriptor+MaterialTypography.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h new file mode 100644 index 00000000..f432bf18 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h @@ -0,0 +1,88 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCFontTextStyle.h" + +/** + A representation of a mapping of UIContentSizeCategory keys to font size values. + + The values of this dictionary are CGFloat values represented as an NSNumber. Each value defines the + font size to be used for a given content size category. + */ +typedef NSDictionary *MDCScalingCurve; + +@interface UIFont (MaterialScalable) + +/** + A custom scaling curve to be used when scaling this font for Dynamic Type. + + The keys of a scaling curve MUST include the complete set of UIContentSizeCategory values, from + UIContentSizeCategoryExtraSmall to UIContentSizeCategoryExtraExtraExtraLarge AND all + UIContentSizeCategoryAccessibility categories. If any of these keys are missing then any scaling + behavior that reads from this property is undefined. + */ +@property(nonatomic, copy, nullable, setter=mdc_setScalingCurve:) MDCScalingCurve mdc_scalingCurve; + +/** + Returns a font with the same family, weight and traits, but whose point size is based on the given + size category and the corresponding value from @c mdc_scalingCurve. + + @param sizeCategory The size category for which the font should be scaled. + @return A font whose point size is extracted from @c mdc_scalingCurve for the given size category, + or self if @c mdc_scalingCurve is nil. + */ +- (nonnull UIFont *)mdc_scaledFontForSizeCategory:(nonnull UIContentSizeCategory)sizeCategory; + +/** + Returns a font with the same family, weight and traits, but whose point size is based on the given + trait environment's preferred content size category. + + @param traitEnvironment The trait environment whose trait collection should be queried. + @return A font whose point size is determined by @c mdc_scalingCurve for the given trait + environment's content size category, or self if @c mdc_scalingCurve is nil. + */ +- (nonnull UIFont *)mdc_scaledFontForTraitEnvironment: + (nonnull id)traitEnvironment; + +/** + Returns a font with the same family, weight and traits, but whose point size is based on the + default size category of UIContentSizeCategoryLarge and the corresponding value from + @c mdc_scalingCurve. + + This can be used to return a font for a text element that should *not* be scaled with Dynamic + Type. + + @return A font whose point size is extracted from @c mdc_scalingCurve for + UIContentSizeCategoryLarge, or self if @c mdc_scalingCurve is nil. + */ +- (nonnull UIFont *)mdc_scaledFontAtDefaultSize; + +/** + Returns a font with the same family, weight and traits, but whose point size is based on the + device's current content size category and the corresponding value from @c mdc_scalingCurve. + + @note Prefer @c -mdc_scaledFontForSizeCategory: because it encourages use of trait collections + instead. + + @return If @c mdc_scalingCurve is nil, returns self. On iOS 10 and above, returns a font whose + point size is extracted from @c mdc_scalingCurve for UIScreen.mainScreen's + preferredContentSizeCategory. On iOS 9, returns a font whose point size is extracted from + @c mdc_scalingCurve for UIApplication.sharedApplication's preferredContentSizeCategory, if a shared + application is available, otherwise uses UIContentSizeCategoryLarge instead. + */ +- (nonnull UIFont *)mdc_scaledFontForCurrentSizeCategory; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m new file mode 100644 index 00000000..54805308 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m @@ -0,0 +1,86 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIFont+MaterialScalable.h" + +#import + +#import "MaterialApplication.h" + +#import "private/MDCTypographyUtilities.h" + +static char MDCFontScaleObjectKey; + +@implementation UIFont (MaterialScalable) + +- (UIFont *)mdc_scaledFontForSizeCategory:(UIContentSizeCategory)sizeCategory { + if (!self.mdc_scalingCurve) { + return self; + } + + NSNumber *fontSizeNumber; + if (sizeCategory) { + // Pick the correct font size from the pre-attached scaling curve that + // fits the specific size category. The scaling curve is attached based on + // the type of font, so a button font has a different scaling curve than + // a headline font, and the two will therefore see different font size numbers + // for the same size category. + fontSizeNumber = self.mdc_scalingCurve[sizeCategory]; + } + + // Guard against broken / incomplete scaling curves by returning self if fontSizeNumber is nil. + if (fontSizeNumber == nil) { + return self; + } + + CGFloat fontSize = (CGFloat)fontSizeNumber.doubleValue; + + // Guard against broken scaling curves encoded with 0.0 or negative values + if (fontSize <= 0.0) { + return self; + } + + UIFont *scaledFont = [UIFont fontWithDescriptor:self.fontDescriptor size:fontSize]; + scaledFont.mdc_scalingCurve = self.mdc_scalingCurve; + + return scaledFont; +} + +- (UIFont *)mdc_scaledFontForTraitEnvironment:(id)traitEnvironment { + UIContentSizeCategory sizeCategory = + traitEnvironment.traitCollection.preferredContentSizeCategory; + return [self mdc_scaledFontForSizeCategory:sizeCategory]; +} + +- (UIFont *)mdc_scaledFontForCurrentSizeCategory { + UIContentSizeCategory currentSizeCategory = GetCurrentSizeCategory(); + + return [self mdc_scaledFontForSizeCategory:currentSizeCategory]; +} + +- (nonnull UIFont *)mdc_scaledFontAtDefaultSize { + return [self mdc_scaledFontForSizeCategory:UIContentSizeCategoryLarge]; +} + +- (NSDictionary *)mdc_scalingCurve { + return (NSDictionary *)objc_getAssociatedObject( + self, &MDCFontScaleObjectKey); +} + +- (void)mdc_setScalingCurve:(NSDictionary *)scalingCurve { + objc_setAssociatedObject(self, &MDCFontScaleObjectKey, scalingCurve, + OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h new file mode 100644 index 00000000..1d4364df --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h @@ -0,0 +1,31 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + This UIFont category provides a shallow equality check. http://www.openradar.appspot.com/28406766 + */ +@interface UIFont (MaterialSimpleEquality) + +/* + Checks simple characteristics: name, weight, pointsize, traits. + + While the actual implementation of UIFont's isEqual: is not known, it is believed that + isSimplyEqual: is more 'shallow' than isEqual:. + */ + +- (BOOL)mdc_isSimplyEqual:(UIFont*)font; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m new file mode 100644 index 00000000..aa4bab0e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m @@ -0,0 +1,28 @@ +// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIFont+MaterialSimpleEquality.h" + +#import "MaterialMath.h" + +@implementation UIFont (MaterialSimpleEquality) + +- (BOOL)mdc_isSimplyEqual:(UIFont *)font { + return [self.fontName isEqualToString:font.fontName] && + MDCCGFloatEqual(self.pointSize, font.pointSize) && + [[self.fontDescriptor objectForKey:UIFontDescriptorFaceAttribute] + isEqual:[font.fontDescriptor objectForKey:UIFontDescriptorFaceAttribute]]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h new file mode 100644 index 00000000..5f59a296 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h @@ -0,0 +1,49 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCFontTextStyle.h" + +@interface UIFont (MaterialTypography) + +/** + Returns an instance of the font associated with the Material text style and scaled based on the + content size category. + + @param style The Material font text style for which to return a font. + @return The font associated with the specified style. + */ ++ (nonnull UIFont *)mdc_preferredFontForMaterialTextStyle:(MDCFontTextStyle)style; + +/** + Returns an instance of the font associated with the Material text style + This font is *not* scaled based on the content size category (Dynamic Type). + + @param style The Material font text style for which to return a font. + @return The font associated with the specified style. + */ ++ (nonnull UIFont *)mdc_standardFontForMaterialTextStyle:(MDCFontTextStyle)style; + +/** + Returns an new instance of the font sized according to the text-style and whether the content + size category (Dynamic Type) should be taken into account. + + @param style The Material font text style that will determine the fontSize of the new font + @param scaled Should the new font be scaled according to the content size category (Dynamic Type) + */ +- (nonnull UIFont *)mdc_fontSizedForMaterialTextStyle:(MDCFontTextStyle)style + scaledForDynamicType:(BOOL)scaled; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m new file mode 100644 index 00000000..6b4ae45e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m @@ -0,0 +1,80 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIFont+MaterialTypography.h" + +#import "MDCTypography.h" +#import "UIFontDescriptor+MaterialTypography.h" + +@implementation UIFont (MaterialTypography) + ++ (UIFont *)mdc_preferredFontForMaterialTextStyle:(MDCFontTextStyle)style { + // Due to the way iOS handles missing glyphs in fonts, we do not support using + // our font loader with Dynamic Type. + id fontLoader = [MDCTypography fontLoader]; + if (![fontLoader isKindOfClass:[MDCSystemFontLoader class]]) { + NSLog(@"MaterialTypography : Custom font loaders are not compatible with Dynamic Type."); + } + + UIFontDescriptor *fontDescriptor = + [UIFontDescriptor mdc_preferredFontDescriptorForMaterialTextStyle:style]; + + // Size is included in the fontDescriptor, so we pass in 0.0 in the parameter. + UIFont *font = [UIFont fontWithDescriptor:fontDescriptor size:0.0]; + + return font; +} + ++ (nonnull UIFont *)mdc_standardFontForMaterialTextStyle:(MDCFontTextStyle)style { + // Caches a font for a specific MDCFontTextStyle value + static NSCache *fontCache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // NOTE: We assume the Font Loader will never change, so the cached fonts are never invalidated. + fontCache = [[NSCache alloc] init]; + }); + + // Due to the way iOS handles missing glyphs in fonts, we do not support using our + // font loader with standardFont. + id fontLoader = [MDCTypography fontLoader]; + if (![fontLoader isKindOfClass:[MDCSystemFontLoader class]]) { + NSLog(@"MaterialTypography : Custom font loaders are not compatible with Dynamic Type."); + } + + UIFont *font = [fontCache objectForKey:@(style)]; + if (!font) { + UIFontDescriptor *fontDescriptor = + [UIFontDescriptor mdc_standardFontDescriptorForMaterialTextStyle:style]; + + // Size is included in the fontDescriptor, so we pass in 0.0 in the parameter. + font = [UIFont fontWithDescriptor:fontDescriptor size:0.0]; + [fontCache setObject:font forKey:@(style)]; + } + + return font; +} + +- (nonnull UIFont *)mdc_fontSizedForMaterialTextStyle:(MDCFontTextStyle)style + scaledForDynamicType:(BOOL)scaled { + UIFontDescriptor *fontDescriptor; + if (scaled) { + fontDescriptor = [UIFontDescriptor mdc_preferredFontDescriptorForMaterialTextStyle:style]; + } else { + fontDescriptor = [UIFontDescriptor mdc_standardFontDescriptorForMaterialTextStyle:style]; + } + + return [self fontWithSize:fontDescriptor.pointSize]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h new file mode 100644 index 00000000..a438f38f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h @@ -0,0 +1,41 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCFontTextStyle.h" + +@interface UIFontDescriptor (MaterialTypography) + +/** + Returns an instance of the font descriptor associated with the Material text style and scaled + based on the content size category. + + @param style The Material font text style for which to return a font descriptor. + @return The font descriptor associated with the specified style. + */ ++ (nonnull UIFontDescriptor *)mdc_preferredFontDescriptorForMaterialTextStyle: + (MDCFontTextStyle)style; + +/** + Returns an instance of the font descriptor associated with the Material text style. + This font descriptor is *not* scaled based on the content size category (Dynamic Type). + + @param style The Material font text style for which to return a font descriptor. + @return The font descriptor associated with the specified style. + */ ++ (nonnull UIFontDescriptor *)mdc_standardFontDescriptorForMaterialTextStyle: + (MDCFontTextStyle)style; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m new file mode 100644 index 00000000..7c86c4b8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m @@ -0,0 +1,83 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIFontDescriptor+MaterialTypography.h" + +#import "MaterialApplication.h" + +#import "private/MDCFontTraits.h" + +@implementation UIFontDescriptor (MaterialTypography) + ++ (nonnull UIFontDescriptor *)mdc_fontDescriptorForMaterialTextStyle:(MDCFontTextStyle)style + sizeCategory:(NSString *)sizeCategory { + // TODO(#1179): We should include our leading and tracking metrics when creating this descriptor. + MDCFontTraits *materialTraits = [MDCFontTraits traitsForTextStyle:style + sizeCategory:sizeCategory]; + + // Store the system font family name to ensure that we load the system font. + // If we do not explicitly include this UIFontDescriptorFamilyAttribute in the + // FontDescriptor the OS will default to Helvetica. On iOS 9+, the Font Family + // changes from San Francisco to San Francisco Display at point size 20. + static NSString *smallSystemFontFamilyName; + static NSString *largeSystemFontFamilyName; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + UIFont *smallSystemFont; + UIFont *largeSystemFont; + smallSystemFont = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular]; + largeSystemFont = [UIFont systemFontOfSize:20 weight:UIFontWeightRegular]; + smallSystemFontFamilyName = [smallSystemFont.familyName copy]; + largeSystemFontFamilyName = [largeSystemFont.familyName copy]; + }); + + NSDictionary *traits = @{UIFontWeightTrait : @(materialTraits.weight)}; + NSString *fontFamily = + materialTraits.pointSize < 19.5 ? smallSystemFontFamilyName : largeSystemFontFamilyName; + NSDictionary *attributes = @{ + UIFontDescriptorSizeAttribute : @(materialTraits.pointSize), + UIFontDescriptorTraitsAttribute : traits, + UIFontDescriptorFamilyAttribute : fontFamily + }; + + UIFontDescriptor *fontDescriptor = [[UIFontDescriptor alloc] initWithFontAttributes:attributes]; + + return fontDescriptor; +} + ++ (nonnull UIFontDescriptor *)mdc_preferredFontDescriptorForMaterialTextStyle: + (MDCFontTextStyle)style { + // iOS' default UIContentSizeCategory is Large. + NSString *sizeCategory = UIContentSizeCategoryLarge; + + // If we are within an application, query the preferredContentSizeCategory. + if ([UIApplication mdc_safeSharedApplication]) { + sizeCategory = [UIApplication mdc_safeSharedApplication].preferredContentSizeCategory; + } else { + sizeCategory = UIScreen.mainScreen.traitCollection.preferredContentSizeCategory; + } + + return [UIFontDescriptor mdc_fontDescriptorForMaterialTextStyle:style sizeCategory:sizeCategory]; +} + ++ (nonnull UIFontDescriptor *)mdc_standardFontDescriptorForMaterialTextStyle: + (MDCFontTextStyle)style { + // iOS' default UIContentSizeCategory is Large. + // Since we don't want to scale with Dynamic Type create the font descriptor based on that. + NSString *sizeCategory = UIContentSizeCategoryLarge; + + return [UIFontDescriptor mdc_fontDescriptorForMaterialTextStyle:style sizeCategory:sizeCategory]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h new file mode 100644 index 00000000..5ac7c75a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h @@ -0,0 +1,60 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MDCFontTextStyle.h" + +/** + Provides a means of storing defining font metrics based on size categories. + + This class is based off Apple recommendation in WWDC 2016 - 803 - Typography and Fonts @ 17:33. + */ +@interface MDCFontTraits : NSObject + +/** + The size to which the font is scaled. + + This value, in points, must be greater than 0.0. + */ +@property(nonatomic, readonly) CGFloat pointSize; + +/** + The weight of the font, specified as a font weight constant. + + For a list of possible values, see "Font Weights” in UIFontDescriptor. Avoid passing an arbitrary + floating-point number for weight, because a font might not include a variant for every weight. + */ +@property(nonatomic, readonly) CGFloat weight; + +/** + The leading value represents additional space between lines of text and is measured in points. + */ +@property(nonatomic, readonly) CGFloat leading; + +/** + The tracking value represents additional horizontal space between glyphs and is measured in points. + */ +@property(nonatomic, readonly) CGFloat tracking; + +/** + @param style MDCFontStyle of font traits being requested. + @param sizeCategory UIContentSizeCategory of the font traits being requested. + + @return Font traits that can be used to initialize a UIFont or UIFontDescriptor. + */ ++ (nonnull MDCFontTraits *)traitsForTextStyle:(MDCFontTextStyle)style + sizeCategory:(nonnull NSString *)sizeCategory; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m new file mode 100644 index 00000000..0598681c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m @@ -0,0 +1,511 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCFontTraits.h" + +static NSDictionary *_body1Traits; +static NSDictionary *_body2Traits; +static NSDictionary *_buttonTraits; +static NSDictionary *_captionTraits; +static NSDictionary *_display1Traits; +static NSDictionary *_display2Traits; +static NSDictionary *_display3Traits; +static NSDictionary *_display4Traits; +static NSDictionary *_headlineTraits; +static NSDictionary *_subheadlineTraits; +static NSDictionary *_titleTraits; + +static NSDictionary *_styleTable; + +@interface MDCFontTraits (MaterialTypographyPrivate) + ++ (instancetype)traitsWithPointSize:(CGFloat)pointSize + weight:(CGFloat)weight + leading:(CGFloat)leading + tracking:(CGFloat)tracking; + +- (instancetype)initWithPointSize:(CGFloat)pointSize + weight:(CGFloat)weight + leading:(CGFloat)leading + tracking:(CGFloat)tracking; + +@end + +@implementation MDCFontTraits + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" ++ (void)initialize { + _body1Traits = @{ + UIContentSizeCategoryExtraSmall : [MDCFontTraits traitsWithPointSize:11 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [MDCFontTraits traitsWithPointSize:12 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [MDCFontTraits traitsWithPointSize:13 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [MDCFontTraits traitsWithPointSize:14 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [MDCFontTraits traitsWithPointSize:16 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : [MDCFontTraits traitsWithPointSize:18 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [MDCFontTraits traitsWithPointSize:20 weight:UIFontWeightRegular leading:0.0 tracking:0.0], + UIContentSizeCategoryAccessibilityMedium : + [MDCFontTraits traitsWithPointSize:25 weight:UIFontWeightRegular leading:0.0 tracking:0.0], + UIContentSizeCategoryAccessibilityLarge : [MDCFontTraits traitsWithPointSize:30 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryAccessibilityExtraLarge : + [MDCFontTraits traitsWithPointSize:37 weight:UIFontWeightRegular leading:0.0 tracking:0.0], + UIContentSizeCategoryAccessibilityExtraExtraLarge : + [MDCFontTraits traitsWithPointSize:44 weight:UIFontWeightRegular leading:0.0 tracking:0.0], + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : + [MDCFontTraits traitsWithPointSize:52 weight:UIFontWeightRegular leading:0.0 tracking:0.0], + }; + + _body2Traits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:11 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:12 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:13 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:14 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:16 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:18 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:20 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryAccessibilityMedium : + [[MDCFontTraits alloc] initWithPointSize:25 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryAccessibilityLarge : + [[MDCFontTraits alloc] initWithPointSize:30 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryAccessibilityExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:37 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryAccessibilityExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:44 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:52 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + }; + + _buttonTraits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:11 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:12 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:13 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:14 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:16 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:18 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:20 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + }; + + _captionTraits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:11 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:11 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:11 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:12 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:14 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:16 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:18 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + }; + + _display1Traits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:28 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:30 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:32 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:34 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:36 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:38 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:40 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + }; + + _display2Traits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:39 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:41 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:43 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:45 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:47 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:49 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:51 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + }; + + _display3Traits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:50 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:52 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:54 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:56 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:58 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:60 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:62 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + }; + + _display4Traits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:100 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:104 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:108 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:112 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:116 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:120 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:124 + weight:UIFontWeightLight + leading:0.0 + tracking:0.0], + }; + + _headlineTraits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:21 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:22 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:23 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:24 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:26 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:28 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:30 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + }; + + _subheadlineTraits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:13 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:14 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:15 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:16 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:18 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:20 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:22 + weight:UIFontWeightRegular + leading:0.0 + tracking:0.0], + }; + + _titleTraits = @{ + UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:17 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:18 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:19 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:20 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:22 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:24 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + UIContentSizeCategoryExtraExtraExtraLarge : + [[MDCFontTraits alloc] initWithPointSize:26 + weight:UIFontWeightMedium + leading:0.0 + tracking:0.0], + }; + + _styleTable = @{ + @(MDCFontTextStyleBody1) : _body1Traits, + @(MDCFontTextStyleBody2) : _body2Traits, + @(MDCFontTextStyleButton) : _buttonTraits, + @(MDCFontTextStyleCaption) : _captionTraits, + @(MDCFontTextStyleDisplay1) : _display1Traits, + @(MDCFontTextStyleDisplay2) : _display2Traits, + @(MDCFontTextStyleDisplay3) : _display3Traits, + @(MDCFontTextStyleDisplay4) : _display4Traits, + @(MDCFontTextStyleHeadline) : _headlineTraits, + @(MDCFontTextStyleSubheadline) : _subheadlineTraits, + @(MDCFontTextStyleTitle) : _titleTraits + }; +} +#pragma clang diagnostic pop + ++ (instancetype)traitsWithPointSize:(CGFloat)pointSize + weight:(CGFloat)weight + leading:(CGFloat)leading + tracking:(CGFloat)tracking { + return [[MDCFontTraits alloc] initWithPointSize:pointSize + weight:weight + leading:leading + tracking:tracking]; +} + +- (instancetype)initWithPointSize:(CGFloat)pointSize + weight:(CGFloat)weight + leading:(CGFloat)leading + tracking:(CGFloat)tracking { + self = [super init]; + if (self) { + _pointSize = pointSize; + _weight = weight; + _leading = leading; + _tracking = tracking; + } + + return self; +} + ++ (MDCFontTraits *)traitsForTextStyle:(MDCFontTextStyle)style + sizeCategory:(NSString *)sizeCategory { + NSDictionary *traitsTable = _styleTable[@(style)]; + NSCAssert(traitsTable, @"traitsTable cannot be nil. Is style valid?"); + + MDCFontTraits *traits; + if (traitsTable) { + if (sizeCategory) { + traits = traitsTable[sizeCategory]; + } + + // If you have queried the table for a sizeCategory that doesn't exist, we will return the + // traits for XXXL. This handles the case where the values are requested for one of the + // accessibility size categories beyond XXXL such as + // UIContentSizeCategoryAccessibilityExtraLarge. Accessbility size categories are only + // defined for the Body Font Style. + if (traits == nil) { + traits = traitsTable[UIContentSizeCategoryExtraExtraExtraLarge]; + } + } + + return traits; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h new file mode 100644 index 00000000..6fa1d139 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h @@ -0,0 +1,17 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +UIContentSizeCategory GetCurrentSizeCategory(void); diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m new file mode 100644 index 00000000..4210d676 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m @@ -0,0 +1,25 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCTypographyUtilities.h" + +#import "MaterialApplication.h" + +/** + @return Device's current UIContentSizeCategory or UIContentSizeCategoryLarge + if we are unable to query the device due to being in an extension. + */ +UIContentSizeCategory GetCurrentSizeCategory(void) { + return UIScreen.mainScreen.traitCollection.preferredContentSizeCategory; +} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h new file mode 100644 index 00000000..7d3c1860 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h @@ -0,0 +1,42 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface UIFont (MaterialTypographyPrivate) + +/** + Returns the weight of the font. + + @return A value between -1.0 (very thin) to 1.0 (very thick). + + Regular weight is 0.0. + */ +- (CGFloat)mdc_weight; + +/** + Returns the slant of the font. + + @return more than 0 when italic and 0 when not italic. + + Regular slant is 0.0. + */ +- (CGFloat)mdc_slant; + +/** + Returns an extended description of the font including name, pointsize and weight. + */ +- (nonnull NSString *)mdc_extendedDescription; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m new file mode 100644 index 00000000..f29c7517 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m @@ -0,0 +1,113 @@ +// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIFont+MaterialTypographyPrivate.h" + +#import "UIFont+MaterialScalable.h" + +@implementation UIFont (MaterialTypographyPrivate) + +/* + Returns a string indicating the weight of the font. These weights were added in iOS 8.2. + */ ++ (NSString *)mdc_fontWeightDescription:(CGFloat)weight { + // The UIFontWeight enumeration was added in iOS 8.2 + NSString *description = [NSString stringWithFormat:@"(%.3f)", weight]; +#if defined(__IPHONE_8_2) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" + if (&UIFontWeightMedium != NULL) { + if (weight == UIFontWeightUltraLight) { + return @"UltraLight"; + } else if (weight == UIFontWeightThin) { + return @"Thin"; + } else if (weight == UIFontWeightLight) { + return @"Light"; + } else if (weight == UIFontWeightRegular) { + return @"Regular"; + } else if (weight == UIFontWeightMedium) { + return @"Medium"; + } else if (weight == UIFontWeightSemibold) { + return @"Semibold"; + } else if (weight == UIFontWeightBold) { + return @"Bold"; + } else if (weight == UIFontWeightHeavy) { + return @"Heavy"; + } else if (weight == UIFontWeightBlack) { + return @"Black"; + } else { + return description; + } + } else { + return description; + } +#pragma clang diagnostic pop +#else + return description; +#endif +} + +- (CGFloat)mdc_weight { + // The default font weight is UIFontWeightRegular, which is 0.0. + CGFloat weight = 0.0; + + NSDictionary *fontTraits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; + if (fontTraits) { + NSNumber *weightNumber = fontTraits[UIFontWeightTrait]; + if (weightNumber != nil) { + weight = [weightNumber floatValue]; + } + } + + return weight; +} + +- (CGFloat)mdc_slant { + CGFloat slant = 0.0; + + NSDictionary *fontTraits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; + if (fontTraits) { + NSNumber *slantNumber = fontTraits[UIFontSlantTrait]; + if (slantNumber != nil) { + slant = [slantNumber floatValue]; + } + } + + return slant; +} + +- (NSString *)mdc_weightString { + CGFloat weight = [self mdc_weight]; + NSString *weightString = [UIFont mdc_fontWeightDescription:weight]; + + return weightString; +} + +- (NSString *)mdc_extendedDescription { + NSMutableString *extendedDescription = [[super description] mutableCopy]; + [extendedDescription appendFormat:@"%@ : ", self.fontName]; + [extendedDescription appendFormat:@"%@ : ", self.familyName]; + [extendedDescription appendFormat:@"%.1f pt : ", self.pointSize]; + [extendedDescription appendFormat:@"%@ : ", [self mdc_weightString]]; + if (self.mdc_scalingCurve) { + [extendedDescription appendString:@"+ScalingCurve"]; + } else { + [extendedDescription appendString:@"NoScalingCurve"]; + } + + return extendedDescription; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h new file mode 100644 index 00000000..346a5d70 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h @@ -0,0 +1,14 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#import "UIApplication+MDCAppExtensions.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h new file mode 100644 index 00000000..75ddca1a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h @@ -0,0 +1,34 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + UIApplication extension for working with sharedApplication inside of app extensions. + */ +@interface UIApplication (MDCAppExtensions) + +/** + Returns sharedApplication if it is available otherwise returns nil. + + This is a wrapper around sharedApplication which is safe to compile and use in app extensions. + */ ++ (UIApplication *)mdc_safeSharedApplication; + +/** + Returns YES if called inside an application extension otherwise returns NO. + */ ++ (BOOL)mdc_isAppExtension; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m new file mode 100644 index 00000000..a6482278 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m @@ -0,0 +1,41 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIApplication+MDCAppExtensions.h" + +@implementation UIApplication (MDCAppExtensions) + ++ (UIApplication *)mdc_safeSharedApplication { + static UIApplication *application; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (![self mdc_isAppExtension]) { + // We can't call sharedApplication directly or else this won't build for app extensions. + application = [[UIApplication class] performSelector:@selector(sharedApplication)]; + } + }); + return application; +} + ++ (BOOL)mdc_isAppExtension { + static BOOL isAppExtension; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + isAppExtension = + [[[NSBundle mainBundle] executablePath] rangeOfString:@".appex/"].location != NSNotFound; + }); + return isAppExtension; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h new file mode 100644 index 00000000..eb5514a4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h @@ -0,0 +1,16 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIColor+MaterialBlending.h" +#import "UIColor+MaterialDynamic.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h new file mode 100644 index 00000000..4c458665 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h @@ -0,0 +1,29 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface UIColor (MaterialBlending) + +/** + Blending a color over a background color using Alpha compositing technique. + More info about Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing + + @param color UIColor value that sits on top. + @param backgroundColor UIColor on the background. + */ ++ (nonnull UIColor *)mdc_blendColor:(nonnull UIColor *)color + withBackgroundColor:(nonnull UIColor *)backgroundColor; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m new file mode 100644 index 00000000..1cfa186c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m @@ -0,0 +1,45 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIColor+MaterialBlending.h" + +/** + Helper method to blend a color channel with a background color channel using alpha composition. + More info about Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing + + @params value is the value of color channel + @params bValue is the value of background color channel + @params alpha is the alpha of color channel + @params bAlpha is the alpha of background color channel + */ + +static CGFloat blendColorChannel(CGFloat value, CGFloat bValue, CGFloat alpha, CGFloat bAlpha) { + return ((1 - alpha) * bValue * bAlpha + alpha * value) / (alpha + bAlpha * (1 - alpha)); +} + +@implementation UIColor (MaterialBlending) + ++ (UIColor *)mdc_blendColor:(UIColor *)color withBackgroundColor:(UIColor *)backgroundColor { + CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + CGFloat bRed = 0.0, bGreen = 0.0, bBlue = 0.0, bAlpha = 0.0; + [backgroundColor getRed:&bRed green:&bGreen blue:&bBlue alpha:&bAlpha]; + + return [UIColor colorWithRed:blendColorChannel(red, bRed, alpha, bAlpha) + green:blendColorChannel(green, bGreen, alpha, bAlpha) + blue:blendColorChannel(blue, bBlue, alpha, bAlpha) + alpha:alpha + bAlpha * (1 - alpha)]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h new file mode 100644 index 00000000..54c39184 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h @@ -0,0 +1,56 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface UIColor (MaterialDynamic) + +/// Returns a color object that picks its value from given color objects dynamically +/// based on currently active traits. For pre iOS 13, this method returns the default +/// color object. +/// +/// @param darkColor A color object returned when @c userInterfaceStyle is @c +/// UIUserInterfaceStyleDark based on currently active traits. +/// @param defaultColor A default color object. ++ (nonnull UIColor *)colorWithUserInterfaceStyleDarkColor:(nonnull UIColor *)darkColor + defaultColor:(nonnull UIColor *)defaultColor; + +/** + Returns a dynamic color object that resolves to high contrast color when current + @c accessibilityContrast is @c UIAccessibilityContrastHigh, and to normal contrast color + when @c accessibilityContrast is @c UIAccessibilityContrastNormal. + + The high contrast color and normal contrast color can be dynamic color objects. This method can + be used together with @c colorWithUserInterfaceStyleDarkColor:defaultColor:. + + For pre iOS 13, this method always returns the normal contrast color. + + @param highContrastColor A color object for high contrast color. + @param normalContrastColor A default color object for normal contrast color. + */ ++ (nonnull UIColor *)colorWithAccessibilityContrastHigh:(nonnull UIColor *)highContrastColor + normal:(nonnull UIColor *)normalContrastColor; + +/** + Returns the version of the current color that takes the specified traits into account. + + @note On pre-iOS 13 the orginal color is returned. + + @param traitCollection The traits to use when resolving the color information. + @return The version of the color to display for the specified traits. + */ +- (nonnull UIColor *)mdc_resolvedColorWithTraitCollection: + (nonnull UITraitCollection *)traitCollection; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m new file mode 100644 index 00000000..d3d71190 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m @@ -0,0 +1,73 @@ +// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "UIColor+MaterialDynamic.h" + +#import "MaterialAvailability.h" + +@implementation UIColor (MaterialDynamic) + ++ (UIColor *)colorWithUserInterfaceStyleDarkColor:(UIColor *)darkColor + defaultColor:(UIColor *)defaultColor { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + return [UIColor + colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull traitCollection) { + if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + return darkColor; + } else { + return defaultColor; + } + }]; + } else { + return defaultColor; + } +#else + return defaultColor; +#endif // MDC_AVAILABLE_SDK_IOS(13_0) +} + ++ (UIColor *)colorWithAccessibilityContrastHigh:(UIColor *)highContrastColor + normal:(UIColor *)normalContrastColor { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + return [UIColor + colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull traitCollection) { + if (traitCollection.accessibilityContrast == UIAccessibilityContrastHigh) { + return highContrastColor; + } else { + return normalContrastColor; + } + }]; + } else { + return normalContrastColor; + } +#else + return normalContrastColor; +#endif // MDC_AVAILABLE_SDK_IOS(13_0) +} + +- (UIColor *)mdc_resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection { +#if MDC_AVAILABLE_SDK_IOS(13_0) + if (@available(iOS 13.0, *)) { + return [self resolvedColorWithTraitCollection:traitCollection]; + } else { + return self; + } +#else + return self; +#endif // MDC_AVAILABLE_SDK_IOS(13_0) +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h new file mode 100644 index 00000000..818207ba --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h @@ -0,0 +1,30 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "MaterialIcons.h" + +// This file was automatically generated by running ./scripts/sync_icons.sh +// Do not modify directly. + +@interface MDCIcons (ic_check_circle) + +/* + Returns the image for the ic_check_circle image contained in + MaterialIcons_ic_check_circle.bundle. + */ ++ (nullable UIImage *)imageFor_ic_check_circle; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m new file mode 100644 index 00000000..77176bc9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m @@ -0,0 +1,34 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file was automatically generated by running ./scripts/sync_icons.sh +// Do not modify directly. + +#import "MaterialIcons+ic_check_circle.h" + +static NSString *const kBundleName = @"MaterialIcons_ic_check_circle"; +static NSString *const kIconName = @"ic_check_circle"; + +// Export a nonsense symbol to suppress a libtool warning when this is linked alone in a static lib. +__attribute__((visibility("default"))) char MDCIconsExportToSuppressLibToolWarning_ic_check_circle = + 0; + +@implementation MDCIcons (ic_check_circle) + ++ (nullable UIImage *)imageFor_ic_check_circle { + NSBundle *bundle = [self bundleNamed:kBundleName]; + return [UIImage imageNamed:kIconName inBundle:bundle compatibleWithTraitCollection:nil]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json new file mode 100644 index 00000000..2d92bd53 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json new file mode 100644 index 00000000..1143a92d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_check_circle.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_check_circle@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_check_circle@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..a2caa18db2b2f00f890d119183e78093314e7a64 GIT binary patch literal 260 zcmV+f0sH=mP)ayb&5+IG8{X5M=Wfq|%hM?=*;Bc- zRdy^W5Hsb1x}BNelK4Qu)+BmN4-)z_vHXh=V+;zHQ2*qG72-s;V0l;PKM->Z_K$VM z)`DtpLtPv#xFN)A6MjNnT>D@JP5Sn93+$35U#>0=7VOX<@pQ4VV1WjSr;C{dh4Ucs z^hyR}f*x!{$NH}RP7f(gcq8F5=Hz@I5kqFqmLu1;0~=;!p3EOL#1#p?D_&0k0000< KMNUMnLSTXkp==BQ literal 0 HcmV?d00001 diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..86bf38e98fd6cd4917a9a05f1627b4cd00bc95d4 GIT binary patch literal 493 zcmVM}5-5a(K1E3IIb76@&*2A@3Ka?2H4rk8_6a0`E`;ve`T*h!RCH0s zX}8X7{@uyIb^g5{ZiJlQMmOg$&b_i1ybw{R$1^iha&l5;JkjNvh+y7w%MvNsf*UUI z<_(YJh(0o6PVu0{AH;GinmBRD7sPQfRV;bMH^gIOYFKcn>-B`qQ+!4O_J9s8ByjKa zgZbA>`kmqCbHWLwJbLP11O3hS`|?|xd+RAA)||7I+aP`M8HSh;v2@n|*s2%iR4}Lq z>x;X>ptnQ4(81t&hk9Xz!3?3ek4zN(k*XJ_7^FyH?|~yGo_Zm{pm+y90GJ7Dy^!gF zK9nSWYI;48B6`9>FC_X)HWP~8_9Z_dYQj#h9MdkMCS3GF8-r^^O}OZVOAI0wdcr~f ziwc;eA(g)CGAuU6=+00000NkvXXu0mjf+)>#> literal 0 HcmV?d00001 diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c2ddcd5f3474304cb73cf39be7d6f5deff012b9 GIT binary patch literal 709 zcmV;$0y_PPP)E9`6SZ1GRcK*-cVL!j)!@?JaXJ)yvnwpx@j4@A+V@j0q@hMZ{m>lzr z(oTXh%uv8b>a4;6)7-(iA-1_jELUZVEY1vbh%~SQgQ)A_3ld}Nbff4QS4hlVGJ%R# zJ|Ie4CPhr0wa@A~C3<;|1ojb2!%lJlULb*Gv2y4r2Vfy^vX6x!hdBU?fzttuww>m{ zQs8_OgACUR<_J|h>Y8z#Q?n`RDkPjE>`_M=Kj9qVH*JI(bD0ym5kk$Ae2y?js2mcq zD`z>vDxr=;e)5us{B)Ef91*I7P`d>H9&qj?M<^1ijMVHE|MYS0AV;_$RPCi-.h header and calling + [MDCIcons pathFor_] to get the icon's file system path or calling + [MDCIcons imageFor_] to get a cached image. + */ +@interface MDCIcons (BundleLoader) + +/** Returns a disk path for an icon contained within a bundle of a given name. */ ++ (nonnull NSString *)pathForIconName:(nonnull NSString *)iconName + withBundleName:(nonnull NSString *)bundleName; + ++ (nullable NSBundle *)bundleNamed:(nonnull NSString *)bundleName; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h new file mode 100644 index 00000000..c2e9da62 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h @@ -0,0 +1,29 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + The MDCIcons class is designed to be extended by adding individual icon extensions to a project. + + Individual icons can be accessed by importing their MaterialIcons+.h header and calling + [MDCIcons pathFor_] to get the icon's file system path or calling + [MDCIcons imageFor_] to get a cached image. + */ +@interface MDCIcons : NSObject + +// This object is not intended to be instantiated. +- (instancetype)init NS_UNAVAILABLE; + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m new file mode 100644 index 00000000..aa8179d4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m @@ -0,0 +1,47 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCIcons.h" + +#import "MDCIcons+BundleLoader.h" + +@implementation MDCIcons + ++ (nullable NSBundle *)bundleNamed:(nonnull NSString *)bundleName { + static NSCache *bundleCache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bundleCache = [[NSCache alloc] init]; + }); + + NSBundle *bundle = [bundleCache objectForKey:bundleName]; + if (!bundle) { + NSBundle *baseBundle = [NSBundle bundleForClass:[MDCIcons class]]; + NSString *bundlePath = [baseBundle pathForResource:bundleName ofType:@"bundle"]; + bundle = [NSBundle bundleWithPath:bundlePath]; + if (bundle) { + [bundleCache setObject:bundle forKey:bundleName]; + } + } + return bundle; +} + ++ (nonnull NSString *)pathForIconName:(nonnull NSString *)iconName + withBundleName:(nonnull NSString *)bundleName { + NSBundle *bundle = [self bundleNamed:bundleName]; + NSAssert(bundle, @"Missing bundle %@ containing icon %@.", bundleName, iconName); + return [bundle pathForResource:iconName ofType:@"png"]; +} + +@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h new file mode 100644 index 00000000..f527b7b0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h @@ -0,0 +1,16 @@ +// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCIcons+BundleLoader.h" +#import "MDCIcons.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h new file mode 100644 index 00000000..b12f0c65 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h @@ -0,0 +1,256 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import +#import +#import + +__deprecated_msg("Use sin instead.") static inline CGFloat MDCSin(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return sin(value); +#else + return sinf(value); +#endif +} + +__deprecated_msg("Use cos instead.") static inline CGFloat MDCCos(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return cos(value); +#else + return cosf(value); +#endif +} + +__deprecated_msg("Use atan2 instead.") static inline CGFloat MDCAtan2(CGFloat y, CGFloat x) { +#if CGFLOAT_IS_DOUBLE + return atan2(y, x); +#else + return atan2f(y, x); +#endif +} + +__deprecated_msg("Use ceil instead.") static inline CGFloat MDCCeil(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return ceil(value); +#else + return ceilf(value); +#endif +} + +__deprecated_msg("Use fabs instead.") static inline CGFloat MDCFabs(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return fabs(value); +#else + return fabsf(value); +#endif +} + +static inline CGFloat MDCDegreesToRadians(CGFloat degrees) { +#if CGFLOAT_IS_DOUBLE + return degrees * (CGFloat)M_PI / 180.0; +#else + return degrees * (CGFloat)M_PI / 180; +#endif +} + +static inline BOOL MDCCGFloatEqual(CGFloat a, CGFloat b) { + const CGFloat constantK = 3; +#if CGFLOAT_IS_DOUBLE + const CGFloat epsilon = DBL_EPSILON; + const CGFloat min = DBL_MIN; +#else + const CGFloat epsilon = FLT_EPSILON; + const CGFloat min = FLT_MIN; +#endif + return (fabs(a - b) < constantK * epsilon * fabs(a + b) || fabs(a - b) < min); +} + +__deprecated_msg("Use floor instead.") static inline CGFloat MDCFloor(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return floor(value); +#else + return floorf(value); +#endif +} + +__deprecated_msg("Use hypot instead.") static inline CGFloat MDCHypot(CGFloat x, CGFloat y) { +#if CGFLOAT_IS_DOUBLE + return hypot(x, y); +#else + return hypotf(x, y); +#endif +} + +// Checks whether the provided floating point number is exactly zero. +static inline BOOL MDCCGFloatIsExactlyZero(CGFloat value) { + return (value == 0); +} + +__deprecated_msg("Use pow instead.") static inline CGFloat MDCPow(CGFloat value, CGFloat power) { +#if CGFLOAT_IS_DOUBLE + return pow(value, power); +#else + return powf(value, power); +#endif +} + +__deprecated_msg("Use rint instead.") static inline CGFloat MDCRint(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return rint(value); +#else + return rintf(value); +#endif +} + +__deprecated_msg("Use round instead.") static inline CGFloat MDCRound(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return round(value); +#else + return roundf(value); +#endif +} + +__deprecated_msg("Use sqrt instead.") static inline CGFloat MDCSqrt(CGFloat value) { +#if CGFLOAT_IS_DOUBLE + return sqrt(value); +#else + return sqrtf(value); +#endif +} + +/** + Round the given value to ceiling with provided scale factor. + If @c scale is zero, then the rounded value will be zero. + + @param value The value to round + @param scale The scale factor + @return The ceiling value calculated using the provided scale factor + */ +static inline CGFloat MDCCeilScaled(CGFloat value, CGFloat scale) { + if (MDCCGFloatEqual(scale, 0)) { + return 0; + } + + return ceil(value * scale) / scale; +} + +/** + Round the given value to floor with provided scale factor. + If @c scale is zero, then the rounded value will be zero. + + @param value The value to round + @param scale The scale factor + @return The floor value calculated using the provided scale factor + */ +static inline CGFloat MDCFloorScaled(CGFloat value, CGFloat scale) { + if (MDCCGFloatEqual(scale, 0)) { + return 0; + } + + return floor(value * scale) / scale; +} + +/** + Expand `rect' to the smallest standardized rect containing it with pixel-aligned origin and size. + If @c scale is zero, then a scale of 1 will be used instead. + + @param rect the rectangle to align. + @param scale the scale factor to use for pixel alignment. + + @return the input rectangle aligned to the nearest pixels using the provided scale factor. + + @see CGRectIntegral + */ +static inline CGRect MDCRectAlignToScale(CGRect rect, CGFloat scale) { + if (CGRectIsNull(rect)) { + return CGRectNull; + } + if (MDCCGFloatEqual(scale, 0)) { + scale = 1; + } + + if (MDCCGFloatEqual(scale, 1)) { + return CGRectIntegral(rect); + } + + CGPoint originalMinimumPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)); + CGPoint newOrigin = CGPointMake(floor(originalMinimumPoint.x * scale) / scale, + floor(originalMinimumPoint.y * scale) / scale); + CGSize adjustWidthHeight = + CGSizeMake(originalMinimumPoint.x - newOrigin.x, originalMinimumPoint.y - newOrigin.y); + return CGRectMake(newOrigin.x, newOrigin.y, + ceil((CGRectGetWidth(rect) + adjustWidthHeight.width) * scale) / scale, + ceil((CGRectGetHeight(rect) + adjustWidthHeight.height) * scale) / scale); +} + +static inline CGPoint MDCPointRoundWithScale(CGPoint point, CGFloat scale) { + if (MDCCGFloatEqual(scale, 0)) { + return CGPointZero; + } + + return CGPointMake(round(point.x * scale) / scale, round(point.y * scale) / scale); +} + +/** + Expand `size' to the closest larger pixel-aligned value. + If @c scale is zero, then a CGSizeZero will be returned. + + @param size the size to align. + @param scale the scale factor to use for pixel alignment. + + @return the size aligned to the closest larger pixel-aligned value using the provided scale factor. + */ +static inline CGSize MDCSizeCeilWithScale(CGSize size, CGFloat scale) { + if (MDCCGFloatEqual(scale, 0)) { + return CGSizeZero; + } + + return CGSizeMake(ceil(size.width * scale) / scale, ceil(size.height * scale) / scale); +} + +/** + Align the centerPoint of a view so that its origin is pixel-aligned to the nearest pixel. + Returns @c CGRectZero if @c scale is zero or @c bounds is @c CGRectNull. + + @param center the unaligned center of the view. + @param bounds the bounds of the view. + @param scale the native scaling factor for pixel alignment. + + @return the center point of the view such that its origin will be pixel-aligned. + */ +static inline CGPoint MDCRoundCenterWithBoundsAndScale(CGPoint center, + CGRect bounds, + CGFloat scale) { + if (MDCCGFloatEqual(scale, 0) || CGRectIsNull(bounds)) { + return CGPointZero; + } + + CGFloat halfWidth = CGRectGetWidth(bounds) / 2; + CGFloat halfHeight = CGRectGetHeight(bounds) / 2; + CGPoint origin = CGPointMake(center.x - halfWidth, center.y - halfHeight); + origin = MDCPointRoundWithScale(origin, scale); + return CGPointMake(origin.x + halfWidth, origin.y + halfHeight); +} + +/// Compare two edge insets using MDCCGFloatEqual. +/// @param insets1 An edge inset to compare with insets2 +/// @param insets2 An edge inset to compare with insets1 +static inline BOOL MDCEdgeInsetsEqualToEdgeInsets(UIEdgeInsets insets1, UIEdgeInsets insets2) { + BOOL topEqual = MDCCGFloatEqual(insets1.top, insets2.top); + BOOL leftEqual = MDCCGFloatEqual(insets1.left, insets2.left); + BOOL bottomEqual = MDCCGFloatEqual(insets1.bottom, insets2.bottom); + BOOL rightEqual = MDCCGFloatEqual(insets1.right, insets2.right); + return topEqual && leftEqual && bottomEqual && rightEqual; +} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h new file mode 100644 index 00000000..94b26d64 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h @@ -0,0 +1,15 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "MDCMath.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m new file mode 100644 index 00000000..75cef75c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m @@ -0,0 +1,17 @@ +// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + This file exists to keep pod lib lint passing + */ diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 00000000..3d8bd023 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,3465 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 002C20BF2F798D2C64290D641171C7F3 /* MaterialShapeLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 90A761C8F692CCA4FDA5BFC5DEBACE74 /* MaterialShapeLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 004D58D25F2EB03B40ADB43039641BC0 /* AnimationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708C0285547318992A3989A6415E5D6B /* AnimationContext.swift */; }; + 023569941EF2F377A6F87A87062B479A /* MDFColorCalculations.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CCE660C00123EB989E5679091A134A /* MDFColorCalculations.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 02621C4B82398D0657F474E21493A3A2 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = C27430E91F40A49F302EA2C974453696 /* HTTPMethod.swift */; }; + 02DB462B121245593CE653B9B377F970 /* Protected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D70DCDE236059947C0BF795B512C84 /* Protected.swift */; }; + 02E41CCFF8E90ACE9B3F5267F6FE3DB7 /* MaterialCards.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DCF5983D0F4C2FB0E19A0D320B789B /* MaterialCards.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 03219B99BEEC9AB447188784DFDF4E19 /* UIImage+MaterialRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE016D373F57DCED6E20186B20A6596 /* UIImage+MaterialRTL.m */; }; + 048E7F185D045CEAB317FF30D739E39C /* MDCInkLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 92BF3EBBBF215FDB97014A1C7F674282 /* MDCInkLayer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 04FBE0F7CABE3A8D6913CC677B5557DB /* MDCLegacyInkLayer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 16900192B6F1F880A2BEEE4A15228EE3 /* MDCLegacyInkLayer+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0505586ED69AFD180618E639E5526C63 /* AnimationViewInitializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6966E76E2E05FEC74DB6972B4F38FCC /* AnimationViewInitializers.swift */; }; + 059F26929B3EB05770116463CC9DE9BD /* PathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC6506BF9A73061A58868C0D592B9B1 /* PathNode.swift */; }; + 06894D1A68123F284D70772314BBC171 /* FillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A07731940B92711A5CA2D9CF2C7DA38F /* FillRenderer.swift */; }; + 0711A9391B98D049C553FEF13D38C1AD /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6557B599662265675CB317D7A3AA35 /* Animation.swift */; }; + 0716AAD8964E0F60518EF92178614471 /* TextAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75A55C9E6101E233016AB66DD634B926 /* TextAnimator.swift */; }; + 0828664E6967E49C11D8F03D40827F40 /* MDCShadowElevations.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BA153CC755CD4895140457FB4274997 /* MDCShadowElevations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 092666672EA5B706BA5DAEAE9B49444F /* NodeProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55C5A1F93EFADC2096EE6DC9303B38A8 /* NodeProperty.swift */; }; + 0A4178C93B7F1DF931BF92BC4D971841 /* Vectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = A422591030AC5B802703CAAFA3536ED3 /* Vectors.swift */; }; + 0B399DCF32F8FE4F09B03B6E7B65E0D1 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1742EDF4DDCBB582B88771AEB1201E1A /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0B6124DD33313BE9E4A71126F8F3DB75 /* UIFont+MaterialScalable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE15CF0E1981C1CA722C1B5121C088C /* UIFont+MaterialScalable.m */; }; + 0C7FEE5AB0CD94782BD549DCF9974205 /* MDFImageCalculations.m in Sources */ = {isa = PBXBuildFile; fileRef = 408FEF5380BBEF2A9A4EF9878452691D /* MDFImageCalculations.m */; }; + 0D9E44B0B2AC6D75ACB5D1D1B8EB6090 /* MDCFloatingButton+Animation.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F1688277322547EA5D6CCDF2C94C1F /* MDCFloatingButton+Animation.m */; }; + 0E5AF92BC5AD820D750CDDE0DDC887D2 /* Pods-ios-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 100C94E08EE07B09E19F115713E5B184 /* Pods-ios-dummy.m */; }; + 0E97277DFF7EA15585CCD7FD4F881799 /* KeyedDecodingContainerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111FAD1AC980B2D67B27016C087B61ED /* KeyedDecodingContainerExtensions.swift */; }; + 0EDBF34C06554E9537C4DFCBAF24433E /* MDCRectangleShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 909F969A28FE09D5039EF1A274202B6D /* MDCRectangleShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0F1B88139EBAE43C4D04C4D238AE8F51 /* MDCLegacyInkLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 600ED7A50816D1A3222193C97DEA9BC3 /* MDCLegacyInkLayer.m */; }; + 0F7ADF590B5BBABFEE77D9E562058102 /* MDCRippleTouchController.h in Headers */ = {isa = PBXBuildFile; fileRef = 62A2F94AFB07146E8A9A5DC4985F19BA /* MDCRippleTouchController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0FF000CEEA87B583977819DE456F949B /* UIView+MDCTimingFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = E69B2A401E928B1CBB9CD4B02BEE853D /* UIView+MDCTimingFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1062EDD2C5C3B8D7A91592F639600D74 /* MDFInternationalization.h in Headers */ = {isa = PBXBuildFile; fileRef = B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */; }; + 10F1A59D76699A85A3D3F9CE14D68340 /* MDCTriangleEdgeTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = AC80A65D7F1D3E258FEC205047C3EB0A /* MDCTriangleEdgeTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 114CD9D207171C75415C5B60E0609675 /* Pods-iosTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CEC781B13B51E108C5658001DCCCD2A /* Pods-iosTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 11E325AD2BE51DA7C48EB54B7737B4BF /* UIColor+MaterialBlending.m in Sources */ = {isa = PBXBuildFile; fileRef = 1495FD5410C5BA9748915AE95726D978 /* UIColor+MaterialBlending.m */; }; + 12017489E0E497EC8A12E9418643AA6D /* UIFont+MaterialTypographyPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = FE68F0EF6188796115A2408E20255E7C /* UIFont+MaterialTypographyPrivate.m */; }; + 124EEAE22B8077371D55FDB1AC4F4D30 /* NSString+MaterialBidi.m in Sources */ = {isa = PBXBuildFile; fileRef = BC918096888E65550CADD29286F70E6B /* NSString+MaterialBidi.m */; }; + 12AC4439EB9B96B847691B34BB75656D /* AnimationTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5DF95E38B16446E73BCFAC6F53DA2E /* AnimationTime.swift */; }; + 1378724980581463BB5A2AD8A3B39BC3 /* MDCButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F90CBF2D1339BB6CDDAFF78CC3B59761 /* MDCButton.m */; }; + 138A6746D98577BC9AEFBA7E0DBCE9A7 /* MDCRectangleShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FB83587411063DC53155C1A2C082DD3 /* MDCRectangleShapeGenerator.m */; }; + 13E62623092B680C6A5C349D48B8A4FD /* CachedResponseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748CA1EDB7765D88F678941AA746AA9E /* CachedResponseHandler.swift */; }; + 1773084DECF68CADD45567FBEC56036D /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DF304359AEBCFADD6BBD420523B223F /* Alamofire-dummy.m */; }; + 17CEA056B7594689546EAB74E595D060 /* MDCTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = 5264280FEB93B40117162EB11A934890 /* MDCTypography.m */; }; + 185E3D77C15AC7CE8A4870A909237FCA /* AnimationCacheProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6E10A41E382E72F54FC92EF85F00963 /* AnimationCacheProvider.swift */; }; + 1899682FBADFB9FAE8EBCF416FD2C577 /* PointValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D52B7A10D74C906F9C84CC976B1B2D /* PointValueProvider.swift */; }; + 192B25A167B62AA5C7B878CE91F3582E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + 198CE4B8F3B6704CB4AD916821DC22A1 /* ReplyFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F40483DF444914DE771891BCED1F593 /* ReplyFeedbackGenerator.swift */; }; + 1A3B96F4D5840C14E50F8320D2E81542 /* MDCRippleViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A71810DBABD1DE3268F0A777230A85A2 /* MDCRippleViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1A7D0E6F5105EC32427DBB1983EC27BE /* BezierPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEB21337FEDA85FD4CE4B5534D436F9 /* BezierPath.swift */; }; + 1BA653A4F6BE2F4443363A09C3E99628 /* MDCInkTouchController.m in Sources */ = {isa = PBXBuildFile; fileRef = 02F95682318FB0084CD0FAE9E698A337 /* MDCInkTouchController.m */; }; + 1C3510C6893F019ABE94367A4EE9D07E /* MDCShadow.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B3D295F42CEAEA35B48C43AD477C61 /* MDCShadow.m */; }; + 1C63F278B31BE01E9A399C213588B1F5 /* MDCTypographyUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D638A90314C4C723DF58CBA2D02DCF3 /* MDCTypographyUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1C994289D94896DEAE034CD8A0B4CDF0 /* KeyframeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABFF571A3232481B34A73FDDE0F2B55 /* KeyframeExtensions.swift */; }; + 1D17B83410DC98911D539F2BD5254C05 /* RequestTaskMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6090940C42D19C30699D9DFCD46FEA1 /* RequestTaskMap.swift */; }; + 1D313F4071D041D30364BC379612A7C8 /* MDCEdgeTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E786DB3BAA72871D457F5DC195A08AD /* MDCEdgeTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1DCF9A800AE274F89C29078A6E212B7D /* MDFInternationalization-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B120981A19A9C956A2941EB064D1FD1D /* MDFInternationalization-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1F3D77EAFD74D2CFE5C43D46FD93C6D8 /* VectorsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD410EF9494BB5D4662CBBE59C2C06C /* VectorsExtensions.swift */; }; + 20F80E96D387808DB7427A1249E8EADF /* TextAnimatorNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF0A7BE4E58D6697CCF7D0FA0CBBDFB /* TextAnimatorNode.swift */; }; + 2155754362430305C54CBCC65CC1A1A1 /* MDCPathGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 8246440519C85238FCFCF8F58392A86F /* MDCPathGenerator.m */; }; + 22AA981BFEFAFBA8C6B511348D60D284 /* MaterialComponents-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6741DCC6A7B256467254EDFBBDF05B3F /* MaterialComponents-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 22FFC6C3AAFCD260E0DAB32352B704A9 /* PreCompLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82AB7D6FD755ADF708F94AF789430203 /* PreCompLayerModel.swift */; }; + 24B503B9E52E65303577FE6910BF3D15 /* MDCShadowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 561E46EAC325F966EA9DBA55F0689607 /* MDCShadowLayer.m */; }; + 252655EF3425EC8EF3154477B9B4C3F1 /* ChatItemPresenterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06460E2CD793BBF2C535219D13A50C05 /* ChatItemPresenterFactory.swift */; }; + 2550F0D474DE846FEC5C76CBE85F927E /* OperationQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30734D2F4660366E1C28FA6E2E4345B1 /* OperationQueue+Alamofire.swift */; }; + 25ABCCB506CD51393434BE44F446AD5F /* MDFInternationalization-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F1F807B149E883C56B85C5C055D651D /* MDFInternationalization-dummy.m */; }; + 25C84FA79FF9DCAA88669C3380168F44 /* AnimationTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BEC31BE7157E03DF220C390111D13 /* AnimationTextProvider.swift */; }; + 267ED94E779B638FF06A3796A5DBA7E5 /* ChatCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F96BF2DFC0E11E6A55539B9225C94 /* ChatCollectionViewLayout.swift */; }; + 27004AABBE0496F9D5C7B3D8450F657B /* MaterialButtons.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DA90325405F33A5808A47E8BFFAEE6F /* MaterialButtons.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 274C601104AC3151F7A65A4F15A82F3D /* RenderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E173EE03A996655ED2311253DA58EB9 /* RenderNode.swift */; }; + 2866EB6C78D5E0A3256F868DA33E06DA /* MaterialInk.h in Headers */ = {isa = PBXBuildFile; fileRef = A8AB1E1A488B0F97C034BFD4ECB9C11B /* MaterialInk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2A94738462743EA303248E706CF448BB /* TextLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7639C506BC1DBC0CA831E0E2A106D658 /* TextLayerModel.swift */; }; + 2ADBB4BE80F61C7FF2AC39558549DDC1 /* AnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F4B5DB7011E5176A063CF3110CD09E /* AnimationView.swift */; }; + 2CE76602B51D001D59FCC5CA12BEC37B /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 713A971DE11ED11949FB41259A01995C /* Merge.swift */; }; + 2DC8E9D2FBC9CB113C32F1F2DD4DCA22 /* MaterialComponents-MaterialIcons_ic_check_circle in Resources */ = {isa = PBXBuildFile; fileRef = 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */; }; + 2E8A20C40A0F48D1F8C0806CE740853D /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17C76564686F46F80432DCE2D053940 /* LottieView.swift */; }; + 3064376167F8EFF6F63AEC652D55E9A0 /* MDCPillShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 2102D1FE11630B5091FE865DDD43F05A /* MDCPillShapeGenerator.m */; }; + 30A331CD9286145E92DB11D671664C63 /* MultipartUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7872898EA812A796E27F3664AA901902 /* MultipartUpload.swift */; }; + 31B827F3FA1AB67F3139AA92AA3C9C88 /* BaseChatViewController+CellPanGestureHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F58CC76B154EE1FFCEBFD29458765F /* BaseChatViewController+CellPanGestureHandler.swift */; }; + 32DA6FCE9DD284D13AC2A4CE1259320D /* MDCFloatingButtonModeAnimatorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B239243820382C1C6E7038645E10958 /* MDCFloatingButtonModeAnimatorDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 32E691399B269968E2DD2CAD9F66C4F9 /* NSArray+MDFUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ADBAC831F15371103E6075A7099776A /* NSArray+MDFUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 335CCDCEBD9EB7CB33697888F3E0D6F8 /* AnimationKeypath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4259483CFD0AC1C7F1295B7B354781A1 /* AnimationKeypath.swift */; }; + 336037502C5A263DA6CC3831EF8EFD9F /* MDCPillShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2882BFF487CA5879D2F68C6B944595F5 /* MDCPillShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 33F86FAB918B148A63A1575667F9B570 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 870DC3835055FF27A861C151FF69B97C /* CFNetwork.framework */; }; + 34426DF353C060CB74307404D7788A7F /* MDCInkGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = A67A50DB4E82C2861D15BCA11383D919 /* MDCInkGestureRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34F8E92AE1B47864EB2828243BEA8378 /* MDCInkGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9906975C7ABA6E0912B4362EFF02B32C /* MDCInkGestureRecognizer.m */; }; + 35A265AD5CA62B93ED1BE1780D22B92B /* AnyValueContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE99EB6CC9C88A5D592843A9171EC23B /* AnyValueContainer.swift */; }; + 370A0DE2184C02814935D61032C2E189 /* MDCRippleTouchControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = D6E67B621B39259F3CED1DE2F43E57AE /* MDCRippleTouchControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3730BD7F19450F90CC632D633517DCD8 /* KeypathSearchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FC5A268C4EB4A715BD6C5613A37B5A9 /* KeypathSearchable.swift */; }; + 378706EAE3C525A5A25FD652CF35ED3A /* MDCShadowsCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = C73D5045364BD3202565B4FDB54AE37A /* MDCShadowsCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 37E3D167D051B6E64ACCC71D8111CBE1 /* AnimationKeypathExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F0F84872DE9FA147E94B5562B8FE68 /* AnimationKeypathExtension.swift */; }; + 388951286A46DDEBC11081395CE19424 /* GroupInterpolator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5739897ED45AAC422F0EECBA1D75F58A /* GroupInterpolator.swift */; }; + 388CB67650DEC60578CD193C099F93C7 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E035C3FBB06190E0CE480284B8CF014 /* Group.swift */; }; + 39874777CA1F53BB8BF51F9C0DF90173 /* MDCCurvedRectShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = FEC9B0599DF18776D54CF802CB9FFFD9 /* MDCCurvedRectShapeGenerator.m */; }; + 3A8BFA61B606E1EA5527EF2D18D49D4D /* ShapeTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DBE44442938F2CBE3624C972232C24E /* ShapeTransform.swift */; }; + 3B214DA1F9E0A2F6BAA4DF248BE271AE /* MDCCardCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A035D2874E49F84C6A8FF8FBF6503C8 /* MDCCardCollectionCell.m */; }; + 3B8C4CEB676840C42DE451C65C4D79E9 /* MDCStatefulRippleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BD15A4D6488FC7DCA7F0F18E0CAC1B9 /* MDCStatefulRippleView.m */; }; + 3C044F4DA0AAF294B5B40D8D61F76067 /* LRUAnimationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94E88E183E7AC90A64BD46E39E2C85E /* LRUAnimationCache.swift */; }; + 3C1AF825830E083E899D53A5C2F7E3EB /* MDCIcons.h in Headers */ = {isa = PBXBuildFile; fileRef = C364DE9E9FEDDD730DBF09F77CB0B143 /* MDCIcons.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3C1C2FE0D3AD4E5B2EEDF80C83AD822F /* ChatItemProtocolDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A488EF6B3932789A199EFF0E48669A8F /* ChatItemProtocolDefinitions.swift */; }; + 3C51EC6664C4867B564D0B6604C7738E /* MDFColorCalculations.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BEEEC76296F936D8FA70CA0604BB74F /* MDFColorCalculations.m */; }; + 3CE30BDD410305F4D3C8C13D12507A4A /* NSLocale+MaterialRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */; }; + 3CE69A673E4CCBBC2BF363C34B3E4702 /* MDCTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DE63B387DA24E48BA2EDE84C68DC8DE /* MDCTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3DCCF761B72AB77C921C6347AD39D34E /* NSLocale+MaterialRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */; }; + 3ED06C6A277226FD2D9410BAC8E6649D /* Glyph.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C869DD8FBC246E0FE2199DCEB50D3D /* Glyph.swift */; }; + 3F016AAA33E691084D294F7A12AC56AD /* MDCRaisedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FF5E5B45041E7444D6447C17872668F /* MDCRaisedButton.m */; }; + 40F06CD846C60BCA8E500808BFC5D8AB /* StarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD727B37100536B0582E4ACA83844 /* StarNode.swift */; }; + 40FA888B44A7961B13F0E7B332102CFC /* AnimatedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F84C77FED35072FBA03ADBD28FF9F3 /* AnimatedButton.swift */; }; + 415CF97552CF15ABEB0FBF16BB7CBC0C /* BaseChatViewController+Changes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA378A26B99BBD94560AE1C8C02B11F6 /* BaseChatViewController+Changes.swift */; }; + 41A27CED3C6DB6554EBDD9BA47356969 /* MaterialIcons.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D0323B0058AFC25122470F8D9558B7D /* MaterialIcons.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 41C21FEBC88B3A3F97B18DBDF929C751 /* MDCTriangleEdgeTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B5993FADA59638CD437C1C18E3DDA1D /* MDCTriangleEdgeTreatment.m */; }; + 42DD01B84A5FF2063B4C6ECA671FCE3E /* ItemsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61814AFE969DF03C54E6B9E503F28302 /* ItemsExtension.swift */; }; + 437EF01383D730AE242D947B9B58AA18 /* MDCInkLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3414E16D6767D8F8FBAB002ECBAA6CD /* MDCInkLayer.m */; }; + 442D9429E34A98231CFCDE3F82B30DE5 /* Marker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22A0783A99782B1368A7C295F05B893 /* Marker.swift */; }; + 45E483574827876CEA2E3D2DE49257BF /* LayerImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35BD0FEEB2EF9ADE87455CB6949E56DC /* LayerImageProvider.swift */; }; + 461BEAD29EE3D6E33BCDD8951CA80FD3 /* GradientStroke.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6ACC937F5C195801B15F2AB3B92101F /* GradientStroke.swift */; }; + 4634BA717BFCE522E5B42304C6A78B5D /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5CBEF7738EFE577EEAE11F00FF329E /* ParameterEncoding.swift */; }; + 471611F482CDC15BF464E3BA9CB83968 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE7DFA57EB0C03A4757CAB9256086346 /* Notifications.swift */; }; + 47A9B68E4429C21FB25951BF68989FA8 /* NSArray+MDFUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C83FB5770E9E4004C80C5217E3CD69 /* NSArray+MDFUtils.m */; }; + 482827C9A937BDC9B86EC162974D8F3E /* MDCShadowsCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D714F498A6A5E7FC8FB92A3819A449A /* MDCShadowsCollection.m */; }; + 489308FC142DC204B40A895C3A6F3A1B /* GroupNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63431295907542E5A9DD8630DC9338BA /* GroupNode.swift */; }; + 48A55C7FBBE4B4F30AEA67E3A50006EE /* UICollectionViewController+MDCCardReordering.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A370E644FBCA6F70723C4C1D6720158 /* UICollectionViewController+MDCCardReordering.m */; }; + 490142072F8EE916442517A5884282E0 /* UIColor+MaterialElevation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C64B5F902ABF8446821C23B3779E31D /* UIColor+MaterialElevation.m */; }; + 496713FA3DBA4082C7948ECF7349A992 /* Pods-iosTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F5922665F1DC85A6D8A92E2E648D19BE /* Pods-iosTests-dummy.m */; }; + 49A404FC51F1F7CDCDF7FD0112F00A5F /* SerialTaskQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536C19C18245423A6AFB8166C8F93F35 /* SerialTaskQueue.swift */; }; + 49D5F0C6A4922BCF3D0B41DFDCE905CA /* ShapeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBCF7000A4C9DC9C24B02BFE17451F85 /* ShapeNode.swift */; }; + 49E32757F5D405E67A46B71E862DE633 /* Interpolatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91855A9940FB42F19FD2875C45E2CB01 /* Interpolatable.swift */; }; + 49F6DAAA65DA40549E2D693A7E1B9E85 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + 4AF0F0425EE13873D1A3D72C87FD0CFE /* Star.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE689E19EA5C8EF636167214A787615 /* Star.swift */; }; + 4B4B75F4B215FD3E854871B3E1F31CD3 /* MaterialShadowElevations.h in Headers */ = {isa = PBXBuildFile; fileRef = 0056C47896966D18A6A1A5446B66D105 /* MaterialShadowElevations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B9A1C30219659C0AA8D1AE79832347B /* MaterialColor.h in Headers */ = {isa = PBXBuildFile; fileRef = A575DBC967BD1B9824384DE5EAC71824 /* MaterialColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4CD67A5130FE795F570FA71E4A0F0817 /* MDCFontScaler.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D37C9BDBB809F1F5B2D0212FF9CFE99 /* MDCFontScaler.m */; }; + 4D06CA57EA8BFCEC35A0E97BEA2DF034 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEFD189E2BBDDBFEFE3D285ECEA7330D /* Bundle.swift */; }; + 4F63D1C9A3C378378BEBA8171E0EB4B3 /* MDCFlatButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6828FC052E6421784F770E0D6078DDB6 /* MDCFlatButton.m */; }; + 4FAB52F6DBCC8A48A6FC5B865CF4984D /* ShapeLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF96553AC1A4981ED8F74D3571CB86C /* ShapeLayerModel.swift */; }; + 4FC0BE22D1389E78DCDC8515F3C1F124 /* ValueContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80EB9AAB47357453596394BFD2FBD6D /* ValueContainer.swift */; }; + 509492048834DCECFB5FAC619B80CDBA /* ImageLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB0A58B0BC617DECB8D3427A1405634 /* ImageLayerModel.swift */; }; + 512FAFBD71830F126224C033B6C45F4E /* RetryPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3619A8910E5127A2A7EBD2417924A3D /* RetryPolicy.swift */; }; + 51BE4D64CB57A36659FD73D354F810F0 /* RectNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20AA7DE19CA8DB5FD1FE6636C4C6E89 /* RectNode.swift */; }; + 5269279BA5DAB06E59635312FF2FCC76 /* MDCInkView.h in Headers */ = {isa = PBXBuildFile; fileRef = 79EA03EF06AA77426C2C84981497D6F8 /* MDCInkView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 52BE6F747C26DF2A24532458E55DC10F /* HTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FFD3AD6BFF43E494BD4BD3E560F4A9 /* HTTPHeaders.swift */; }; + 535D24555149DB4F16003AEB62B2F4FE /* lottie-ios-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 631CC31B1866E988A56C286024B8A805 /* lottie-ios-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 546594038A39C1C5480919C5C51FFF46 /* NSString+MaterialBidi.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */; }; + 569D15C26BA16947069372B7D6269C51 /* SizeValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C78482FC811B81F0499146DB6F8592 /* SizeValueProvider.swift */; }; + 58287E8A2D3CC19A61F6EC3D5138CED3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB57E704596E48FF3325A1F46B6E783C /* QuartzCore.framework */; }; + 593F9E60F9CA978FD7DEF9A6C0EB917D /* MaterialMathDummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D3C80D0A870376D3DAB74C8D106BFF8C /* MaterialMathDummy.m */; }; + 597AB8E13D0B44321059C630A47807D7 /* MaterialMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 98EB1455F30B28A8F4F59C33E8CB38D2 /* MaterialMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 59CA53BBA8B52B7565A5B31E205B5DBE /* MDCIcons+BundleLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FCF28462DDA6182768A18DEE727FA6C /* MDCIcons+BundleLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 59DB1972DC0FB82B837041D7ADA10E9B /* MDFImageCalculations.h in Headers */ = {isa = PBXBuildFile; fileRef = 7053BE770B19CADCC67259D2058FBFE5 /* MDFImageCalculations.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 59E047AE8E4BE5C88696952252BF2AF4 /* PathOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 940160C49FE72F93E569C21314AB30EE /* PathOutputNode.swift */; }; + 5CDEA5D93507BB56778DFE0FF9D755B6 /* MDCFloatingButtonModeAnimator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4148219036DBEF09983EE603792119B1 /* MDCFloatingButtonModeAnimator.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5DEC3A3BBE20B44348CE23832360AB86 /* ShapeItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86684B080F0E3D4C1D38F093601783B /* ShapeItem.swift */; }; + 5E479B4E6701DA0CEA6D0A57C50DD9DC /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E145BBB7A0266AF3CEF37E16F141F03 /* ColorExtension.swift */; }; + 5E5703B9B2DCBD225054E143EDE69080 /* GradientStrokeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0A65CE9CFA027DECC15DDE7C84261A /* GradientStrokeRenderer.swift */; }; + 5E594FA3290D3D70F500572D0AC100DB /* URLConvertible+URLRequestConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C4846B514755F166442D8D10CEA565 /* URLConvertible+URLRequestConvertible.swift */; }; + 61FC00C7FCA912D5B60ACFCD7B93CB32 /* MDCInkView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA395024B9E67C9EE8187839618E96FB /* MDCInkView.m */; }; + 626F1E2369BBC5E226FCB50B0B3D74A2 /* Asset.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7B1A690B1D232B90A70E4E318BB297 /* Asset.swift */; }; + 629777492DD9FCEC0E6BADDED7A06292 /* SolidCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C5DEABB0B24A9A6A4FB076F829A033 /* SolidCompositionLayer.swift */; }; + 64317603918517CB58BC50355BBD14F8 /* MDFInternationalization.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */; }; + 64A1B6BAE055F3BEAD2244B06B758B47 /* UIFont+MaterialScalable.h in Headers */ = {isa = PBXBuildFile; fileRef = B867765B8E02C5AE1B3C6A2B771DE65C /* UIFont+MaterialScalable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 654D4B8FB3BA49FB52CF733DF0F28CDF /* AnimationFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD480CF250BF63F248837A06A6F55B90 /* AnimationFontProvider.swift */; }; + 688337B18659C4BF722F87AFC4FEEF81 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7B54C5101F5D61940E8C3AE0858E0A6 /* SessionDelegate.swift */; }; + 68C81A493C4CD3F1D8425C3C12B6F1E3 /* MDFRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */; }; + 69200AC3BAAF8F7878D72847AB8DDB8C /* MDCCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B2BD90586E1B7C4656D777C09F8F99D /* MDCCornerTreatment.m */; }; + 6A6B387C7487CCA8B700C94CAE4297C3 /* StrokeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C11F8849A9F9FA96193BDF6D47B305 /* StrokeNode.swift */; }; + 6A7604C990C5DFF551C625E6EBC1E93A /* MDCCurvedCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = CBBE66515626EA5EE7282EF95C8275B3 /* MDCCurvedCornerTreatment.m */; }; + 6BC837979F6FD58ABE2029174745CF8F /* FillNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B32DE56F884A29871DDC06DEFDD299 /* FillNode.swift */; }; + 6BFC3EBA0293805C586DED141F57D440 /* MDCInkTouchController.h in Headers */ = {isa = PBXBuildFile; fileRef = AB54FAD6E9F36E88C0A18E94EB070CA3 /* MDCInkTouchController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6C945C98391E1347EE968F36DABA4B3D /* SolidLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187B43272E4EAEF4B35EA65BBA8B1334 /* SolidLayerModel.swift */; }; + 6CEDCE10C0318529B41437117724D2A3 /* GradientFillNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19302162D052125FE5F7992C054A240 /* GradientFillNode.swift */; }; + 6DADCA7F70AA8718696BB357CD2D6140 /* UIApplication+MDCAppExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = AC2834BC1BEF7C025DE72333BE447006 /* UIApplication+MDCAppExtensions.m */; }; + 6FF5AC95881C5994A17183FCD68D9760 /* MDCShapeMediator.h in Headers */ = {isa = PBXBuildFile; fileRef = A287971E3E70E72ECCDFC5485A8B6D35 /* MDCShapeMediator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7004F49ED8B75EF71E86E1BB0BF7F31B /* StrokeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0EBEFA02E716358C707E8E92A56CC2 /* StrokeRenderer.swift */; }; + 70BC4C88A01730C10637E04170D18842 /* MDCShapedView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4157CBEADF18154FD243E39C50468258 /* MDCShapedView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7154A1C49053E5493963962660A03513 /* CAMediaTimingFunction+MDCAnimationTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = A4D99A5054C55BC32C98A5EF91CB5BDD /* CAMediaTimingFunction+MDCAnimationTiming.m */; }; + 7187BA8BB4975F949B9A3E5413EF29E6 /* MDCFontTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = 275AD0BC20557E9DC939C53DFF5BB2F3 /* MDCFontTraits.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 71EEB4CB0D6FF002E43D958FAE52AF63 /* UIView+MDCTimingFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D1A663AAA238F5A6580850607B58FB9 /* UIView+MDCTimingFunction.m */; }; + 728FBE5D5AFDA125AB4D0FF6F66DDDA0 /* KeyframeInterpolator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174A76F8A707305878153E63E7AF69F3 /* KeyframeInterpolator.swift */; }; + 731FB6C1A89663C6273A5ED2A93838A2 /* UIColor+MaterialBlending.h in Headers */ = {isa = PBXBuildFile; fileRef = 73500719BEBC67E98C2432BA726CF2BD /* UIColor+MaterialBlending.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74129ED9A3E24DFAFFC726872335B23E /* MDCFloatingButtonModeAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1732F4D5612B18CF4BCB5A0D5F9A8C /* MDCFloatingButtonModeAnimator.m */; }; + 7429801C83C663E2AC03DF593EB09F26 /* MDCCurvedCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A09CD4BB399191B8387A7D72E4B2909 /* MDCCurvedCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74804BD8118FAA4C1A0F62E7A9E8F8B7 /* UIFont+MaterialTypographyPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = F49594B80BEB495C857FE4A0C034A387 /* UIFont+MaterialTypographyPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 74B1E17BF464786B34DEB58A508B48B4 /* MDCRoundedCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 95DF7386D55C9F9642DC5077C341E35E /* MDCRoundedCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74C2C803859AE1F9EDBB8BC6D027ACE9 /* InputPositionControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA603856ED143A5A0536C7F9F8785FFF /* InputPositionControlling.swift */; }; + 7514339E0BB67279B4151F7E7C9DF20D /* UIApplication+MDCAppExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = B27333CDD645D2D5CF612BB67776A72B /* UIApplication+MDCAppExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 75A51A634B4414C4BC6CE7DA089CAEC8 /* MDCEdgeTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 19E09DB898B5D3E34265E0EE3D2FB559 /* MDCEdgeTreatment.m */; }; + 77F503B82A75DA59A61413C1D5371422 /* MDCTypographyUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 49FB87A2C6705951A697BFD94E3FFB81 /* MDCTypographyUtilities.m */; }; + 785E547BC62FE180EDB4F80976888437 /* MaterialIcons+ic_check_circle.h in Headers */ = {isa = PBXBuildFile; fileRef = DF79A6BB5BC283B6F1FFF7394D1ABD51 /* MaterialIcons+ic_check_circle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7873F0E585A1DD314E9A681B5E4C050C /* MDCRippleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 65D2F1380362B7EE62D2678F79650A3A /* MDCRippleView.m */; }; + 78A557FFD62CE14E4D6B85C238D6D367 /* BaseChatItemPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6102438DC85CE0EBEE3E8E37512F0869 /* BaseChatItemPresenter.swift */; }; + 7A42366DBD41A248734C3A9973EC9DE9 /* MDCPathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D1132D92154CA46250E791A60FC2C43 /* MDCPathGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7B71F5D8CEF38A4E44B101984BF32A22 /* MDCCutCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 43693CCEC265B75D660F71A9422AC97E /* MDCCutCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7B90B8B1E94AA7BCB6F8195B232C0504 /* NodePropertyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF2CED0DB3829D4D4BE54A78C1072DB /* NodePropertyMap.swift */; }; + 7C49833872601FCD2511E3717A726467 /* ColorValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0B52C8F35A67626A42F0430F36E070 /* ColorValueProvider.swift */; }; + 7D3E0B70A1031AF6F52BB2F2704C5D7A /* Chatto-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B93D411155688717E99B55AE5D57CF10 /* Chatto-dummy.m */; }; + 7D6751B1F74E12C7B85C8A86EF398113 /* AnimatedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6EB232EC11EB49D9DA52EDFB9660747 /* AnimatedControl.swift */; }; + 7D990BA3B83998A2A1ABD69E0F3D44CE /* Pods-ios-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D720E1DADE8B1BB169230ADFA11012E3 /* Pods-ios-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7DB6497C9CC06CA922AC1152CE38CDAC /* MDFRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */; }; + 7E4FBF289DDBE5FE3D144CF1347D4092 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3117010738ED3E7B5080810EBF99FBBD /* CoreGraphics.framework */; }; + 7E636D3BE68B055932C1DBF52E9E5F8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + 7EF5AA59D08C107D83A5E3FE51633211 /* UIView+MaterialElevationResponding.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA556AD9FDEF109652EAA16FF14D299 /* UIView+MaterialElevationResponding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7F1DEF5C995E93F3EEDEE6562BD3FBF4 /* ChatLayoutConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD0995EC979D7375ABF14FB9A0A9EB0 /* ChatLayoutConfiguration.swift */; }; + 8143C58AA2F1E117E2A7BF20E9582EF3 /* TextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BBD822E6408AD8D9D1D46DBCBA9381 /* TextLayer.swift */; }; + 822EE6C99BED7B9C6A92C87D7717EECC /* UIFont+MaterialSimpleEquality.m in Sources */ = {isa = PBXBuildFile; fileRef = 039BA34B5A5B9FE2B7971FE6E64557BB /* UIFont+MaterialSimpleEquality.m */; }; + 82558E399C6304091C91304BC4E9FAD1 /* UIFont+MaterialTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E28A208954B54643898470FABAC2905 /* UIFont+MaterialTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 847D96F05D6EF0D2AC3E54036A72843F /* ChatDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA843D119FBBE1873DB868B2C42F47A /* ChatDataSourceProtocol.swift */; }; + 84C8B10EB6AFC1DB7574650206F73019 /* GroupOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 058498847739D73FC959AC6FAC7A0D9F /* GroupOutputNode.swift */; }; + 84DA7DB4EF4E0E8B628B429882B14FBB /* TextCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C8E244ED61A9B57F738C5DB46FCC12 /* TextCompositionLayer.swift */; }; + 84F43C1B8B0B179BBFF86DE99BBF4422 /* MDFTextAccessibility-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = FBE0655303C1520FF0EF50C57907E661 /* MDFTextAccessibility-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 856565C8AF270085E80E72C5D81FA186 /* BundleImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAEF5AB5FA15E20986DA16EBD0840CA2 /* BundleImageProvider.swift */; }; + 85AF5816CC3948E8200711491BA938E5 /* MaterialApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CE46363B5ADC47CE9B57A3335A26016 /* MaterialApplication.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 86DB9FBEAE3DE9E248D74A04EEE10875 /* TrimPathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0CB865665244183E4B9659E94101F9B /* TrimPathNode.swift */; }; + 874F69B4F2476462C2E8952A231DEF39 /* LayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525767FD21D481C033ED537000C2A5B1 /* LayerModel.swift */; }; + 87882C5A446E492E7BC82763056E299E /* MDCShapeGenerating.h in Headers */ = {isa = PBXBuildFile; fileRef = 46AE7E00528585497EA59CD94F669E3A /* MDCShapeGenerating.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8917A3295F44CCF46EEB66EC69B6DC31 /* GradientStrokeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FAB2482AA7F33F1F62927A25599EF4 /* GradientStrokeNode.swift */; }; + 8AC8E7FC1C4A48B36484927306556F31 /* CellPanGestureHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C197D9AE91D805F5FFDD745A3332768 /* CellPanGestureHandler.swift */; }; + 8AEE5FA821A6A42749CFDF8DD6CDF413 /* ShapeRenderLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6DB58D8654DC8B5F88E4D12FD74409 /* ShapeRenderLayer.swift */; }; + 8B8F97130DA7C5ACCF2ADBD9A8B8C138 /* MDCRippleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = E81ECAF8BBE1DB54276ED819382CA9FA /* MDCRippleLayer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8B9CDBE3FFD712120CD66DD8B06C44E4 /* ParameterEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA343ABB695B730239978538A94F38D6 /* ParameterEncoder.swift */; }; + 8BBBA0F8098CEDCDE429C22E32810395 /* MDCLegacyInkLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = B15025A80B5750A7F05A489683577910 /* MDCLegacyInkLayer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8C050C985EF60CCC3B9E25050BE20F3E /* NSLocale+MaterialRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A85C401C2117402508517940B47B11D /* NSLocale+MaterialRTL.m */; }; + 8C59CE48E9D049159ECD769482024C7A /* AssetLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D7840BB6B99AC092EAB4942FDD3F53 /* AssetLibrary.swift */; }; + 8C802F4EE8500BDBC0D000E200B162F2 /* DummyChatItemPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A9EA755014DD5DD4C27D7ECD93783FD /* DummyChatItemPresenter.swift */; }; + 8E9D0B3DD987E4503F62401A7F412FCA /* MDCRippleTouchController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DC202126AC7DEA521D46EACE0A8CB33 /* MDCRippleTouchController.m */; }; + 8EFABEE2E3BAE5BD9B93C1B58A69A178 /* UIView+MaterialRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */; }; + 8F9E1EEF2FE52E3231A769722D5C4148 /* ServerTrustEvaluation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8B9BF018FD470AE5EBE4F9FF3808CD /* ServerTrustEvaluation.swift */; }; + 9165FD4860C36068CF81600ED9992F02 /* MDCRoundedCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A16E9C361081323779110A2E0EE4F83 /* MDCRoundedCornerTreatment.m */; }; + 92D5715E90A1540B39AFE10F3D2A5443 /* CompatibleAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6F69E4E368E5F576A3D6AF24022D2D /* CompatibleAnimationView.swift */; }; + 92DA5E1124FFD63F1FD2CB5B3A106B3F /* MDFTextAccessibility-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AA82D677A35A05B022A324E49F50C /* MDFTextAccessibility-dummy.m */; }; + 941822CDF68EB8F4D49F150457A82616 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B411BF2903D79884DFBA1F089404388E /* ResponseSerialization.swift */; }; + 9476C7CB756F02F62FF85F3AC6A74FDE /* AnyValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A162FEC0123631BFA766AC1CD2E593 /* AnyValueProvider.swift */; }; + 94C1D0EC92D3122730BAC6014910D19F /* MDCButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 97BE17BC31FAF7F8B04D6EFFBBE78C80 /* MDCButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9535D614A8A0FD3B56CA7E8BACA11451 /* MDCCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D46DD556D1F791A1853E2A6E2506FF6 /* MDCCard.m */; }; + 960892840B75C67C133BAA7E39C35D55 /* MDCSlantedRectShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EEB4FCAEBF9555B90A5EC48FE3D6B4F /* MDCSlantedRectShapeGenerator.m */; }; + 96A6F504929F868038D46B5B6A03038B /* MaterialShapes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A7B2BF1390CAC2A1A79D280BA2AA739 /* MaterialShapes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 97030F2617BD73C118C8334624A84458 /* MDCFloatingButton+Animation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4838FCADE32426C4BC81299CE2906DE1 /* MDCFloatingButton+Animation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 97584BC08D2B494417BDEE268CFF38C9 /* Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93520D487909F1B889485508132F1A96 /* Combine.swift */; }; + 984167BC563C943B94B43A01DECBEAB0 /* UIView+MaterialRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = A2783799D5308869A35311BBDF845364 /* UIView+MaterialRTL.m */; }; + 98E4EFD77D2015E422923ED1EAD4AF7F /* BaseChatViewControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4F064EEF86F1829D1B928D11963739 /* BaseChatViewControllerView.swift */; }; + 99D85B4B92B509E63CB8C3056CF601C5 /* SingleValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF8701D965CC5D013B9CC49B8B228E8 /* SingleValueProvider.swift */; }; + 9C0BE8FA0030B2BC1DF7C159FA059389 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + 9CFDA7C92E0EEA31F709663B0E727ABA /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6A0C29D359F8F037F7DF2A21B78FC4 /* EventMonitor.swift */; }; + 9D3A2386D2F126134C1A61111BAF2B8C /* Stroke.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCD73AF1D7111668851A56E369B30AF /* Stroke.swift */; }; + 9D880C5CFBE97A142FC516777217C492 /* MaterialShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = ABED2A4D6F3787803F5376C66CB9544C /* MaterialShadow.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9DAA6E25378FE244F7D4451A5D35FB51 /* MDCCornerTreatment+CornerTypeInitalizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC4A25E6CBE6CD94D65C7642079986 /* MDCCornerTreatment+CornerTypeInitalizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9E361C56F23EEFCEBF62E79BF5165AD6 /* MaterialElevation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DD0AD806399CC6AE3AA2B4F75C77C23 /* MaterialElevation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9F64E84B54A721D525FB5FEB6B04AD92 /* NSString+MaterialBidi.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */; }; + 9FA8E4B6DCC77FC54C0E72979D87FDDA /* MDCFontTextStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 87107EE4D974CAE959A375B662F4204F /* MDCFontTextStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A093909FE7269A84364560DDC9375F31 /* MDCCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E28ED602A75DB92340D7444DB1995A1 /* MDCCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A0E0BC90F446AAFA84B16933CE45D172 /* CAMediaTimingFunction+MDCAnimationTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 37F289E6F2E521F6C7C3C1203689674C /* CAMediaTimingFunction+MDCAnimationTiming.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A3153333FC136836B0028E6AB2A56BEE /* AlamofireExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 701CF46760379C28084078F7D4BAFF32 /* AlamofireExtended.swift */; }; + A32194DA20A525E2A0DD2C1DC780FF99 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45EFEC8F1A0F1093631AF60C90BB060 /* Font.swift */; }; + A3D5AADA1B068C272F4C41E9771EE195 /* TextDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDC53D7CBF3A251E463BB224DD12DCE /* TextDocument.swift */; }; + A4D811A62B4CD50FEF2943AB7A090055 /* MDCRippleLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6DE9329322864658D938C211EAFA09 /* MDCRippleLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A4F1202CE5BBE79F3BBCAE3D2B16BC03 /* Result+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246B8D62D146A318E129AB0C450D33BA /* Result+Alamofire.swift */; }; + A6487D3314DFDA70D3EF7D709AFD1A17 /* UIFont+MaterialSimpleEquality.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AB8B4FEB07692832F02CED08212015A /* UIFont+MaterialSimpleEquality.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A664924D6CCE2922A3F81EC932F4D476 /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BDB7E8223919BD683DE47719CEA1A69 /* AFError.swift */; }; + A6735D2299B4CAEA6BE631073D8BA734 /* AnimatorNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1DDE875714E724C5D0109D3735095 /* AnimatorNode.swift */; }; + A6F57488F348E0036579E5B9DD98CAA2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + A76309F8030847107CE541B3B173FC24 /* EllipseNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67AF078C9EE64DED1961778EFF012A0 /* EllipseNode.swift */; }; + A76F7B04181F8D8A0F931424F54C4C07 /* MDCCutCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = EA9BAA45A6BA523F01C4735963DDDADC /* MDCCutCornerTreatment.m */; }; + A94F1967A266EA1EB3E4910073BE41F4 /* BaseChatViewController+Scrolling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0314F72C05B7FF63F27BE4646BD8305E /* BaseChatViewController+Scrolling.swift */; }; + A957485EF720B71593D3C8160C44B9DF /* MDCLegacyInkLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DCA91058EFB02847D0DA47F4F3195AC /* MDCLegacyInkLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A9613D5848A69B5554E49963050E189E /* InterpolatableExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805B67A1EC691E2F297D0CB0A125C31B /* InterpolatableExtensions.swift */; }; + A9778B7C3AC696E47DF7D19ABEBA80C9 /* DashPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = C290807FDFEFCB92E5FABE8FE851443C /* DashPattern.swift */; }; + A9B10FC18D020818728E285314FDEDAD /* FloatValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 686DE3CEFB348F67B7F2E0CCDBED00A7 /* FloatValueProvider.swift */; }; + AA41EBE1B990DE355E95E626BCDE0D20 /* MDCShadowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ED311731D2D73DBC56836B34F3D634A /* MDCShadowLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AA578A39DA8FF95C6E3B387DE3C8654A /* AnimatedSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD14A3955F01AD7D390CC5763E43CEFE /* AnimatedSwitch.swift */; }; + AABFEA684B7B708D497FCACE7A03FFF7 /* CGFloatExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FE3D47542C17E69E982939D7C4342C /* CGFloatExtensions.swift */; }; + AB0E5CD170B9656EA06269823DD89CB1 /* UIColor+MaterialDynamic.m in Sources */ = {isa = PBXBuildFile; fileRef = 1555D5D35385492B8D027E811BE666CB /* UIColor+MaterialDynamic.m */; }; + ABFFB41EC17E1CC5B8A7B0266718DAE1 /* MDCFloatingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C7C60015DC526524C5DC274704897564 /* MDCFloatingButton.m */; }; + AD9CE391F7F42CA08809086C88DA94F8 /* MDCSlantedRectShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 34EBCCA2E4424A06FB1C11FFF86AE0E2 /* MDCSlantedRectShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AEDBDAA38F82EC2FA51759F782D4D5AC /* MDCButton+Subclassing.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C628C67659ABBADF02BA056396F87E1 /* MDCButton+Subclassing.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AFE7E0A95861830B7BE0CFCF493F662E /* Keyframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984A2B5195B698FF523B7D82CD694F0 /* Keyframe.swift */; }; + B061007C0C097FF93364AC9411CAFCDD /* KeyframeGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F19458534DF74ABBBC1D9171D2FA1A /* KeyframeGroup.swift */; }; + B1F8084190567650796949754C0D25E7 /* ShapeCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10CAC47530EA0936ECF4F5B18F41899 /* ShapeCompositionLayer.swift */; }; + B2CE1396D0B81AF08A5F6C886FF6E61C /* MaterialIcons_ic_check_circle.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 427AFF770A87FFC8BA489ECCD299D58F /* MaterialIcons_ic_check_circle.xcassets */; }; + B3901084F60BD621D593F30B15E9D5C0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + B3CB521DFC1B2FC4C400466B2636C9DA /* MDCLegacyInkLayerRippleDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F5C3BCCB2FBBD3DC6701A99A1EC16E2 /* MDCLegacyInkLayerRippleDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B4B385550E3C5121D46697EB10E83C02 /* MDCRippleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2931B1BB799911DD00AA57C202BA2A6F /* MDCRippleLayer.m */; }; + B50CAE951FE6B2B57ADDE94632B53B82 /* ImageAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D884B74C43A162D8B9756985E116E84 /* ImageAsset.swift */; }; + B59FA52B2FA220A61E7B371F6902BF61 /* MDCShapedShadowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E0B142ACFD6D223F820B0A902B17ADB /* MDCShapedShadowLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B5C83F05C6D42CD1F1AA5322952A475F /* FillI.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2502619916EAC0AB2E21B1A54DB347D /* FillI.swift */; }; + B5EB74E6B21E71690D59818B99FD9F4B /* GradientFill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B6A73993CD9CF59863BB4C36386EEAE /* GradientFill.swift */; }; + B6473B8E8353317F75D6800D4F7054CB /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D25173317D99ECFE9E0ECC69FCEC95FA /* NetworkReachabilityManager.swift */; }; + B713DE1857A1BE1020811FB842D67B1F /* MDCFloatingButton.h in Headers */ = {isa = PBXBuildFile; fileRef = F873EC876A8574DA2C7425CA4C3BC418 /* MDCFloatingButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B7964B8982C863F7AA9A9A0051387976 /* CompositionLayersInitializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31592E117E0046F2678B3066EB06AC3 /* CompositionLayersInitializer.swift */; }; + B89D1C69742F61878115334A1D2DFFE7 /* URLRequest+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B995834698359995E0262F3F70C896A9 /* URLRequest+Alamofire.swift */; }; + BB9A0A0E2A295ECBE4DF5DD2BCC49A6E /* MDCRaisedButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF5C7C1F8F18927BF9A4EBDE9886621 /* MDCRaisedButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BC95E3D42356C72892097F3C727F8ED4 /* MDCElevationOverriding.h in Headers */ = {isa = PBXBuildFile; fileRef = BF7CDBB13E9F0AC3175BDF383C468787 /* MDCElevationOverriding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD275ABF1DF797F0313866144001AE2D /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB9978D69E4ABC8F7AE8425D9E1F048 /* StringExtensions.swift */; }; + BECE55736F8FE0964E095D93B134E4E0 /* ImageCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A0FCAED698C32DB774702E78F6A9BB /* ImageCompositionLayer.swift */; }; + BF61BD1B780F84EB855CDC8D07C70CF6 /* AnimationPublic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B613FA13F2264BCF72F6DE1BD3588C52 /* AnimationPublic.swift */; }; + BF7F9976FA80C821BC13E58E30E94B5B /* MDFRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = FC7593B690F872386EAC6DA2F85BD36D /* MDFRTL.m */; }; + C02D5ACD4E1A80E8AE4FFC958652C0AA /* Rectangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E21A4FEF3A460EFF2C8B38BBA65C25 /* Rectangle.swift */; }; + C033F3E94355E90147C197D165E7DEE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + C16A047C4E8D856309A486182A490993 /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F557E15F0C636BE0C4547A358DA8C93 /* MultipartFormData.swift */; }; + C16B4056D4AF47CF2BDF2B30E9ED27B1 /* Ellipse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA25E16755D429C6CC771AD6044CEED /* Ellipse.swift */; }; + C1C2719D78A6372AF387756B6D2FB07B /* UIColor+MaterialElevation.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F86DF24DDC31E73E6DC1AD1E8E22E6 /* UIColor+MaterialElevation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C32D94C3DBA969C96E803632C0F15D5E /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AF02B3727FEC99B62286905D9F172 /* Color.swift */; }; + C4DA75B4F7DD07F64D5B2FB7B83421D5 /* ChatItemCompanion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 777B7794311A16E26C00B24639C365CB /* ChatItemCompanion.swift */; }; + C5248490BD1AB45374D2AFA6BC3C5C32 /* ChatItemCompanionCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FB631B982623359A1D4E17E34F6369B /* ChatItemCompanionCollection.swift */; }; + C52D6111A5A724CED4C0451F2D8D7F76 /* MaterialShadowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = B3F1C077ED9B83ED31FAF400299DC965 /* MaterialShadowLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C58D6FE775EF73EB56A0DB0F81E9F505 /* FilepathImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64882FB458891751B0C4E20C1C23D5F1 /* FilepathImageProvider.swift */; }; + C6464DD1F08A953F238EAD6328090708 /* MaskContainerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5241F42DE93198A9743B520528DEBE0E /* MaskContainerLayer.swift */; }; + C6A87EEB51FF906E9FF947949211D34E /* Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1BFB72AD30F0DE0585E804791B833D6 /* Transform.swift */; }; + C78971D6EF0C7FFBCF847261C2015F62 /* MDCCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 11A09793C107A7840B6234B0400A787E /* MDCCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C7F66519CE6148F21D7DB11423F1D34D /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1962BFA68BF11D74BDD363384439BAC5 /* Response.swift */; }; + C872E018E08E0FD0B30B386614AB0E43 /* UIImage+MaterialRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */; }; + C97B9E8FA09F8CDA9458EC1387BBB6A2 /* PassThroughOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25B52A26AE822B9B2F58EA6B9E7E240 /* PassThroughOutputNode.swift */; }; + CA13097C8726C13E7D19984C391E156B /* MDCInkTouchControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 229265038E32F623E46E6B51D41CC33C /* MDCInkTouchControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CA24AD420264A0A9E0481C2F2E062AB0 /* MaterialComponents-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F3ED95F3D3F150E9EF3F7B365B3BE9C /* MaterialComponents-dummy.m */; }; + CA9F252FDC0DCF869BDE8A27E5B03BA0 /* MathKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919EA980AF20A4FC75CE16B54373D398 /* MathKit.swift */; }; + CBC6305777E98D5C59FC5ACAB00A17D1 /* MDCIcons.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A7AEC8A27ED687F3745E5FF8E0FEB75 /* MDCIcons.m */; }; + CD82340C0739E932322EB77FBCFA6D29 /* PolygonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80B62ED9C592FA542C65C50680BD2D3 /* PolygonNode.swift */; }; + CE150C3D6D3F9D4DA4F25E6B2831A385 /* LayerTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D4358B34E866143ECD08151FA7D559 /* LayerTextProvider.swift */; }; + CEDB69EFF8FEDC065680C80B49BDC5EE /* PreCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EE66424995BD90C997E2CEA9946F2AA /* PreCompositionLayer.swift */; }; + CFD442A95C590C28AA710A2A228F9F4B /* PrecompAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81EFFCF10BFC1B08CD7DBED57D297675 /* PrecompAsset.swift */; }; + D04A750433B71EFFE369931A5990825E /* MDCFontScaler.h in Headers */ = {isa = PBXBuildFile; fileRef = 42A318810EC6F37DE9379EF21DE434C3 /* MDCFontScaler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0C349CE9DB7214843BCDE9B8A4E4AF9 /* UIFontDescriptor+MaterialTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = EEBE2EE981029A2CA291F17EA8723B03 /* UIFontDescriptor+MaterialTypography.m */; }; + D0CC26A37DD0CD758EA556DD00D6503F /* MDCFontTraits.m in Sources */ = {isa = PBXBuildFile; fileRef = 789CAA7B6C1E469B4554820ADC2147D3 /* MDCFontTraits.m */; }; + D15FEA31AA9625BBF041FB91E48A9995 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C971CF5443E97989CD0DF44D9C18846 /* Validation.swift */; }; + D1662BB63BC57C2FEAA5E16F73DFB9C9 /* CompatibleAnimationKeypath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95BFC36D1019120C1DEC0F6758A0ED /* CompatibleAnimationKeypath.swift */; }; + D28B26A308824EEAE6D48615A1E0D14C /* GradientValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553199DA713332B2A7CE2BC1B053344C /* GradientValueProvider.swift */; }; + D2EB1D5874469F96E26EF47829867231 /* ShapeContainerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B96F646D146FE43BCB3C820D9573D99 /* ShapeContainerLayer.swift */; }; + D323E03646FFB2D4FA1C0633BE363EC0 /* MDFTextAccessibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 14889A17A94EB87B76300716DD2A9F36 /* MDFTextAccessibility.m */; }; + D3B9D2340450E1A03FC812FED6DE209C /* MDCCardCollectionCell.h in Headers */ = {isa = PBXBuildFile; fileRef = B518C0072DFC3DBA1FA3C49569B1F3DA /* MDCCardCollectionCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4939BB9A0101F96A5D863184AE21A7F /* MaterialAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = E58F9B821896E6DE3629364AB9B21F35 /* MaterialAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D620FDB5BDD15F3DF283F6989DAEF5C7 /* MDCRippleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 31F194A86821D5801BEACFA2DADF397D /* MDCRippleView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D6245FA6A979B1028595B60789B07701 /* CollectionChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA5A232C8B4D4671F587B33DCEF5FB4 /* CollectionChanges.swift */; }; + D66484BFD46F95EE237049C7F9150C76 /* Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CD9DD5A9448FED236DB4E8AAF6C8A4 /* Trim.swift */; }; + D681FB9211B3EB99507A7659A926AD3B /* CurveVertex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74405B4230DF6726303FA767434E6A14 /* CurveVertex.swift */; }; + D9D325BD0837A5B3F2C4368B4AF42D8D /* AnimationSubview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAC390311C727CE7C129F84BD9B09B9 /* AnimationSubview.swift */; }; + D9F13F6D976BDA43867D98A5F1CF6BF1 /* MDCCornerTreatment+CornerTypeInitalizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 24866442BD723570188362AFBC61ECA3 /* MDCCornerTreatment+CornerTypeInitalizer.m */; }; + DA24453A3C4C0EFDC25A253712BCEFB6 /* BaseChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C3EF2AB0724BFD75D6FE28F5429702 /* BaseChatViewController.swift */; }; + DAF43358D7A8ED2F222994AA7F1EDED6 /* MDCShapedShadowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = FDEEADA23D6F4C3DFE04A4117338D768 /* MDCShapedShadowLayer.m */; }; + DB44F781C343FE7260506125A2B217D5 /* Repeater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F59122698AC6FE35BC3BC2998203C14 /* Repeater.swift */; }; + DB65627B75E519F4B1B65221BA00176A /* CompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EAC9DA9D8A3173E14F6714F6DD597E /* CompositionLayer.swift */; }; + DC21E754F631A9780BEF04CB9EC24C23 /* AnimationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970D404B267F9ADF520FA00EC3700F65 /* AnimationContainer.swift */; }; + DC6BF07D4BE73BA3084613035CD88F56 /* Mask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77EF08C7D4C2345726A07A8B9A265452 /* Mask.swift */; }; + DCD0C33A2B50811D53CF68F021284B47 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931593E088B694A7D6B0D1FC4FE6C080 /* DispatchQueue+Alamofire.swift */; }; + DD58A00EACBEE274C381B491519C6B8C /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1E20D6B957EF263B8B500DA4F13C39 /* RedirectHandler.swift */; }; + DEBE3EC1FC8DCA47B2FFC71153B0636B /* LayerFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D7B9CC58E107433D11681261211BE1 /* LayerFontProvider.swift */; }; + DF4526CA0220910B45398C91C207AB2A /* MDCElevatable.h in Headers */ = {isa = PBXBuildFile; fileRef = 26833FC1284ED4F21560352EB2FA83B1 /* MDCElevatable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E06D3EE4B89C14DF0F1EACA6DB9DCF87 /* UICollectionViewController+MDCCardReordering.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FF269172DE0E3442E369BAF00EAB3CD /* UICollectionViewController+MDCCardReordering.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E0C65E16219718869CD2AFCA2C5465CB /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7800E126D44EA6D8FC842E5214971EF6 /* Request.swift */; }; + E1149D3565B395487CF0EB4487448957 /* MDCShapedView.m in Sources */ = {isa = PBXBuildFile; fileRef = BBF566D92B960CE6B69285672FDE16A8 /* MDCShapedView.m */; }; + E1769C267E82B0C24FE0FFBF949F0A6E /* StringEncoding+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF3276002056A8603CF35E5D0FDBB3B /* StringEncoding+Alamofire.swift */; }; + E1B1195009B9F9BD2E5EFE2113CFE20A /* MaterialRipple.h in Headers */ = {isa = PBXBuildFile; fileRef = 33FE8470F10646E30DF3366F83774F58 /* MaterialRipple.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E2BFA4A6A408809ECF183418A7368E1C /* PathElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096CBFAE6AD4730265460085F9186006 /* PathElement.swift */; }; + E43BAEAC9F260161234AFCA6EE7CA2B9 /* MaterialShadowElevationsDummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D0F908B38DD2B1F35BBC0B8F214C6DC0 /* MaterialShadowElevationsDummy.m */; }; + E46C272C4B95CBC92CDA9A2519D37833 /* MDFTextAccessibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C2F6B0AED78566F963846D8D10A8CC9 /* MDFTextAccessibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E47B9E036187569C65F248B6D527020E /* MDCInkViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 42C1DD5860EC1699A757C9BAB12A66D7 /* MDCInkViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E56C430EEEEAF6E39FF0942CCD78A061 /* UIView+MaterialRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */; }; + E5F04CB5F20F4E13DCFBC88AB7506E55 /* UIFont+MaterialTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = D390D3DEF1579285A22574E5E6EA97D0 /* UIFont+MaterialTypography.m */; }; + E70A755D57CC8683634F611358142A89 /* MDCMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9A7C0A475863CB257C9866B1EB05E /* MDCMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E80D58FB9E5F025ED34C4817E1D03D20 /* MDCCurvedRectShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = DB1E998F48C65751461A4CB1E13E45C5 /* MDCCurvedRectShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E857ADCAD7B647883D5B2AEC3F16D1D5 /* URLSessionConfiguration+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6587D8532A9E9E13BA4B37E713566C3B /* URLSessionConfiguration+Alamofire.swift */; }; + E990970757CDEBE53FA26AA40A17BD7E /* MaterialIcons+ic_check_circle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AF20B708D8368FA42456BB1F0DF92F7 /* MaterialIcons+ic_check_circle.m */; }; + E9CD4CB2CB433D9920922378F7886DAF /* MDCAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 3647DF7F6475E74ED3EB0CD6926B63DB /* MDCAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EAAFAB74BE099CA0217F13D56892CC50 /* MDFTextAccessibility-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 97A0DF460C679110DBBD0ED8B315B64D /* MDFTextAccessibility-Bridging-Header.h */; settings = {ATTRIBUTES = (Project, ); }; }; + EB0960E12A2CB2EDF98C11A39F506A15 /* InvertedMatteLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1B1AAE95B01FE76CC5E0D131135D20 /* InvertedMatteLayer.swift */; }; + EB1C650699E9DA20023D24CCB95944D4 /* CompoundBezierPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE0D77753F90766C6F5C64AAADA005 /* CompoundBezierPath.swift */; }; + EB4388F68E8E658447BDAC97AE5805C2 /* NullCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E0BB0AD007791BD42D66DF1F4DB387 /* NullCompositionLayer.swift */; }; + EC11B17DA78F7EEBEBC3EFAF68C6DF9F /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6684560C13B020634CFCE92295A15B72 /* Session.swift */; }; + EC2275BC165A2F54AC3498E0FDB085C9 /* AnimationImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F711AC4E23408CDD020388C5E5D71009 /* AnimationImageProvider.swift */; }; + EC9E770804940B15285BD22E29A6FCBA /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D5C4656A9953B89898D46D2942F6E89 /* UIColorExtension.swift */; }; + ED10CCA16008FE6450460EADE24C758B /* KeyboardTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97374F6103D866145B36386C14D1D8E5 /* KeyboardTracker.swift */; }; + EDC23A30E84A45747FC576F67901CCE3 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9DE4BB5450E42050B34C1355AB7E72 /* Utils.swift */; }; + EED90CBEBBCB86F3FE4215070F7737E0 /* MDCShapeMediator.m in Sources */ = {isa = PBXBuildFile; fileRef = F6BB438AED2F3B6F61B65BEBABF41B1F /* MDCShapeMediator.m */; }; + F03C958A2DC83E362F4E76C1758F2E99 /* UIColor+MaterialDynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = B829E8E11D02D1970F8C8FF989A82B64 /* UIColor+MaterialDynamic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F0501EA15D82306F07F094CA2558130F /* BaseChatViewController+Presenters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0594042CC23BC8F1D7592815BDBFC2C6 /* BaseChatViewController+Presenters.swift */; }; + F129D1B53CBB7D4F4F19F7C651C9EF04 /* MDCShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = 9625E67A414049AFFBCF73EDB3F00B1B /* MDCShadow.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F1665937EE88175C13BD066950658EDB /* LayerDebugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C30F85DF0D50528EF9BEE8A2BA482034 /* LayerDebugging.swift */; }; + F337417E09334F51565A55905275A05E /* MDCStatefulRippleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6AA23B87CFACBF3F6DA8E1FFE48A3A /* MDCStatefulRippleView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F363D238E6734C6503D7E8AACFB6F01A /* UIView+MaterialElevationResponding.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EA8C38C23FD8FDD5FE62F2CFD7B55B9 /* UIView+MaterialElevationResponding.m */; }; + F36D96A4346C90A2D11CB3B6A2ECF4CF /* AuthenticationInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB094FC5A0B58CDEC708A4D9A8189F89 /* AuthenticationInterceptor.swift */; }; + F457424CFB7849798D80D62A5EF98BB0 /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3D75AB0F4CDA59E7BB16BD81745B956 /* Shape.swift */; }; + F45BE4AD9E22109487817B3E369E8774 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 617F2D7F52382550906F7F058A9681F9 /* UIKit.framework */; }; + F59346B37473C393BC5B9573CA75D311 /* MDCFlatButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 985D00F7F6511C37B9DECF5DBEB3B451 /* MDCFlatButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F5D2A31C7EB1DE010771140B6E7ABAD8 /* URLEncodedFormEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D28FDB1911F3BD71EEBE077FA899488 /* URLEncodedFormEncoder.swift */; }; + F63BE0585331CAA3482EF736803F8243 /* RequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097A230A81C9305A1D7CAF969BD3B009 /* RequestInterceptor.swift */; }; + F659E0EBE31959B1758B3628AB4720B4 /* AnimatorNodeDebugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6DBEEB82DA69272704408A3AEBA6EC /* AnimatorNodeDebugging.swift */; }; + F79954C7D48B356B32ACA8BDDFDD183E /* LayerTransformNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B246A845EEBC2196E07F54E35ACA25A /* LayerTransformNode.swift */; }; + F8460445640FB3C461E58C1DFA2E0F16 /* AnyNodeProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678552B49FF59718A815C8CB72951E75 /* AnyNodeProperty.swift */; }; + F95134B340BDE8537CEDC91162AE776B /* UIImage+MaterialRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */; }; + FAF429A0D030A7708AC3E1C890CDFBBC /* MDCInkLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43E371B84D82B4BE12084C4D22A945 /* MDCInkLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FBBCCDCCAFD735DA076AEC214B31DEBE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; + FC622CB054985417281FE0FB6755C6DD /* MaterialAnimationTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = B443745DA64B8F614899680B793F7108 /* MaterialAnimationTiming.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCA5FAFF46860FFD052B5A96A4253915 /* UIFontDescriptor+MaterialTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 4040341BD83B0FAE1A20FF1D71955BB5 /* UIFontDescriptor+MaterialTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FDE49FD4B92B698131BA846A877E4F5C /* MaterialTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FA60992213EE87D4775A402EF9DE39B /* MaterialTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FEDBAD32E2EDA85AD6E362B82892A74A /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854600D850F2C3A98DF50071F6F04C09 /* Alamofire.swift */; }; + FF11F9BF6D01BF9AFD49B1831C74E77F /* lottie-ios-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E5379887AF991A23AF0071E5BBA66A3 /* lottie-ios-dummy.m */; }; + FF82E104EC48B26651A60B944BFFA297 /* GradientFillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619B96C839D9C1480A0A40E988C63AFD /* GradientFillRenderer.swift */; }; + FFFCB73998E59CCA79007CF915D56E22 /* Chatto-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DE1B915229A73A86A29689D51EBCEE8E /* Chatto-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 09ADAB9C1EBB34AFF27286BC540ECBF9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7F4022CD4249985E761F745058CAB372; + remoteInfo = "MaterialComponents-MaterialIcons_ic_check_circle"; + }; + 1E07159C86DCD357DDD6438B3A657BB7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CB5AA7A4FA94F4765AE579D6F4491956; + remoteInfo = "Pods-ios"; + }; + 37B8EC58BCB778F91D6C37656EDA3111 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2508BC3B908322C9E85E61AE743C9842; + remoteInfo = Chatto; + }; + 547A50F181286054E7D1AD178F2C9D36 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0B967D7F8561D42493EE289EC8D450D1; + remoteInfo = "lottie-ios"; + }; + 6ED33D3BA173DAE7C34C14C6974F4E02 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = EAAA1AD3A8A1B59AB91319EE40752C6D; + remoteInfo = Alamofire; + }; + 7678DE8865A337D774511B701E4FB23B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 55A01F8E987A6E2F7F0ED141A1ECA406; + remoteInfo = MDFInternationalization; + }; + 86D033B1EAA6DB4A9250A038CF62AF07 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B4723B5744013DF08BE3A5FEAA286944; + remoteInfo = MaterialComponents; + }; + 8AAE22C70F1836AC9C87C67A00D249D2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 55A01F8E987A6E2F7F0ED141A1ECA406; + remoteInfo = MDFInternationalization; + }; + CCFB263D6803CCB26550A6888F18657A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A8E4C927B2E899954BC2FD3314BCA9DA; + remoteInfo = MDFTextAccessibility; + }; + D397BCB4986AFF763539DD0013C5417D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A8E4C927B2E899954BC2FD3314BCA9DA; + remoteInfo = MDFTextAccessibility; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 85B3B64C90CFC972449F93DC4A812BBD /* Copy . Public Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/."; + dstSubfolderSpec = 16; + files = ( + 64317603918517CB58BC50355BBD14F8 /* MDFInternationalization.h in Copy . Public Headers */, + 7DB6497C9CC06CA922AC1152CE38CDAC /* MDFRTL.h in Copy . Public Headers */, + 3DCCF761B72AB77C921C6347AD39D34E /* NSLocale+MaterialRTL.h in Copy . Public Headers */, + 546594038A39C1C5480919C5C51FFF46 /* NSString+MaterialBidi.h in Copy . Public Headers */, + C872E018E08E0FD0B30B386614AB0E43 /* UIImage+MaterialRTL.h in Copy . Public Headers */, + 8EFABEE2E3BAE5BD9B93C1B58A69A178 /* UIView+MaterialRTL.h in Copy . Public Headers */, + ); + name = "Copy . Public Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0056C47896966D18A6A1A5446B66D105 /* MaterialShadowElevations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShadowElevations.h; path = components/ShadowElevations/src/MaterialShadowElevations.h; sourceTree = ""; }; + 02F95682318FB0084CD0FAE9E698A337 /* MDCInkTouchController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkTouchController.m; path = components/Ink/src/MDCInkTouchController.m; sourceTree = ""; }; + 0314F72C05B7FF63F27BE4646BD8305E /* BaseChatViewController+Scrolling.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+Scrolling.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift"; sourceTree = ""; }; + 039BA34B5A5B9FE2B7971FE6E64557BB /* UIFont+MaterialSimpleEquality.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialSimpleEquality.m"; path = "components/Typography/src/UIFont+MaterialSimpleEquality.m"; sourceTree = ""; }; + 058498847739D73FC959AC6FAC7A0D9F /* GroupOutputNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupOutputNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift; sourceTree = ""; }; + 0594042CC23BC8F1D7592815BDBFC2C6 /* BaseChatViewController+Presenters.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+Presenters.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+Presenters.swift"; sourceTree = ""; }; + 06460E2CD793BBF2C535219D13A50C05 /* ChatItemPresenterFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemPresenterFactory.swift; path = Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift; sourceTree = ""; }; + 096CBFAE6AD4730265460085F9186006 /* PathElement.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathElement.swift; path = Sources/Private/Utility/Primitives/PathElement.swift; sourceTree = ""; }; + 097A230A81C9305A1D7CAF969BD3B009 /* RequestInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestInterceptor.swift; path = Source/RequestInterceptor.swift; sourceTree = ""; }; + 0A5DF95E38B16446E73BCFAC6F53DA2E /* AnimationTime.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationTime.swift; path = Sources/Public/Primitives/AnimationTime.swift; sourceTree = ""; }; + 0C07D8193799950468B6CF3885E1193B /* Pods-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ios.debug.xcconfig"; sourceTree = ""; }; + 0D37C9BDBB809F1F5B2D0212FF9CFE99 /* MDCFontScaler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFontScaler.m; path = components/Typography/src/MDCFontScaler.m; sourceTree = ""; }; + 0D46DD556D1F791A1853E2A6E2506FF6 /* MDCCard.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCard.m; path = components/Cards/src/MDCCard.m; sourceTree = ""; }; + 0EA7FAE72F27B4A66C2CAEEC25C31634 /* MDFTextAccessibility.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFTextAccessibility.release.xcconfig; sourceTree = ""; }; + 0F40483DF444914DE771891BCED1F593 /* ReplyFeedbackGenerator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReplyFeedbackGenerator.swift; path = Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift; sourceTree = ""; }; + 0F59122698AC6FE35BC3BC2998203C14 /* Repeater.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Repeater.swift; path = Sources/Private/Model/ShapeItems/Repeater.swift; sourceTree = ""; }; + 0FC5A268C4EB4A715BD6C5613A37B5A9 /* KeypathSearchable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeypathSearchable.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift; sourceTree = ""; }; + 100C94E08EE07B09E19F115713E5B184 /* Pods-ios-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-ios-dummy.m"; sourceTree = ""; }; + 111FAD1AC980B2D67B27016C087B61ED /* KeyedDecodingContainerExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyedDecodingContainerExtensions.swift; path = Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift; sourceTree = ""; }; + 11A09793C107A7840B6234B0400A787E /* MDCCard.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCard.h; path = components/Cards/src/MDCCard.h; sourceTree = ""; }; + 14889A17A94EB87B76300716DD2A9F36 /* MDFTextAccessibility.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFTextAccessibility.m; path = src/MDFTextAccessibility.m; sourceTree = ""; }; + 1495FD5410C5BA9748915AE95726D978 /* UIColor+MaterialBlending.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MaterialBlending.m"; path = "components/private/Color/src/UIColor+MaterialBlending.m"; sourceTree = ""; }; + 1555D5D35385492B8D027E811BE666CB /* UIColor+MaterialDynamic.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MaterialDynamic.m"; path = "components/private/Color/src/UIColor+MaterialDynamic.m"; sourceTree = ""; }; + 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MaterialRTL.h"; path = "Sources/UIView+MaterialRTL.h"; sourceTree = ""; }; + 16900192B6F1F880A2BEEE4A15228EE3 /* MDCLegacyInkLayer+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCLegacyInkLayer+Private.h"; path = "components/Ink/src/private/MDCLegacyInkLayer+Private.h"; sourceTree = ""; }; + 1742EDF4DDCBB582B88771AEB1201E1A /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; + 174A76F8A707305878153E63E7AF69F3 /* KeyframeInterpolator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyframeInterpolator.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift; sourceTree = ""; }; + 187B43272E4EAEF4B35EA65BBA8B1334 /* SolidLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SolidLayerModel.swift; path = Sources/Private/Model/Layers/SolidLayerModel.swift; sourceTree = ""; }; + 18C8E244ED61A9B57F738C5DB46FCC12 /* TextCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift; sourceTree = ""; }; + 1962BFA68BF11D74BDD363384439BAC5 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = ""; }; + 19E09DB898B5D3E34265E0EE3D2FB559 /* MDCEdgeTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCEdgeTreatment.m; path = components/Shapes/src/MDCEdgeTreatment.m; sourceTree = ""; }; + 1A09CD4BB399191B8387A7D72E4B2909 /* MDCCurvedCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCurvedCornerTreatment.h; path = components/ShapeLibrary/src/MDCCurvedCornerTreatment.h; sourceTree = ""; }; + 1A85C401C2117402508517940B47B11D /* NSLocale+MaterialRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSLocale+MaterialRTL.m"; path = "Sources/NSLocale+MaterialRTL.m"; sourceTree = ""; }; + 1A8B9BF018FD470AE5EBE4F9FF3808CD /* ServerTrustEvaluation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustEvaluation.swift; path = Source/ServerTrustEvaluation.swift; sourceTree = ""; }; + 1A9AE3381193F742879F523CCE471749 /* lottie-ios.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "lottie-ios.modulemap"; sourceTree = ""; }; + 1B239243820382C1C6E7038645E10958 /* MDCFloatingButtonModeAnimatorDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFloatingButtonModeAnimatorDelegate.h; path = components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h; sourceTree = ""; }; + 1BD15A4D6488FC7DCA7F0F18E0CAC1B9 /* MDCStatefulRippleView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCStatefulRippleView.m; path = components/Ripple/src/MDCStatefulRippleView.m; sourceTree = ""; }; + 1C0B52C8F35A67626A42F0430F36E070 /* ColorValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ColorValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift; sourceTree = ""; }; + 1CBA47FDA3E910C7EDFE324EFA897551 /* lottie-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "lottie-ios.release.xcconfig"; sourceTree = ""; }; + 1CEC781B13B51E108C5658001DCCCD2A /* Pods-iosTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iosTests-umbrella.h"; sourceTree = ""; }; + 1D76179FC8700321E982CBF63CBE974C /* MDFInternationalization-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MDFInternationalization-Info.plist"; sourceTree = ""; }; + 1E1B1AAE95B01FE76CC5E0D131135D20 /* InvertedMatteLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InvertedMatteLayer.swift; path = Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift; sourceTree = ""; }; + 1E5379887AF991A23AF0071E5BBA66A3 /* lottie-ios-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "lottie-ios-dummy.m"; sourceTree = ""; }; + 1E786DB3BAA72871D457F5DC195A08AD /* MDCEdgeTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCEdgeTreatment.h; path = components/Shapes/src/MDCEdgeTreatment.h; sourceTree = ""; }; + 1E9DE4BB5450E42050B34C1355AB7E72 /* Utils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = Chatto/Source/Utils.swift; sourceTree = ""; }; + 2102D1FE11630B5091FE865DDD43F05A /* MDCPillShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCPillShapeGenerator.m; path = components/ShapeLibrary/src/MDCPillShapeGenerator.m; sourceTree = ""; }; + 21ABC4AABBA66B5F87EF5A5C789FDC8F /* lottie-ios-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "lottie-ios-Info.plist"; sourceTree = ""; }; + 21FFD3AD6BFF43E494BD4BD3E560F4A9 /* HTTPHeaders.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HTTPHeaders.swift; path = Source/HTTPHeaders.swift; sourceTree = ""; }; + 229265038E32F623E46E6B51D41CC33C /* MDCInkTouchControllerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkTouchControllerDelegate.h; path = components/Ink/src/MDCInkTouchControllerDelegate.h; sourceTree = ""; }; + 22BBD822E6408AD8D9D1D46DBCBA9381 /* TextLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextLayer.swift; path = Sources/Private/LayerContainers/Utility/TextLayer.swift; sourceTree = ""; }; + 246B8D62D146A318E129AB0C450D33BA /* Result+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Result+Alamofire.swift"; path = "Source/Result+Alamofire.swift"; sourceTree = ""; }; + 24866442BD723570188362AFBC61ECA3 /* MDCCornerTreatment+CornerTypeInitalizer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MDCCornerTreatment+CornerTypeInitalizer.m"; path = "components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m"; sourceTree = ""; }; + 25A7B53BBB25EA665175DB0281DBDE11 /* Pods-ios-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ios-frameworks.sh"; sourceTree = ""; }; + 26833FC1284ED4F21560352EB2FA83B1 /* MDCElevatable.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCElevatable.h; path = components/Elevation/src/MDCElevatable.h; sourceTree = ""; }; + 275AD0BC20557E9DC939C53DFF5BB2F3 /* MDCFontTraits.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFontTraits.h; path = components/Typography/src/private/MDCFontTraits.h; sourceTree = ""; }; + 27B3D295F42CEAEA35B48C43AD477C61 /* MDCShadow.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShadow.m; path = components/Shadow/src/MDCShadow.m; sourceTree = ""; }; + 2882BFF487CA5879D2F68C6B944595F5 /* MDCPillShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCPillShapeGenerator.h; path = components/ShapeLibrary/src/MDCPillShapeGenerator.h; sourceTree = ""; }; + 2931B1BB799911DD00AA57C202BA2A6F /* MDCRippleLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRippleLayer.m; path = components/Ripple/src/private/MDCRippleLayer.m; sourceTree = ""; }; + 2AB8B4FEB07692832F02CED08212015A /* UIFont+MaterialSimpleEquality.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialSimpleEquality.h"; path = "components/Typography/src/UIFont+MaterialSimpleEquality.h"; sourceTree = ""; }; + 2AD410EF9494BB5D4662CBBE59C2C06C /* VectorsExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = VectorsExtensions.swift; path = Sources/Private/Utility/Primitives/VectorsExtensions.swift; sourceTree = ""; }; + 2AF20B708D8368FA42456BB1F0DF92F7 /* MaterialIcons+ic_check_circle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MaterialIcons+ic_check_circle.m"; path = "components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m"; sourceTree = ""; }; + 2B1E20D6B957EF263B8B500DA4F13C39 /* RedirectHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RedirectHandler.swift; path = Source/RedirectHandler.swift; sourceTree = ""; }; + 2B2BD90586E1B7C4656D777C09F8F99D /* MDCCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCornerTreatment.m; path = components/Shapes/src/MDCCornerTreatment.m; sourceTree = ""; }; + 2C197D9AE91D805F5FFDD745A3332768 /* CellPanGestureHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CellPanGestureHandler.swift; path = Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift; sourceTree = ""; }; + 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+MaterialRTL.h"; path = "Sources/UIImage+MaterialRTL.h"; sourceTree = ""; }; + 2D6557B599662265675CB317D7A3AA35 /* Animation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Animation.swift; path = Sources/Private/Model/Animation.swift; sourceTree = ""; }; + 2EE66424995BD90C997E2CEA9946F2AA /* PreCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PreCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift; sourceTree = ""; }; + 2FA60992213EE87D4775A402EF9DE39B /* MaterialTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialTypography.h; path = components/Typography/src/MaterialTypography.h; sourceTree = ""; }; + 2FB631B982623359A1D4E17E34F6369B /* ChatItemCompanionCollection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemCompanionCollection.swift; path = Chatto/Source/ChatItemCompanionCollection.swift; sourceTree = ""; }; + 30734D2F4660366E1C28FA6E2E4345B1 /* OperationQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "OperationQueue+Alamofire.swift"; path = "Source/OperationQueue+Alamofire.swift"; sourceTree = ""; }; + 30C3EF2AB0724BFD75D6FE28F5429702 /* BaseChatViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseChatViewController.swift; path = Chatto/Source/ChatController/BaseChatViewController.swift; sourceTree = ""; }; + 3117010738ED3E7B5080810EBF99FBBD /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + 3198F273FE8A5D0B39F6AA64EB844FAD /* MaterialComponents.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MaterialComponents.modulemap; sourceTree = ""; }; + 31D7840BB6B99AC092EAB4942FDD3F53 /* AssetLibrary.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssetLibrary.swift; path = Sources/Private/Model/Assets/AssetLibrary.swift; sourceTree = ""; }; + 31F194A86821D5801BEACFA2DADF397D /* MDCRippleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleView.h; path = components/Ripple/src/MDCRippleView.h; sourceTree = ""; }; + 32E84BC23BD4ED514C1BB73378A0EF16 /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = ""; }; + 33FE8470F10646E30DF3366F83774F58 /* MaterialRipple.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialRipple.h; path = components/Ripple/src/MaterialRipple.h; sourceTree = ""; }; + 341DED9C8A182716EE1504F58E3BBA27 /* Pods-iosTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosTests-acknowledgements.plist"; sourceTree = ""; }; + 34EBCCA2E4424A06FB1C11FFF86AE0E2 /* MDCSlantedRectShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCSlantedRectShapeGenerator.h; path = components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h; sourceTree = ""; }; + 35A0FCAED698C32DB774702E78F6A9BB /* ImageCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift; sourceTree = ""; }; + 35BD0FEEB2EF9ADE87455CB6949E56DC /* LayerImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerImageProvider.swift; path = Sources/Private/LayerContainers/Utility/LayerImageProvider.swift; sourceTree = ""; }; + 3647DF7F6475E74ED3EB0CD6926B63DB /* MDCAvailability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCAvailability.h; path = components/Availability/src/MDCAvailability.h; sourceTree = ""; }; + 37F289E6F2E521F6C7C3C1203689674C /* CAMediaTimingFunction+MDCAnimationTiming.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "CAMediaTimingFunction+MDCAnimationTiming.h"; path = "components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h"; sourceTree = ""; }; + 3984A2B5195B698FF523B7D82CD694F0 /* Keyframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Keyframe.swift; path = Sources/Private/Model/Keyframes/Keyframe.swift; sourceTree = ""; }; + 3A16E9C361081323779110A2E0EE4F83 /* MDCRoundedCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRoundedCornerTreatment.m; path = components/ShapeLibrary/src/MDCRoundedCornerTreatment.m; sourceTree = ""; }; + 3A7AEC8A27ED687F3745E5FF8E0FEB75 /* MDCIcons.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCIcons.m; path = components/private/Icons/src/MDCIcons.m; sourceTree = ""; }; + 3AEB21337FEDA85FD4CE4B5534D436F9 /* BezierPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BezierPath.swift; path = Sources/Private/Utility/Primitives/BezierPath.swift; sourceTree = ""; }; + 3B5993FADA59638CD437C1C18E3DDA1D /* MDCTriangleEdgeTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCTriangleEdgeTreatment.m; path = components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m; sourceTree = ""; }; + 3BEEEC76296F936D8FA70CA0604BB74F /* MDFColorCalculations.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFColorCalculations.m; path = src/private/MDFColorCalculations.m; sourceTree = ""; }; + 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "MaterialComponents-MaterialIcons_ic_check_circle"; path = MaterialIcons_ic_check_circle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 3CDCB1C8D529F8DE664D2E4C96BD3A7D /* Alamofire.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.release.xcconfig; sourceTree = ""; }; + 3D28FDB1911F3BD71EEBE077FA899488 /* URLEncodedFormEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLEncodedFormEncoder.swift; path = Source/URLEncodedFormEncoder.swift; sourceTree = ""; }; + 3D5C4656A9953B89898D46D2942F6E89 /* UIColorExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIColorExtension.swift; path = Sources/Public/iOS/UIColorExtension.swift; sourceTree = ""; }; + 3D6F69E4E368E5F576A3D6AF24022D2D /* CompatibleAnimationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompatibleAnimationView.swift; path = Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift; sourceTree = ""; }; + 3D884B74C43A162D8B9756985E116E84 /* ImageAsset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageAsset.swift; path = Sources/Private/Model/Assets/ImageAsset.swift; sourceTree = ""; }; + 3DCA91058EFB02847D0DA47F4F3195AC /* MDCLegacyInkLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCLegacyInkLayerDelegate.h; path = components/Ink/src/private/MDCLegacyInkLayerDelegate.h; sourceTree = ""; }; + 3E7BEEB423FD0BB8206D7833EAFB5A94 /* MaterialComponents-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MaterialComponents-prefix.pch"; sourceTree = ""; }; + 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MaterialComponents.debug.xcconfig; sourceTree = ""; }; + 3EEB4FCAEBF9555B90A5EC48FE3D6B4F /* MDCSlantedRectShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCSlantedRectShapeGenerator.m; path = components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m; sourceTree = ""; }; + 3F6AA23B87CFACBF3F6DA8E1FFE48A3A /* MDCStatefulRippleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCStatefulRippleView.h; path = components/Ripple/src/MDCStatefulRippleView.h; sourceTree = ""; }; + 3FCF28462DDA6182768A18DEE727FA6C /* MDCIcons+BundleLoader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCIcons+BundleLoader.h"; path = "components/private/Icons/src/MDCIcons+BundleLoader.h"; sourceTree = ""; }; + 4040341BD83B0FAE1A20FF1D71955BB5 /* UIFontDescriptor+MaterialTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFontDescriptor+MaterialTypography.h"; path = "components/Typography/src/UIFontDescriptor+MaterialTypography.h"; sourceTree = ""; }; + 408FEF5380BBEF2A9A4EF9878452691D /* MDFImageCalculations.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFImageCalculations.m; path = src/private/MDFImageCalculations.m; sourceTree = ""; }; + 40FC4A25E6CBE6CD94D65C7642079986 /* MDCCornerTreatment+CornerTypeInitalizer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCCornerTreatment+CornerTypeInitalizer.h"; path = "components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h"; sourceTree = ""; }; + 4148219036DBEF09983EE603792119B1 /* MDCFloatingButtonModeAnimator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFloatingButtonModeAnimator.h; path = components/Buttons/src/private/MDCFloatingButtonModeAnimator.h; sourceTree = ""; }; + 4157CBEADF18154FD243E39C50468258 /* MDCShapedView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapedView.h; path = components/Shapes/src/MDCShapedView.h; sourceTree = ""; }; + 4259483CFD0AC1C7F1295B7B354781A1 /* AnimationKeypath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationKeypath.swift; path = Sources/Public/DynamicProperties/AnimationKeypath.swift; sourceTree = ""; }; + 427AFF770A87FFC8BA489ECCD299D58F /* MaterialIcons_ic_check_circle.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = MaterialIcons_ic_check_circle.xcassets; path = components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets; sourceTree = ""; }; + 42A318810EC6F37DE9379EF21DE434C3 /* MDCFontScaler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFontScaler.h; path = components/Typography/src/MDCFontScaler.h; sourceTree = ""; }; + 42C1DD5860EC1699A757C9BAB12A66D7 /* MDCInkViewDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkViewDelegate.h; path = components/Ink/src/MDCInkViewDelegate.h; sourceTree = ""; }; + 43693CCEC265B75D660F71A9422AC97E /* MDCCutCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCutCornerTreatment.h; path = components/ShapeLibrary/src/MDCCutCornerTreatment.h; sourceTree = ""; }; + 43E52D204C785271C19FE614C61534A4 /* MDFInternationalization.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFInternationalization.release.xcconfig; sourceTree = ""; }; + 46AE7E00528585497EA59CD94F669E3A /* MDCShapeGenerating.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapeGenerating.h; path = components/Shapes/src/MDCShapeGenerating.h; sourceTree = ""; }; + 4838FCADE32426C4BC81299CE2906DE1 /* MDCFloatingButton+Animation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCFloatingButton+Animation.h"; path = "components/Buttons/src/MDCFloatingButton+Animation.h"; sourceTree = ""; }; + 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFRTL.h; path = Sources/MDFRTL.h; sourceTree = ""; }; + 49FB87A2C6705951A697BFD94E3FFB81 /* MDCTypographyUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCTypographyUtilities.m; path = components/Typography/src/private/MDCTypographyUtilities.m; sourceTree = ""; }; + 4ADBAC831F15371103E6075A7099776A /* NSArray+MDFUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+MDFUtils.h"; path = "src/private/NSArray+MDFUtils.h"; sourceTree = ""; }; + 4AE9A7C0A475863CB257C9866B1EB05E /* MDCMath.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCMath.h; path = components/private/Math/src/MDCMath.h; sourceTree = ""; }; + 4BF5C7C1F8F18927BF9A4EBDE9886621 /* MDCRaisedButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRaisedButton.h; path = components/Buttons/src/MDCRaisedButton.h; sourceTree = ""; }; + 4C628C67659ABBADF02BA056396F87E1 /* MDCButton+Subclassing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCButton+Subclassing.h"; path = "components/Buttons/src/private/MDCButton+Subclassing.h"; sourceTree = ""; }; + 4DE016D373F57DCED6E20186B20A6596 /* UIImage+MaterialRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+MaterialRTL.m"; path = "Sources/UIImage+MaterialRTL.m"; sourceTree = ""; }; + 4E0B142ACFD6D223F820B0A902B17ADB /* MDCShapedShadowLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapedShadowLayer.h; path = components/Shapes/src/MDCShapedShadowLayer.h; sourceTree = ""; }; + 4E2AA82D677A35A05B022A324E49F50C /* MDFTextAccessibility-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MDFTextAccessibility-dummy.m"; sourceTree = ""; }; + 4FF269172DE0E3442E369BAF00EAB3CD /* UICollectionViewController+MDCCardReordering.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UICollectionViewController+MDCCardReordering.h"; path = "components/Cards/src/UICollectionViewController+MDCCardReordering.h"; sourceTree = ""; }; + 51BA97E8B5085EFFB47BC9C0B785CEA7 /* lottie-ios */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "lottie-ios"; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5241F42DE93198A9743B520528DEBE0E /* MaskContainerLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MaskContainerLayer.swift; path = Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift; sourceTree = ""; }; + 525767FD21D481C033ED537000C2A5B1 /* LayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerModel.swift; path = Sources/Private/Model/Layers/LayerModel.swift; sourceTree = ""; }; + 5264280FEB93B40117162EB11A934890 /* MDCTypography.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCTypography.m; path = components/Typography/src/MDCTypography.m; sourceTree = ""; }; + 5278EC208A2BA0E35B57E4AE2C020987 /* MDFInternationalization-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFInternationalization-prefix.pch"; sourceTree = ""; }; + 536C19C18245423A6AFB8166C8F93F35 /* SerialTaskQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SerialTaskQueue.swift; path = Chatto/Source/SerialTaskQueue.swift; sourceTree = ""; }; + 553199DA713332B2A7CE2BC1B053344C /* GradientValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift; sourceTree = ""; }; + 55C5A1F93EFADC2096EE6DC9303B38A8 /* NodeProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NodeProperty.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift; sourceTree = ""; }; + 561E46EAC325F966EA9DBA55F0689607 /* MDCShadowLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShadowLayer.m; path = components/ShadowLayer/src/MDCShadowLayer.m; sourceTree = ""; }; + 5739897ED45AAC422F0EECBA1D75F58A /* GroupInterpolator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupInterpolator.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift; sourceTree = ""; }; + 57C4846B514755F166442D8D10CEA565 /* URLConvertible+URLRequestConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLConvertible+URLRequestConvertible.swift"; path = "Source/URLConvertible+URLRequestConvertible.swift"; sourceTree = ""; }; + 5923674729AC49BE14806A1AEB0FB8DE /* Pods-iosTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosTests-Info.plist"; sourceTree = ""; }; + 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+MaterialBidi.h"; path = "Sources/NSString+MaterialBidi.h"; sourceTree = ""; }; + 5AF2CED0DB3829D4D4BE54A78C1072DB /* NodePropertyMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NodePropertyMap.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift; sourceTree = ""; }; + 5BA153CC755CD4895140457FB4274997 /* MDCShadowElevations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadowElevations.h; path = components/ShadowElevations/src/MDCShadowElevations.h; sourceTree = ""; }; + 5BAC390311C727CE7C129F84BD9B09B9 /* AnimationSubview.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationSubview.swift; path = Sources/Public/iOS/AnimationSubview.swift; sourceTree = ""; }; + 5D06BA269EAAEF5A59A416177B7571E8 /* MDFTextAccessibility-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFTextAccessibility-prefix.pch"; sourceTree = ""; }; + 5D714F498A6A5E7FC8FB92A3819A449A /* MDCShadowsCollection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShadowsCollection.m; path = components/Shadow/src/MDCShadowsCollection.m; sourceTree = ""; }; + 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5DC202126AC7DEA521D46EACE0A8CB33 /* MDCRippleTouchController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRippleTouchController.m; path = components/Ripple/src/MDCRippleTouchController.m; sourceTree = ""; }; + 5DDC53D7CBF3A251E463BB224DD12DCE /* TextDocument.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextDocument.swift; path = Sources/Private/Model/Text/TextDocument.swift; sourceTree = ""; }; + 5EA8C38C23FD8FDD5FE62F2CFD7B55B9 /* UIView+MaterialElevationResponding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MaterialElevationResponding.m"; path = "components/Elevation/src/UIView+MaterialElevationResponding.m"; sourceTree = ""; }; + 5FB83587411063DC53155C1A2C082DD3 /* MDCRectangleShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRectangleShapeGenerator.m; path = components/Shapes/src/MDCRectangleShapeGenerator.m; sourceTree = ""; }; + 5FD3683EBDED4FBCF7CF58DBB4864E15 /* MaterialComponents-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MaterialComponents-Info.plist"; sourceTree = ""; }; + 600ED7A50816D1A3222193C97DEA9BC3 /* MDCLegacyInkLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCLegacyInkLayer.m; path = components/Ink/src/private/MDCLegacyInkLayer.m; sourceTree = ""; }; + 6102438DC85CE0EBEE3E8E37512F0869 /* BaseChatItemPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseChatItemPresenter.swift; path = "Chatto/Source/Chat Items/BaseChatItemPresenter.swift"; sourceTree = ""; }; + 615BC7DD9F6466F845804084D0B12D66 /* lottie-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "lottie-ios.debug.xcconfig"; sourceTree = ""; }; + 617F2D7F52382550906F7F058A9681F9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 61814AFE969DF03C54E6B9E503F28302 /* ItemsExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ItemsExtension.swift; path = Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift; sourceTree = ""; }; + 619B96C839D9C1480A0A40E988C63AFD /* GradientFillRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientFillRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift; sourceTree = ""; }; + 62A2F94AFB07146E8A9A5DC4985F19BA /* MDCRippleTouchController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleTouchController.h; path = components/Ripple/src/MDCRippleTouchController.h; sourceTree = ""; }; + 62FE3D47542C17E69E982939D7C4342C /* CGFloatExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CGFloatExtensions.swift; path = Sources/Private/Utility/Extensions/CGFloatExtensions.swift; sourceTree = ""; }; + 631CC31B1866E988A56C286024B8A805 /* lottie-ios-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "lottie-ios-umbrella.h"; sourceTree = ""; }; + 63431295907542E5A9DD8630DC9338BA /* GroupNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift; sourceTree = ""; }; + 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MaterialComponents.release.xcconfig; sourceTree = ""; }; + 64882FB458891751B0C4E20C1C23D5F1 /* FilepathImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FilepathImageProvider.swift; path = Sources/Public/iOS/FilepathImageProvider.swift; sourceTree = ""; }; + 6587D8532A9E9E13BA4B37E713566C3B /* URLSessionConfiguration+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLSessionConfiguration+Alamofire.swift"; path = "Source/URLSessionConfiguration+Alamofire.swift"; sourceTree = ""; }; + 65D2F1380362B7EE62D2678F79650A3A /* MDCRippleView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRippleView.m; path = components/Ripple/src/MDCRippleView.m; sourceTree = ""; }; + 65EAC9DA9D8A3173E14F6714F6DD597E /* CompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift; sourceTree = ""; }; + 6684560C13B020634CFCE92295A15B72 /* Session.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Session.swift; path = Source/Session.swift; sourceTree = ""; }; + 6741DCC6A7B256467254EDFBBDF05B3F /* MaterialComponents-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MaterialComponents-umbrella.h"; sourceTree = ""; }; + 678552B49FF59718A815C8CB72951E75 /* AnyNodeProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyNodeProperty.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift; sourceTree = ""; }; + 6828FC052E6421784F770E0D6078DDB6 /* MDCFlatButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFlatButton.m; path = components/Buttons/src/MDCFlatButton.m; sourceTree = ""; }; + 686DE3CEFB348F67B7F2E0CCDBED00A7 /* FloatValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FloatValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift; sourceTree = ""; }; + 6A035D2874E49F84C6A8FF8FBF6503C8 /* MDCCardCollectionCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCardCollectionCell.m; path = components/Cards/src/MDCCardCollectionCell.m; sourceTree = ""; }; + 6A7B2BF1390CAC2A1A79D280BA2AA739 /* MaterialShapes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShapes.h; path = components/Shapes/src/MaterialShapes.h; sourceTree = ""; }; + 6A9EA755014DD5DD4C27D7ECD93783FD /* DummyChatItemPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DummyChatItemPresenter.swift; path = "Chatto/Source/Chat Items/DummyChatItemPresenter.swift"; sourceTree = ""; }; + 6BB9978D69E4ABC8F7AE8425D9E1F048 /* StringExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StringExtensions.swift; path = Sources/Private/Utility/Extensions/StringExtensions.swift; sourceTree = ""; }; + 6BDB7E8223919BD683DE47719CEA1A69 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = ""; }; + 6C4F064EEF86F1829D1B928D11963739 /* BaseChatViewControllerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseChatViewControllerView.swift; path = Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift; sourceTree = ""; }; + 6E035C3FBB06190E0CE480284B8CF014 /* Group.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Group.swift; path = Sources/Private/Model/ShapeItems/Group.swift; sourceTree = ""; }; + 6E28A208954B54643898470FABAC2905 /* UIFont+MaterialTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialTypography.h"; path = "components/Typography/src/UIFont+MaterialTypography.h"; sourceTree = ""; }; + 6ED311731D2D73DBC56836B34F3D634A /* MDCShadowLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadowLayer.h; path = components/ShadowLayer/src/MDCShadowLayer.h; sourceTree = ""; }; + 6F2AF02B3727FEC99B62286905D9F172 /* Color.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Color.swift; path = Sources/Public/Primitives/Color.swift; sourceTree = ""; }; + 6F5C3BCCB2FBBD3DC6701A99A1EC16E2 /* MDCLegacyInkLayerRippleDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCLegacyInkLayerRippleDelegate.h; path = components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h; sourceTree = ""; }; + 6F6DE9329322864658D938C211EAFA09 /* MDCRippleLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleLayerDelegate.h; path = components/Ripple/src/private/MDCRippleLayerDelegate.h; sourceTree = ""; }; + 701CF46760379C28084078F7D4BAFF32 /* AlamofireExtended.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AlamofireExtended.swift; path = Source/AlamofireExtended.swift; sourceTree = ""; }; + 7053BE770B19CADCC67259D2058FBFE5 /* MDFImageCalculations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFImageCalculations.h; path = src/private/MDFImageCalculations.h; sourceTree = ""; }; + 708C0285547318992A3989A6415E5D6B /* AnimationContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationContext.swift; path = Sources/Private/Utility/Helpers/AnimationContext.swift; sourceTree = ""; }; + 713A971DE11ED11949FB41259A01995C /* Merge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Merge.swift; path = Sources/Private/Model/ShapeItems/Merge.swift; sourceTree = ""; }; + 72E3FDB351A03387E5608B6C84700D31 /* Pods-iosTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosTests.debug.xcconfig"; sourceTree = ""; }; + 73500719BEBC67E98C2432BA726CF2BD /* UIColor+MaterialBlending.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MaterialBlending.h"; path = "components/private/Color/src/UIColor+MaterialBlending.h"; sourceTree = ""; }; + 73D70DCDE236059947C0BF795B512C84 /* Protected.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Protected.swift; path = Source/Protected.swift; sourceTree = ""; }; + 74405B4230DF6726303FA767434E6A14 /* CurveVertex.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CurveVertex.swift; path = Sources/Private/Utility/Primitives/CurveVertex.swift; sourceTree = ""; }; + 748CA1EDB7765D88F678941AA746AA9E /* CachedResponseHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CachedResponseHandler.swift; path = Source/CachedResponseHandler.swift; sourceTree = ""; }; + 75A55C9E6101E233016AB66DD634B926 /* TextAnimator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextAnimator.swift; path = Sources/Private/Model/Text/TextAnimator.swift; sourceTree = ""; }; + 7639C506BC1DBC0CA831E0E2A106D658 /* TextLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextLayerModel.swift; path = Sources/Private/Model/Layers/TextLayerModel.swift; sourceTree = ""; }; + 76D52B7A10D74C906F9C84CC976B1B2D /* PointValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PointValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift; sourceTree = ""; }; + 777B7794311A16E26C00B24639C365CB /* ChatItemCompanion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemCompanion.swift; path = "Chatto/Source/Chat Items/ChatItemCompanion.swift"; sourceTree = ""; }; + 77EF08C7D4C2345726A07A8B9A265452 /* Mask.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Mask.swift; path = Sources/Private/Model/Objects/Mask.swift; sourceTree = ""; }; + 7800E126D44EA6D8FC842E5214971EF6 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = ""; }; + 7821F9D3A25C026AA6AFACB2A720A953 /* lottie-ios-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "lottie-ios-prefix.pch"; sourceTree = ""; }; + 7872898EA812A796E27F3664AA901902 /* MultipartUpload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartUpload.swift; path = Source/MultipartUpload.swift; sourceTree = ""; }; + 789CAA7B6C1E469B4554820ADC2147D3 /* MDCFontTraits.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFontTraits.m; path = components/Typography/src/private/MDCFontTraits.m; sourceTree = ""; }; + 78E1DDE875714E724C5D0109D3735095 /* AnimatorNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatorNode.swift; path = Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift; sourceTree = ""; }; + 79EA03EF06AA77426C2C84981497D6F8 /* MDCInkView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkView.h; path = components/Ink/src/MDCInkView.h; sourceTree = ""; }; + 7B96F646D146FE43BCB3C820D9573D99 /* ShapeContainerLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeContainerLayer.swift; path = Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift; sourceTree = ""; }; + 7BC6506BF9A73061A58868C0D592B9B1 /* PathNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathNode.swift; path = Sources/Private/NodeRenderSystem/Protocols/PathNode.swift; sourceTree = ""; }; + 7BF3276002056A8603CF35E5D0FDBB3B /* StringEncoding+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "StringEncoding+Alamofire.swift"; path = "Source/StringEncoding+Alamofire.swift"; sourceTree = ""; }; + 7C6DBEEB82DA69272704408A3AEBA6EC /* AnimatorNodeDebugging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatorNodeDebugging.swift; path = Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift; sourceTree = ""; }; + 7CE46363B5ADC47CE9B57A3335A26016 /* MaterialApplication.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialApplication.h; path = components/private/Application/src/MaterialApplication.h; sourceTree = ""; }; + 7DF304359AEBCFADD6BBD420523B223F /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; + 7E145BBB7A0266AF3CEF37E16F141F03 /* ColorExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ColorExtension.swift; path = Sources/Private/Utility/Primitives/ColorExtension.swift; sourceTree = ""; }; + 7E28ED602A75DB92340D7444DB1995A1 /* MDCCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCornerTreatment.h; path = components/Shapes/src/MDCCornerTreatment.h; sourceTree = ""; }; + 7F557E15F0C636BE0C4547A358DA8C93 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = ""; }; + 7FA25E16755D429C6CC771AD6044CEED /* Ellipse.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Ellipse.swift; path = Sources/Private/Model/ShapeItems/Ellipse.swift; sourceTree = ""; }; + 805B67A1EC691E2F297D0CB0A125C31B /* InterpolatableExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InterpolatableExtensions.swift; path = Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift; sourceTree = ""; }; + 8186408B3030AB74BE1331A431FB83D4 /* Chatto.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Chatto.debug.xcconfig; sourceTree = ""; }; + 81EFFCF10BFC1B08CD7DBED57D297675 /* PrecompAsset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PrecompAsset.swift; path = Sources/Private/Model/Assets/PrecompAsset.swift; sourceTree = ""; }; + 8246440519C85238FCFCF8F58392A86F /* MDCPathGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCPathGenerator.m; path = components/Shapes/src/MDCPathGenerator.m; sourceTree = ""; }; + 82AB7D6FD755ADF708F94AF789430203 /* PreCompLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PreCompLayerModel.swift; path = Sources/Private/Model/Layers/PreCompLayerModel.swift; sourceTree = ""; }; + 84C11F8849A9F9FA96193BDF6D47B305 /* StrokeNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StrokeNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift; sourceTree = ""; }; + 854600D850F2C3A98DF50071F6F04C09 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; + 870DC3835055FF27A861C151FF69B97C /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 87107EE4D974CAE959A375B662F4204F /* MDCFontTextStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFontTextStyle.h; path = components/Typography/src/MDCFontTextStyle.h; sourceTree = ""; }; + 8A95BFC36D1019120C1DEC0F6758A0ED /* CompatibleAnimationKeypath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompatibleAnimationKeypath.swift; path = Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift; sourceTree = ""; }; + 8ABFF571A3232481B34A73FDDE0F2B55 /* KeyframeExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyframeExtensions.swift; path = Sources/Private/Utility/Interpolatable/KeyframeExtensions.swift; sourceTree = ""; }; + 8AF96553AC1A4981ED8F74D3571CB86C /* ShapeLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeLayerModel.swift; path = Sources/Private/Model/Layers/ShapeLayerModel.swift; sourceTree = ""; }; + 8C2F6B0AED78566F963846D8D10A8CC9 /* MDFTextAccessibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFTextAccessibility.h; path = src/MDFTextAccessibility.h; sourceTree = ""; }; + 8C64B5F902ABF8446821C23B3779E31D /* UIColor+MaterialElevation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MaterialElevation.m"; path = "components/Elevation/src/UIColor+MaterialElevation.m"; sourceTree = ""; }; + 8C971CF5443E97989CD0DF44D9C18846 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; + 8CE15CF0E1981C1CA722C1B5121C088C /* UIFont+MaterialScalable.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialScalable.m"; path = "components/Typography/src/UIFont+MaterialScalable.m"; sourceTree = ""; }; + 8D0323B0058AFC25122470F8D9558B7D /* MaterialIcons.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialIcons.h; path = components/private/Icons/src/MaterialIcons.h; sourceTree = ""; }; + 8D1A663AAA238F5A6580850607B58FB9 /* UIView+MDCTimingFunction.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MDCTimingFunction.m"; path = "components/AnimationTiming/src/UIView+MDCTimingFunction.m"; sourceTree = ""; }; + 8D638A90314C4C723DF58CBA2D02DCF3 /* MDCTypographyUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCTypographyUtilities.h; path = components/Typography/src/private/MDCTypographyUtilities.h; sourceTree = ""; }; + 8DA90325405F33A5808A47E8BFFAEE6F /* MaterialButtons.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialButtons.h; path = components/Buttons/src/MaterialButtons.h; sourceTree = ""; }; + 8DBE44442938F2CBE3624C972232C24E /* ShapeTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeTransform.swift; path = Sources/Private/Model/ShapeItems/ShapeTransform.swift; sourceTree = ""; }; + 8DD0AD806399CC6AE3AA2B4F75C77C23 /* MaterialElevation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialElevation.h; path = components/Elevation/src/MaterialElevation.h; sourceTree = ""; }; + 8EE689E19EA5C8EF636167214A787615 /* Star.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Star.swift; path = Sources/Private/Model/ShapeItems/Star.swift; sourceTree = ""; }; + 8F1F807B149E883C56B85C5C055D651D /* MDFInternationalization-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MDFInternationalization-dummy.m"; sourceTree = ""; }; + 8F3ED95F3D3F150E9EF3F7B365B3BE9C /* MaterialComponents-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MaterialComponents-dummy.m"; sourceTree = ""; }; + 909F969A28FE09D5039EF1A274202B6D /* MDCRectangleShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRectangleShapeGenerator.h; path = components/Shapes/src/MDCRectangleShapeGenerator.h; sourceTree = ""; }; + 90A761C8F692CCA4FDA5BFC5DEBACE74 /* MaterialShapeLibrary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShapeLibrary.h; path = components/ShapeLibrary/src/MaterialShapeLibrary.h; sourceTree = ""; }; + 91855A9940FB42F19FD2875C45E2CB01 /* Interpolatable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Interpolatable.swift; path = Sources/Private/Utility/Interpolatable/Interpolatable.swift; sourceTree = ""; }; + 919EA980AF20A4FC75CE16B54373D398 /* MathKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MathKit.swift; path = Sources/Private/Utility/Extensions/MathKit.swift; sourceTree = ""; }; + 924ED3C5CACD9B01ED8F6ACDCE0843EE /* Pods-iosTests */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iosTests"; path = Pods_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 92BF3EBBBF215FDB97014A1C7F674282 /* MDCInkLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkLayer.h; path = components/Ink/src/private/MDCInkLayer.h; sourceTree = ""; }; + 931593E088B694A7D6B0D1FC4FE6C080 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; + 93520D487909F1B889485508132F1A96 /* Combine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Combine.swift; path = Source/Combine.swift; sourceTree = ""; }; + 940160C49FE72F93E569C21314AB30EE /* PathOutputNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathOutputNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift; sourceTree = ""; }; + 952959F2E1983393A6EE1CAF37FB84E1 /* Pods-ios-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ios-Info.plist"; sourceTree = ""; }; + 95DF7386D55C9F9642DC5077C341E35E /* MDCRoundedCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRoundedCornerTreatment.h; path = components/ShapeLibrary/src/MDCRoundedCornerTreatment.h; sourceTree = ""; }; + 9625E67A414049AFFBCF73EDB3F00B1B /* MDCShadow.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadow.h; path = components/Shadow/src/MDCShadow.h; sourceTree = ""; }; + 970D404B267F9ADF520FA00EC3700F65 /* AnimationContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationContainer.swift; path = Sources/Private/LayerContainers/AnimationContainer.swift; sourceTree = ""; }; + 97374F6103D866145B36386C14D1D8E5 /* KeyboardTracker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyboardTracker.swift; path = Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift; sourceTree = ""; }; + 97A0DF460C679110DBBD0ED8B315B64D /* MDFTextAccessibility-Bridging-Header.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDFTextAccessibility-Bridging-Header.h"; path = "src/MDFTextAccessibility-Bridging-Header.h"; sourceTree = ""; }; + 97BE17BC31FAF7F8B04D6EFFBBE78C80 /* MDCButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCButton.h; path = components/Buttons/src/MDCButton.h; sourceTree = ""; }; + 985D00F7F6511C37B9DECF5DBEB3B451 /* MDCFlatButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFlatButton.h; path = components/Buttons/src/MDCFlatButton.h; sourceTree = ""; }; + 98EB1455F30B28A8F4F59C33E8CB38D2 /* MaterialMath.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialMath.h; path = components/private/Math/src/MaterialMath.h; sourceTree = ""; }; + 9906975C7ABA6E0912B4362EFF02B32C /* MDCInkGestureRecognizer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkGestureRecognizer.m; path = components/Ink/src/MDCInkGestureRecognizer.m; sourceTree = ""; }; + 9A1732F4D5612B18CF4BCB5A0D5F9A8C /* MDCFloatingButtonModeAnimator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFloatingButtonModeAnimator.m; path = components/Buttons/src/private/MDCFloatingButtonModeAnimator.m; sourceTree = ""; }; + 9A247D7873C2106F7E0006C6D3FA0DC9 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; + 9A2AC0B8BAC6EF061EBC28825653DF99 /* MDFInternationalization */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MDFInternationalization; path = MDFInternationalization.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A370E644FBCA6F70723C4C1D6720158 /* UICollectionViewController+MDCCardReordering.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UICollectionViewController+MDCCardReordering.m"; path = "components/Cards/src/UICollectionViewController+MDCCardReordering.m"; sourceTree = ""; }; + 9B246A845EEBC2196E07F54E35ACA25A /* LayerTransformNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerTransformNode.swift; path = Sources/Private/LayerContainers/Utility/LayerTransformNode.swift; sourceTree = ""; }; + 9B6A73993CD9CF59863BB4C36386EEAE /* GradientFill.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientFill.swift; path = Sources/Private/Model/ShapeItems/GradientFill.swift; sourceTree = ""; }; + 9CF0A7BE4E58D6697CCF7D0FA0CBBDFB /* TextAnimatorNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextAnimatorNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift; sourceTree = ""; }; + 9D1132D92154CA46250E791A60FC2C43 /* MDCPathGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCPathGenerator.h; path = components/Shapes/src/MDCPathGenerator.h; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9DE63B387DA24E48BA2EDE84C68DC8DE /* MDCTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCTypography.h; path = components/Typography/src/MDCTypography.h; sourceTree = ""; }; + 9E173EE03A996655ED2311253DA58EB9 /* RenderNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RenderNode.swift; path = Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift; sourceTree = ""; }; + 9ECD5C7C84C99618AF2620886CE9964E /* Pods-ios-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ios-acknowledgements.markdown"; sourceTree = ""; }; + 9FF5E5B45041E7444D6447C17872668F /* MDCRaisedButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRaisedButton.m; path = components/Buttons/src/MDCRaisedButton.m; sourceTree = ""; }; + A07731940B92711A5CA2D9CF2C7DA38F /* FillRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift; sourceTree = ""; }; + A0D4358B34E866143ECD08151FA7D559 /* LayerTextProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerTextProvider.swift; path = Sources/Private/LayerContainers/Utility/LayerTextProvider.swift; sourceTree = ""; }; + A1BFB72AD30F0DE0585E804791B833D6 /* Transform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Transform.swift; path = Sources/Private/Model/Objects/Transform.swift; sourceTree = ""; }; + A2502619916EAC0AB2E21B1A54DB347D /* FillI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillI.swift; path = Sources/Private/Model/ShapeItems/FillI.swift; sourceTree = ""; }; + A2783799D5308869A35311BBDF845364 /* UIView+MaterialRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MaterialRTL.m"; path = "Sources/UIView+MaterialRTL.m"; sourceTree = ""; }; + A287971E3E70E72ECCDFC5485A8B6D35 /* MDCShapeMediator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapeMediator.h; path = components/Shapes/src/MDCShapeMediator.h; sourceTree = ""; }; + A2C83FB5770E9E4004C80C5217E3CD69 /* NSArray+MDFUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+MDFUtils.m"; path = "src/private/NSArray+MDFUtils.m"; sourceTree = ""; }; + A34157B3708EDB7D17422B3D92F591D2 /* MaterialComponents */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MaterialComponents; path = MaterialComponents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A3619A8910E5127A2A7EBD2417924A3D /* RetryPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RetryPolicy.swift; path = Source/RetryPolicy.swift; sourceTree = ""; }; + A422591030AC5B802703CAAFA3536ED3 /* Vectors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Vectors.swift; path = Sources/Public/Primitives/Vectors.swift; sourceTree = ""; }; + A45EFEC8F1A0F1093631AF60C90BB060 /* Font.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Font.swift; path = Sources/Private/Model/Text/Font.swift; sourceTree = ""; }; + A488EF6B3932789A199EFF0E48669A8F /* ChatItemProtocolDefinitions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemProtocolDefinitions.swift; path = "Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift"; sourceTree = ""; }; + A4D99A5054C55BC32C98A5EF91CB5BDD /* CAMediaTimingFunction+MDCAnimationTiming.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "CAMediaTimingFunction+MDCAnimationTiming.m"; path = "components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m"; sourceTree = ""; }; + A575DBC967BD1B9824384DE5EAC71824 /* MaterialColor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialColor.h; path = components/private/Color/src/MaterialColor.h; sourceTree = ""; }; + A5ED19C47A3B111AEEAE6C09A8E508BB /* Pods-ios.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-ios.modulemap"; sourceTree = ""; }; + A67A50DB4E82C2861D15BCA11383D919 /* MDCInkGestureRecognizer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkGestureRecognizer.h; path = components/Ink/src/MDCInkGestureRecognizer.h; sourceTree = ""; }; + A6DCF5983D0F4C2FB0E19A0D320B789B /* MaterialCards.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialCards.h; path = components/Cards/src/MaterialCards.h; sourceTree = ""; }; + A71810DBABD1DE3268F0A777230A85A2 /* MDCRippleViewDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleViewDelegate.h; path = components/Ripple/src/MDCRippleViewDelegate.h; sourceTree = ""; }; + A8AB1E1A488B0F97C034BFD4ECB9C11B /* MaterialInk.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialInk.h; path = components/Ink/src/MaterialInk.h; sourceTree = ""; }; + A9B79521A3553AD1005FA6A9670CEBF2 /* Pods-iosTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-iosTests.modulemap"; sourceTree = ""; }; + AB54FAD6E9F36E88C0A18E94EB070CA3 /* MDCInkTouchController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkTouchController.h; path = components/Ink/src/MDCInkTouchController.h; sourceTree = ""; }; + ABED2A4D6F3787803F5376C66CB9544C /* MaterialShadow.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShadow.h; path = components/Shadow/src/MaterialShadow.h; sourceTree = ""; }; + AC2834BC1BEF7C025DE72333BE447006 /* UIApplication+MDCAppExtensions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+MDCAppExtensions.m"; path = "components/private/Application/src/UIApplication+MDCAppExtensions.m"; sourceTree = ""; }; + AC80A65D7F1D3E258FEC205047C3EB0A /* MDCTriangleEdgeTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCTriangleEdgeTreatment.h; path = components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h; sourceTree = ""; }; + AD480CF250BF63F248837A06A6F55B90 /* AnimationFontProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationFontProvider.swift; path = Sources/Public/FontProvider/AnimationFontProvider.swift; sourceTree = ""; }; + AD54C4E77B772162BFA739B3159C945F /* MDFTextAccessibility.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MDFTextAccessibility.modulemap; sourceTree = ""; }; + AE7DFA57EB0C03A4757CAB9256086346 /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = ""; }; + AEFD189E2BBDDBFEFE3D285ECEA7330D /* Bundle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Bundle.swift; path = Sources/Private/Model/Extensions/Bundle.swift; sourceTree = ""; }; + AF43E371B84D82B4BE12084C4D22A945 /* MDCInkLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkLayerDelegate.h; path = components/Ink/src/private/MDCInkLayerDelegate.h; sourceTree = ""; }; + AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSLocale+MaterialRTL.h"; path = "Sources/NSLocale+MaterialRTL.h"; sourceTree = ""; }; + B120981A19A9C956A2941EB064D1FD1D /* MDFInternationalization-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFInternationalization-umbrella.h"; sourceTree = ""; }; + B15025A80B5750A7F05A489683577910 /* MDCLegacyInkLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCLegacyInkLayer.h; path = components/Ink/src/private/MDCLegacyInkLayer.h; sourceTree = ""; }; + B17C76564686F46F80432DCE2D053940 /* LottieView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LottieView.swift; path = Sources/Public/iOS/LottieView.swift; sourceTree = ""; }; + B19302162D052125FE5F7992C054A240 /* GradientFillNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientFillNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift; sourceTree = ""; }; + B1F119B189824C232697C892AF030E98 /* Pods-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ios.release.xcconfig"; sourceTree = ""; }; + B1F29114317DB38E4711B41B1C6376C9 /* Chatto-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Chatto-prefix.pch"; sourceTree = ""; }; + B25B52A26AE822B9B2F58EA6B9E7E240 /* PassThroughOutputNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PassThroughOutputNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift; sourceTree = ""; }; + B27333CDD645D2D5CF612BB67776A72B /* UIApplication+MDCAppExtensions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIApplication+MDCAppExtensions.h"; path = "components/private/Application/src/UIApplication+MDCAppExtensions.h"; sourceTree = ""; }; + B2EE0D77753F90766C6F5C64AAADA005 /* CompoundBezierPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompoundBezierPath.swift; path = Sources/Private/Utility/Primitives/CompoundBezierPath.swift; sourceTree = ""; }; + B3F0F84872DE9FA147E94B5562B8FE68 /* AnimationKeypathExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationKeypathExtension.swift; path = Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift; sourceTree = ""; }; + B3F1C077ED9B83ED31FAF400299DC965 /* MaterialShadowLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShadowLayer.h; path = components/ShadowLayer/src/MaterialShadowLayer.h; sourceTree = ""; }; + B411BF2903D79884DFBA1F089404388E /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = ""; }; + B443745DA64B8F614899680B793F7108 /* MaterialAnimationTiming.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialAnimationTiming.h; path = components/AnimationTiming/src/MaterialAnimationTiming.h; sourceTree = ""; }; + B4C869DD8FBC246E0FE2199DCEB50D3D /* Glyph.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Glyph.swift; path = Sources/Private/Model/Text/Glyph.swift; sourceTree = ""; }; + B50C69F9BC8FB745BE744D03394900F8 /* MDFTextAccessibility-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MDFTextAccessibility-Info.plist"; sourceTree = ""; }; + B518C0072DFC3DBA1FA3C49569B1F3DA /* MDCCardCollectionCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCardCollectionCell.h; path = components/Cards/src/MDCCardCollectionCell.h; sourceTree = ""; }; + B613FA13F2264BCF72F6DE1BD3588C52 /* AnimationPublic.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationPublic.swift; path = Sources/Public/Animation/AnimationPublic.swift; sourceTree = ""; }; + B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFInternationalization.h; path = Sources/MDFInternationalization.h; sourceTree = ""; }; + B829E8E11D02D1970F8C8FF989A82B64 /* UIColor+MaterialDynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MaterialDynamic.h"; path = "components/private/Color/src/UIColor+MaterialDynamic.h"; sourceTree = ""; }; + B86684B080F0E3D4C1D38F093601783B /* ShapeItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeItem.swift; path = Sources/Private/Model/ShapeItems/ShapeItem.swift; sourceTree = ""; }; + B867765B8E02C5AE1B3C6A2B771DE65C /* UIFont+MaterialScalable.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialScalable.h"; path = "components/Typography/src/UIFont+MaterialScalable.h"; sourceTree = ""; }; + B9158E5A2523A49B53CFB65D466371AE /* Pods-iosTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iosTests-acknowledgements.markdown"; sourceTree = ""; }; + B93D411155688717E99B55AE5D57CF10 /* Chatto-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Chatto-dummy.m"; sourceTree = ""; }; + B995834698359995E0262F3F70C896A9 /* URLRequest+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLRequest+Alamofire.swift"; path = "Source/URLRequest+Alamofire.swift"; sourceTree = ""; }; + B9C5DEABB0B24A9A6A4FB076F829A033 /* SolidCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SolidCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift; sourceTree = ""; }; + BA395024B9E67C9EE8187839618E96FB /* MDCInkView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkView.m; path = components/Ink/src/MDCInkView.m; sourceTree = ""; }; + BA603856ED143A5A0536C7F9F8785FFF /* InputPositionControlling.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InputPositionControlling.swift; path = Chatto/Source/ChatController/InputPositionControlling.swift; sourceTree = ""; }; + BAA843D119FBBE1873DB868B2C42F47A /* ChatDataSourceProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatDataSourceProtocol.swift; path = Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift; sourceTree = ""; }; + BBCBAB7ADDB2D552DB46A6DEE6C9F5A7 /* Chatto.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Chatto.modulemap; sourceTree = ""; }; + BBF566D92B960CE6B69285672FDE16A8 /* MDCShapedView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShapedView.m; path = components/Shapes/src/MDCShapedView.m; sourceTree = ""; }; + BC918096888E65550CADD29286F70E6B /* NSString+MaterialBidi.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+MaterialBidi.m"; path = "Sources/NSString+MaterialBidi.m"; sourceTree = ""; }; + BD14A3955F01AD7D390CC5763E43CEFE /* AnimatedSwitch.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedSwitch.swift; path = Sources/Public/iOS/AnimatedSwitch.swift; sourceTree = ""; }; + BE3893C08724F3D82CF1D199CF98F274 /* Chatto */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Chatto; path = Chatto.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE5AE749191583AB288E955A216FBDEE /* MDFInternationalization.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFInternationalization.debug.xcconfig; sourceTree = ""; }; + BF467C869E0EBC17B9CB2B58A166300C /* Pods-ios-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ios-acknowledgements.plist"; sourceTree = ""; }; + BF7CDBB13E9F0AC3175BDF383C468787 /* MDCElevationOverriding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCElevationOverriding.h; path = components/Elevation/src/MDCElevationOverriding.h; sourceTree = ""; }; + BF9EDA0CCF4ACD15ECC442F77B0DD927 /* ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist"; sourceTree = ""; }; + BFA5A232C8B4D4671F587B33DCEF5FB4 /* CollectionChanges.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CollectionChanges.swift; path = Chatto/Source/ChatController/Collaborators/CollectionChanges.swift; sourceTree = ""; }; + C1C78482FC811B81F0499146DB6F8592 /* SizeValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SizeValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift; sourceTree = ""; }; + C1FAB2482AA7F33F1F62927A25599EF4 /* GradientStrokeNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientStrokeNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift; sourceTree = ""; }; + C27430E91F40A49F302EA2C974453696 /* HTTPMethod.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HTTPMethod.swift; path = Source/HTTPMethod.swift; sourceTree = ""; }; + C290807FDFEFCB92E5FABE8FE851443C /* DashPattern.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DashPattern.swift; path = Sources/Private/Model/Objects/DashPattern.swift; sourceTree = ""; }; + C30F85DF0D50528EF9BEE8A2BA482034 /* LayerDebugging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerDebugging.swift; path = Sources/Private/Utility/Debugging/LayerDebugging.swift; sourceTree = ""; }; + C31592E117E0046F2678B3066EB06AC3 /* CompositionLayersInitializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompositionLayersInitializer.swift; path = Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift; sourceTree = ""; }; + C364DE9E9FEDDD730DBF09F77CB0B143 /* MDCIcons.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCIcons.h; path = components/private/Icons/src/MDCIcons.h; sourceTree = ""; }; + C43092A50861A2F799989439A7DCBCAF /* Alamofire.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.debug.xcconfig; sourceTree = ""; }; + C5494CF8D45C3C7880D1C7684088125B /* Chatto.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Chatto.release.xcconfig; sourceTree = ""; }; + C59B9A40073373B78D37C4937FD3E223 /* Chatto-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Chatto-Info.plist"; sourceTree = ""; }; + C67AF078C9EE64DED1961778EFF012A0 /* EllipseNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EllipseNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift; sourceTree = ""; }; + C73D5045364BD3202565B4FDB54AE37A /* MDCShadowsCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadowsCollection.h; path = components/Shadow/src/MDCShadowsCollection.h; sourceTree = ""; }; + C7B54C5101F5D61940E8C3AE0858E0A6 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; + C7C60015DC526524C5DC274704897564 /* MDCFloatingButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFloatingButton.m; path = components/Buttons/src/MDCFloatingButton.m; sourceTree = ""; }; + C80B62ED9C592FA542C65C50680BD2D3 /* PolygonNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PolygonNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift; sourceTree = ""; }; + C8F19458534DF74ABBBC1D9171D2FA1A /* KeyframeGroup.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyframeGroup.swift; path = Sources/Private/Model/Keyframes/KeyframeGroup.swift; sourceTree = ""; }; + CA6F96BF2DFC0E11E6A55539B9225C94 /* ChatCollectionViewLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatCollectionViewLayout.swift; path = Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift; sourceTree = ""; }; + CB094FC5A0B58CDEC708A4D9A8189F89 /* AuthenticationInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthenticationInterceptor.swift; path = Source/AuthenticationInterceptor.swift; sourceTree = ""; }; + CBBE66515626EA5EE7282EF95C8275B3 /* MDCCurvedCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCurvedCornerTreatment.m; path = components/ShapeLibrary/src/MDCCurvedCornerTreatment.m; sourceTree = ""; }; + CE0A65CE9CFA027DECC15DDE7C84261A /* GradientStrokeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientStrokeRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift; sourceTree = ""; }; + CE99EB6CC9C88A5D592843A9171EC23B /* AnyValueContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyValueContainer.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift; sourceTree = ""; }; + CEE878F352FF70522E2B4041CB792B24 /* MDFTextAccessibility.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFTextAccessibility.debug.xcconfig; sourceTree = ""; }; + CEF8701D965CC5D013B9CC49B8B228E8 /* SingleValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SingleValueProvider.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift; sourceTree = ""; }; + D0F908B38DD2B1F35BBC0B8F214C6DC0 /* MaterialShadowElevationsDummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MaterialShadowElevationsDummy.m; path = components/ShadowElevations/src/MaterialShadowElevationsDummy.m; sourceTree = ""; }; + D25173317D99ECFE9E0ECC69FCEC95FA /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = ""; }; + D2F86DF24DDC31E73E6DC1AD1E8E22E6 /* UIColor+MaterialElevation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MaterialElevation.h"; path = "components/Elevation/src/UIColor+MaterialElevation.h"; sourceTree = ""; }; + D3414E16D6767D8F8FBAB002ECBAA6CD /* MDCInkLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkLayer.m; path = components/Ink/src/private/MDCInkLayer.m; sourceTree = ""; }; + D390D3DEF1579285A22574E5E6EA97D0 /* UIFont+MaterialTypography.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialTypography.m"; path = "components/Typography/src/UIFont+MaterialTypography.m"; sourceTree = ""; }; + D3C80D0A870376D3DAB74C8D106BFF8C /* MaterialMathDummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MaterialMathDummy.m; path = components/private/Math/src/MaterialMathDummy.m; sourceTree = ""; }; + D6E67B621B39259F3CED1DE2F43E57AE /* MDCRippleTouchControllerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleTouchControllerDelegate.h; path = components/Ripple/src/MDCRippleTouchControllerDelegate.h; sourceTree = ""; }; + D720E1DADE8B1BB169230ADFA11012E3 /* Pods-ios-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-ios-umbrella.h"; sourceTree = ""; }; + D7F58CC76B154EE1FFCEBFD29458765F /* BaseChatViewController+CellPanGestureHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+CellPanGestureHandler.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift"; sourceTree = ""; }; + D80EB9AAB47357453596394BFD2FBD6D /* ValueContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ValueContainer.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift; sourceTree = ""; }; + D8F1688277322547EA5D6CCDF2C94C1F /* MDCFloatingButton+Animation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MDCFloatingButton+Animation.m"; path = "components/Buttons/src/MDCFloatingButton+Animation.m"; sourceTree = ""; }; + DA0CD727B37100536B0582E4ACA83844 /* StarNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StarNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift; sourceTree = ""; }; + DAA556AD9FDEF109652EAA16FF14D299 /* UIView+MaterialElevationResponding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MaterialElevationResponding.h"; path = "components/Elevation/src/UIView+MaterialElevationResponding.h"; sourceTree = ""; }; + DAEF5AB5FA15E20986DA16EBD0840CA2 /* BundleImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BundleImageProvider.swift; path = Sources/Public/iOS/BundleImageProvider.swift; sourceTree = ""; }; + DB0EBEFA02E716358C707E8E92A56CC2 /* StrokeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StrokeRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift; sourceTree = ""; }; + DB1E998F48C65751461A4CB1E13E45C5 /* MDCCurvedRectShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCurvedRectShapeGenerator.h; path = components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h; sourceTree = ""; }; + DB84425C5097649DB7039BCDFCA4DB6C /* Pods-ios */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-ios"; path = Pods_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DBCD73AF1D7111668851A56E369B30AF /* Stroke.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Stroke.swift; path = Sources/Private/Model/ShapeItems/Stroke.swift; sourceTree = ""; }; + DE1B915229A73A86A29689D51EBCEE8E /* Chatto-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Chatto-umbrella.h"; sourceTree = ""; }; + DE6A0C29D359F8F037F7DF2A21B78FC4 /* EventMonitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EventMonitor.swift; path = Source/EventMonitor.swift; sourceTree = ""; }; + DF79A6BB5BC283B6F1FFF7394D1ABD51 /* MaterialIcons+ic_check_circle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MaterialIcons+ic_check_circle.h"; path = "components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h"; sourceTree = ""; }; + E0CB865665244183E4B9659E94101F9B /* TrimPathNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TrimPathNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift; sourceTree = ""; }; + E0D7B9CC58E107433D11681261211BE1 /* LayerFontProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerFontProvider.swift; path = Sources/Private/LayerContainers/Utility/LayerFontProvider.swift; sourceTree = ""; }; + E10CAC47530EA0936ECF4F5B18F41899 /* ShapeCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift; sourceTree = ""; }; + E1470EA198559C0288DDF4068CAFB6ED /* MDFInternationalization.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MDFInternationalization.modulemap; sourceTree = ""; }; + E20AA7DE19CA8DB5FD1FE6636C4C6E89 /* RectNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RectNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/RectNode.swift; sourceTree = ""; }; + E22A0783A99782B1368A7C295F05B893 /* Marker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Marker.swift; path = Sources/Private/Model/Objects/Marker.swift; sourceTree = ""; }; + E3B32DE56F884A29871DDC06DEFDD299 /* FillNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift; sourceTree = ""; }; + E3D75AB0F4CDA59E7BB16BD81745B956 /* Shape.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Shape.swift; path = Sources/Private/Model/ShapeItems/Shape.swift; sourceTree = ""; }; + E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + E58F9B821896E6DE3629364AB9B21F35 /* MaterialAvailability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialAvailability.h; path = components/Availability/src/MaterialAvailability.h; sourceTree = ""; }; + E5A162FEC0123631BFA766AC1CD2E593 /* AnyValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyValueProvider.swift; path = Sources/Public/DynamicProperties/AnyValueProvider.swift; sourceTree = ""; }; + E6966E76E2E05FEC74DB6972B4F38FCC /* AnimationViewInitializers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationViewInitializers.swift; path = Sources/Public/Animation/AnimationViewInitializers.swift; sourceTree = ""; }; + E69B2A401E928B1CBB9CD4B02BEE853D /* UIView+MDCTimingFunction.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MDCTimingFunction.h"; path = "components/AnimationTiming/src/UIView+MDCTimingFunction.h"; sourceTree = ""; }; + E6ACC937F5C195801B15F2AB3B92101F /* GradientStroke.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientStroke.swift; path = Sources/Private/Model/ShapeItems/GradientStroke.swift; sourceTree = ""; }; + E741DFF8BC3B7CB733B6417AE217AEC1 /* Pods-iosTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosTests-frameworks.sh"; sourceTree = ""; }; + E81ECAF8BBE1DB54276ED819382CA9FA /* MDCRippleLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleLayer.h; path = components/Ripple/src/private/MDCRippleLayer.h; sourceTree = ""; }; + E94E88E183E7AC90A64BD46E39E2C85E /* LRUAnimationCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LRUAnimationCache.swift; path = Sources/Public/AnimationCache/LRUAnimationCache.swift; sourceTree = ""; }; + E9E21A4FEF3A460EFF2C8B38BBA65C25 /* Rectangle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Rectangle.swift; path = Sources/Private/Model/ShapeItems/Rectangle.swift; sourceTree = ""; }; + EA26F20034BDBF94780CF01106D4CD64 /* Alamofire-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Alamofire-Info.plist"; sourceTree = ""; }; + EA378A26B99BBD94560AE1C8C02B11F6 /* BaseChatViewController+Changes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+Changes.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+Changes.swift"; sourceTree = ""; }; + EA9BAA45A6BA523F01C4735963DDDADC /* MDCCutCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCutCornerTreatment.m; path = components/ShapeLibrary/src/MDCCutCornerTreatment.m; sourceTree = ""; }; + EB57E704596E48FF3325A1F46B6E783C /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + EB7B1A690B1D232B90A70E4E318BB297 /* Asset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Asset.swift; path = Sources/Private/Model/Assets/Asset.swift; sourceTree = ""; }; + EEBE2EE981029A2CA291F17EA8723B03 /* UIFontDescriptor+MaterialTypography.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFontDescriptor+MaterialTypography.m"; path = "components/Typography/src/UIFontDescriptor+MaterialTypography.m"; sourceTree = ""; }; + F1CD9DD5A9448FED236DB4E8AAF6C8A4 /* Trim.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Trim.swift; path = Sources/Private/Model/ShapeItems/Trim.swift; sourceTree = ""; }; + F1E0BB0AD007791BD42D66DF1F4DB387 /* NullCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NullCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift; sourceTree = ""; }; + F2F4B5DB7011E5176A063CF3110CD09E /* AnimationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationView.swift; path = Sources/Public/Animation/AnimationView.swift; sourceTree = ""; }; + F49594B80BEB495C857FE4A0C034A387 /* UIFont+MaterialTypographyPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialTypographyPrivate.h"; path = "components/Typography/src/private/UIFont+MaterialTypographyPrivate.h"; sourceTree = ""; }; + F4F84C77FED35072FBA03ADBD28FF9F3 /* AnimatedButton.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedButton.swift; path = Sources/Public/iOS/AnimatedButton.swift; sourceTree = ""; }; + F5922665F1DC85A6D8A92E2E648D19BE /* Pods-iosTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iosTests-dummy.m"; sourceTree = ""; }; + F5CCE660C00123EB989E5679091A134A /* MDFColorCalculations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFColorCalculations.h; path = src/private/MDFColorCalculations.h; sourceTree = ""; }; + F6090940C42D19C30699D9DFCD46FEA1 /* RequestTaskMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestTaskMap.swift; path = Source/RequestTaskMap.swift; sourceTree = ""; }; + F6BB438AED2F3B6F61B65BEBABF41B1F /* MDCShapeMediator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShapeMediator.m; path = components/Shapes/src/MDCShapeMediator.m; sourceTree = ""; }; + F6E10A41E382E72F54FC92EF85F00963 /* AnimationCacheProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationCacheProvider.swift; path = Sources/Public/AnimationCache/AnimationCacheProvider.swift; sourceTree = ""; }; + F6EB232EC11EB49D9DA52EDFB9660747 /* AnimatedControl.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedControl.swift; path = Sources/Public/iOS/AnimatedControl.swift; sourceTree = ""; }; + F711AC4E23408CDD020388C5E5D71009 /* AnimationImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationImageProvider.swift; path = Sources/Public/ImageProvider/AnimationImageProvider.swift; sourceTree = ""; }; + F75BEC31BE7157E03DF220C390111D13 /* AnimationTextProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationTextProvider.swift; path = Sources/Public/TextProvider/AnimationTextProvider.swift; sourceTree = ""; }; + F873EC876A8574DA2C7425CA4C3BC418 /* MDCFloatingButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFloatingButton.h; path = components/Buttons/src/MDCFloatingButton.h; sourceTree = ""; }; + F90CBF2D1339BB6CDDAFF78CC3B59761 /* MDCButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCButton.m; path = components/Buttons/src/MDCButton.m; sourceTree = ""; }; + F9C9C35B39EFD7AD82253D0A2AD91B56 /* Pods-iosTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosTests.release.xcconfig"; sourceTree = ""; }; + FA343ABB695B730239978538A94F38D6 /* ParameterEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoder.swift; path = Source/ParameterEncoder.swift; sourceTree = ""; }; + FB8220FA4D9117310AEFC629A6EE4A08 /* MDFTextAccessibility */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MDFTextAccessibility; path = MDFTextAccessibility.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FBCF7000A4C9DC9C24B02BFE17451F85 /* ShapeNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift; sourceTree = ""; }; + FBD0995EC979D7375ABF14FB9A0A9EB0 /* ChatLayoutConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatLayoutConfiguration.swift; path = Chatto/Source/ChatController/ChatLayoutConfiguration.swift; sourceTree = ""; }; + FBE0655303C1520FF0EF50C57907E661 /* MDFTextAccessibility-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFTextAccessibility-umbrella.h"; sourceTree = ""; }; + FC5CBEF7738EFE577EEAE11F00FF329E /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = ""; }; + FC7593B690F872386EAC6DA2F85BD36D /* MDFRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFRTL.m; path = Sources/MDFRTL.m; sourceTree = ""; }; + FDEEADA23D6F4C3DFE04A4117338D768 /* MDCShapedShadowLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShapedShadowLayer.m; path = components/Shapes/src/MDCShapedShadowLayer.m; sourceTree = ""; }; + FE68F0EF6188796115A2408E20255E7C /* UIFont+MaterialTypographyPrivate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialTypographyPrivate.m"; path = "components/Typography/src/private/UIFont+MaterialTypographyPrivate.m"; sourceTree = ""; }; + FE6DB58D8654DC8B5F88E4D12FD74409 /* ShapeRenderLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeRenderLayer.swift; path = Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift; sourceTree = ""; }; + FEB0A58B0BC617DECB8D3427A1405634 /* ImageLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageLayerModel.swift; path = Sources/Private/Model/Layers/ImageLayerModel.swift; sourceTree = ""; }; + FEC9B0599DF18776D54CF802CB9FFFD9 /* MDCCurvedRectShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCurvedRectShapeGenerator.m; path = components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 052B815FEFBCEB5CBBA08741F810D971 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 192B25A167B62AA5C7B878CE91F3582E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1D5C89C35ED64587AA3F3CC9FB379C53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A6F57488F348E0036579E5B9DD98CAA2 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 28BA49E77F658083C4A924833FF0CF7D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E636D3BE68B055932C1DBF52E9E5F8A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 39D530C2A3085A1033683EC9AE0BC313 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 33F86FAB918B148A63A1575667F9B570 /* CFNetwork.framework in Frameworks */, + 9C0BE8FA0030B2BC1DF7C159FA059389 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 78FC9E52ECE20C8C02B38B218A9F8D6C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E4FBF289DDBE5FE3D144CF1347D4092 /* CoreGraphics.framework in Frameworks */, + FBBCCDCCAFD735DA076AEC214B31DEBE /* Foundation.framework in Frameworks */, + 58287E8A2D3CC19A61F6EC3D5138CED3 /* QuartzCore.framework in Frameworks */, + F45BE4AD9E22109487817B3E369E8774 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A46E636AA9FD7591DACA5A99D724E081 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A563BE445A159E73000F25A2BC3B940E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C033F3E94355E90147C197D165E7DEE0 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B05B2D2B6059C5982EE1D520AA115030 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B3901084F60BD621D593F30B15E9D5C0 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9B2E1995C19B72DC80DBCAAE398BC33 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 49F6DAAA65DA40549E2D693A7E1B9E85 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0CE2657B3BF086744C29E6B209CFDE15 /* Ink */ = { + isa = PBXGroup; + children = ( + A8AB1E1A488B0F97C034BFD4ECB9C11B /* MaterialInk.h */, + A67A50DB4E82C2861D15BCA11383D919 /* MDCInkGestureRecognizer.h */, + 9906975C7ABA6E0912B4362EFF02B32C /* MDCInkGestureRecognizer.m */, + 92BF3EBBBF215FDB97014A1C7F674282 /* MDCInkLayer.h */, + D3414E16D6767D8F8FBAB002ECBAA6CD /* MDCInkLayer.m */, + AF43E371B84D82B4BE12084C4D22A945 /* MDCInkLayerDelegate.h */, + AB54FAD6E9F36E88C0A18E94EB070CA3 /* MDCInkTouchController.h */, + 02F95682318FB0084CD0FAE9E698A337 /* MDCInkTouchController.m */, + 229265038E32F623E46E6B51D41CC33C /* MDCInkTouchControllerDelegate.h */, + 79EA03EF06AA77426C2C84981497D6F8 /* MDCInkView.h */, + BA395024B9E67C9EE8187839618E96FB /* MDCInkView.m */, + 42C1DD5860EC1699A757C9BAB12A66D7 /* MDCInkViewDelegate.h */, + B15025A80B5750A7F05A489683577910 /* MDCLegacyInkLayer.h */, + 600ED7A50816D1A3222193C97DEA9BC3 /* MDCLegacyInkLayer.m */, + 16900192B6F1F880A2BEEE4A15228EE3 /* MDCLegacyInkLayer+Private.h */, + 3DCA91058EFB02847D0DA47F4F3195AC /* MDCLegacyInkLayerDelegate.h */, + 6F5C3BCCB2FBBD3DC6701A99A1EC16E2 /* MDCLegacyInkLayerRippleDelegate.h */, + ); + name = Ink; + sourceTree = ""; + }; + 144970CAC0814448ACFB4EE458FC2F64 /* Alamofire */ = { + isa = PBXGroup; + children = ( + 6BDB7E8223919BD683DE47719CEA1A69 /* AFError.swift */, + 854600D850F2C3A98DF50071F6F04C09 /* Alamofire.swift */, + 701CF46760379C28084078F7D4BAFF32 /* AlamofireExtended.swift */, + CB094FC5A0B58CDEC708A4D9A8189F89 /* AuthenticationInterceptor.swift */, + 748CA1EDB7765D88F678941AA746AA9E /* CachedResponseHandler.swift */, + 93520D487909F1B889485508132F1A96 /* Combine.swift */, + 931593E088B694A7D6B0D1FC4FE6C080 /* DispatchQueue+Alamofire.swift */, + DE6A0C29D359F8F037F7DF2A21B78FC4 /* EventMonitor.swift */, + 21FFD3AD6BFF43E494BD4BD3E560F4A9 /* HTTPHeaders.swift */, + C27430E91F40A49F302EA2C974453696 /* HTTPMethod.swift */, + 7F557E15F0C636BE0C4547A358DA8C93 /* MultipartFormData.swift */, + 7872898EA812A796E27F3664AA901902 /* MultipartUpload.swift */, + D25173317D99ECFE9E0ECC69FCEC95FA /* NetworkReachabilityManager.swift */, + AE7DFA57EB0C03A4757CAB9256086346 /* Notifications.swift */, + 30734D2F4660366E1C28FA6E2E4345B1 /* OperationQueue+Alamofire.swift */, + FA343ABB695B730239978538A94F38D6 /* ParameterEncoder.swift */, + FC5CBEF7738EFE577EEAE11F00FF329E /* ParameterEncoding.swift */, + 73D70DCDE236059947C0BF795B512C84 /* Protected.swift */, + 2B1E20D6B957EF263B8B500DA4F13C39 /* RedirectHandler.swift */, + 7800E126D44EA6D8FC842E5214971EF6 /* Request.swift */, + 097A230A81C9305A1D7CAF969BD3B009 /* RequestInterceptor.swift */, + F6090940C42D19C30699D9DFCD46FEA1 /* RequestTaskMap.swift */, + 1962BFA68BF11D74BDD363384439BAC5 /* Response.swift */, + B411BF2903D79884DFBA1F089404388E /* ResponseSerialization.swift */, + 246B8D62D146A318E129AB0C450D33BA /* Result+Alamofire.swift */, + A3619A8910E5127A2A7EBD2417924A3D /* RetryPolicy.swift */, + 1A8B9BF018FD470AE5EBE4F9FF3808CD /* ServerTrustEvaluation.swift */, + 6684560C13B020634CFCE92295A15B72 /* Session.swift */, + C7B54C5101F5D61940E8C3AE0858E0A6 /* SessionDelegate.swift */, + 7BF3276002056A8603CF35E5D0FDBB3B /* StringEncoding+Alamofire.swift */, + 57C4846B514755F166442D8D10CEA565 /* URLConvertible+URLRequestConvertible.swift */, + 3D28FDB1911F3BD71EEBE077FA899488 /* URLEncodedFormEncoder.swift */, + B995834698359995E0262F3F70C896A9 /* URLRequest+Alamofire.swift */, + 6587D8532A9E9E13BA4B37E713566C3B /* URLSessionConfiguration+Alamofire.swift */, + 8C971CF5443E97989CD0DF44D9C18846 /* Validation.swift */, + A95CB1598E998B48CD1D973159ECC08E /* Support Files */, + ); + name = Alamofire; + path = Alamofire; + sourceTree = ""; + }; + 1AF48580728743ABEEC0DA59D34B78E2 /* Support Files */ = { + isa = PBXGroup; + children = ( + AD54C4E77B772162BFA739B3159C945F /* MDFTextAccessibility.modulemap */, + 4E2AA82D677A35A05B022A324E49F50C /* MDFTextAccessibility-dummy.m */, + B50C69F9BC8FB745BE744D03394900F8 /* MDFTextAccessibility-Info.plist */, + 5D06BA269EAAEF5A59A416177B7571E8 /* MDFTextAccessibility-prefix.pch */, + FBE0655303C1520FF0EF50C57907E661 /* MDFTextAccessibility-umbrella.h */, + CEE878F352FF70522E2B4041CB792B24 /* MDFTextAccessibility.debug.xcconfig */, + 0EA7FAE72F27B4A66C2CAEEC25C31634 /* MDFTextAccessibility.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/MDFTextAccessibility"; + sourceTree = ""; + }; + 21AC7CA70041658C6EBD3A1F4775F58C /* lottie-ios */ = { + isa = PBXGroup; + children = ( + F4F84C77FED35072FBA03ADBD28FF9F3 /* AnimatedButton.swift */, + F6EB232EC11EB49D9DA52EDFB9660747 /* AnimatedControl.swift */, + BD14A3955F01AD7D390CC5763E43CEFE /* AnimatedSwitch.swift */, + 2D6557B599662265675CB317D7A3AA35 /* Animation.swift */, + F6E10A41E382E72F54FC92EF85F00963 /* AnimationCacheProvider.swift */, + 970D404B267F9ADF520FA00EC3700F65 /* AnimationContainer.swift */, + 708C0285547318992A3989A6415E5D6B /* AnimationContext.swift */, + AD480CF250BF63F248837A06A6F55B90 /* AnimationFontProvider.swift */, + F711AC4E23408CDD020388C5E5D71009 /* AnimationImageProvider.swift */, + 4259483CFD0AC1C7F1295B7B354781A1 /* AnimationKeypath.swift */, + B3F0F84872DE9FA147E94B5562B8FE68 /* AnimationKeypathExtension.swift */, + B613FA13F2264BCF72F6DE1BD3588C52 /* AnimationPublic.swift */, + 5BAC390311C727CE7C129F84BD9B09B9 /* AnimationSubview.swift */, + F75BEC31BE7157E03DF220C390111D13 /* AnimationTextProvider.swift */, + 0A5DF95E38B16446E73BCFAC6F53DA2E /* AnimationTime.swift */, + F2F4B5DB7011E5176A063CF3110CD09E /* AnimationView.swift */, + E6966E76E2E05FEC74DB6972B4F38FCC /* AnimationViewInitializers.swift */, + 78E1DDE875714E724C5D0109D3735095 /* AnimatorNode.swift */, + 7C6DBEEB82DA69272704408A3AEBA6EC /* AnimatorNodeDebugging.swift */, + 678552B49FF59718A815C8CB72951E75 /* AnyNodeProperty.swift */, + CE99EB6CC9C88A5D592843A9171EC23B /* AnyValueContainer.swift */, + E5A162FEC0123631BFA766AC1CD2E593 /* AnyValueProvider.swift */, + EB7B1A690B1D232B90A70E4E318BB297 /* Asset.swift */, + 31D7840BB6B99AC092EAB4942FDD3F53 /* AssetLibrary.swift */, + 3AEB21337FEDA85FD4CE4B5534D436F9 /* BezierPath.swift */, + AEFD189E2BBDDBFEFE3D285ECEA7330D /* Bundle.swift */, + DAEF5AB5FA15E20986DA16EBD0840CA2 /* BundleImageProvider.swift */, + 62FE3D47542C17E69E982939D7C4342C /* CGFloatExtensions.swift */, + 6F2AF02B3727FEC99B62286905D9F172 /* Color.swift */, + 7E145BBB7A0266AF3CEF37E16F141F03 /* ColorExtension.swift */, + 1C0B52C8F35A67626A42F0430F36E070 /* ColorValueProvider.swift */, + 8A95BFC36D1019120C1DEC0F6758A0ED /* CompatibleAnimationKeypath.swift */, + 3D6F69E4E368E5F576A3D6AF24022D2D /* CompatibleAnimationView.swift */, + 65EAC9DA9D8A3173E14F6714F6DD597E /* CompositionLayer.swift */, + C31592E117E0046F2678B3066EB06AC3 /* CompositionLayersInitializer.swift */, + B2EE0D77753F90766C6F5C64AAADA005 /* CompoundBezierPath.swift */, + 74405B4230DF6726303FA767434E6A14 /* CurveVertex.swift */, + C290807FDFEFCB92E5FABE8FE851443C /* DashPattern.swift */, + 7FA25E16755D429C6CC771AD6044CEED /* Ellipse.swift */, + C67AF078C9EE64DED1961778EFF012A0 /* EllipseNode.swift */, + 64882FB458891751B0C4E20C1C23D5F1 /* FilepathImageProvider.swift */, + A2502619916EAC0AB2E21B1A54DB347D /* FillI.swift */, + E3B32DE56F884A29871DDC06DEFDD299 /* FillNode.swift */, + A07731940B92711A5CA2D9CF2C7DA38F /* FillRenderer.swift */, + 686DE3CEFB348F67B7F2E0CCDBED00A7 /* FloatValueProvider.swift */, + A45EFEC8F1A0F1093631AF60C90BB060 /* Font.swift */, + B4C869DD8FBC246E0FE2199DCEB50D3D /* Glyph.swift */, + 9B6A73993CD9CF59863BB4C36386EEAE /* GradientFill.swift */, + B19302162D052125FE5F7992C054A240 /* GradientFillNode.swift */, + 619B96C839D9C1480A0A40E988C63AFD /* GradientFillRenderer.swift */, + E6ACC937F5C195801B15F2AB3B92101F /* GradientStroke.swift */, + C1FAB2482AA7F33F1F62927A25599EF4 /* GradientStrokeNode.swift */, + CE0A65CE9CFA027DECC15DDE7C84261A /* GradientStrokeRenderer.swift */, + 553199DA713332B2A7CE2BC1B053344C /* GradientValueProvider.swift */, + 6E035C3FBB06190E0CE480284B8CF014 /* Group.swift */, + 5739897ED45AAC422F0EECBA1D75F58A /* GroupInterpolator.swift */, + 63431295907542E5A9DD8630DC9338BA /* GroupNode.swift */, + 058498847739D73FC959AC6FAC7A0D9F /* GroupOutputNode.swift */, + 3D884B74C43A162D8B9756985E116E84 /* ImageAsset.swift */, + 35A0FCAED698C32DB774702E78F6A9BB /* ImageCompositionLayer.swift */, + FEB0A58B0BC617DECB8D3427A1405634 /* ImageLayerModel.swift */, + 91855A9940FB42F19FD2875C45E2CB01 /* Interpolatable.swift */, + 805B67A1EC691E2F297D0CB0A125C31B /* InterpolatableExtensions.swift */, + 1E1B1AAE95B01FE76CC5E0D131135D20 /* InvertedMatteLayer.swift */, + 61814AFE969DF03C54E6B9E503F28302 /* ItemsExtension.swift */, + 111FAD1AC980B2D67B27016C087B61ED /* KeyedDecodingContainerExtensions.swift */, + 3984A2B5195B698FF523B7D82CD694F0 /* Keyframe.swift */, + 8ABFF571A3232481B34A73FDDE0F2B55 /* KeyframeExtensions.swift */, + C8F19458534DF74ABBBC1D9171D2FA1A /* KeyframeGroup.swift */, + 174A76F8A707305878153E63E7AF69F3 /* KeyframeInterpolator.swift */, + 0FC5A268C4EB4A715BD6C5613A37B5A9 /* KeypathSearchable.swift */, + C30F85DF0D50528EF9BEE8A2BA482034 /* LayerDebugging.swift */, + E0D7B9CC58E107433D11681261211BE1 /* LayerFontProvider.swift */, + 35BD0FEEB2EF9ADE87455CB6949E56DC /* LayerImageProvider.swift */, + 525767FD21D481C033ED537000C2A5B1 /* LayerModel.swift */, + A0D4358B34E866143ECD08151FA7D559 /* LayerTextProvider.swift */, + 9B246A845EEBC2196E07F54E35ACA25A /* LayerTransformNode.swift */, + B17C76564686F46F80432DCE2D053940 /* LottieView.swift */, + E94E88E183E7AC90A64BD46E39E2C85E /* LRUAnimationCache.swift */, + E22A0783A99782B1368A7C295F05B893 /* Marker.swift */, + 77EF08C7D4C2345726A07A8B9A265452 /* Mask.swift */, + 5241F42DE93198A9743B520528DEBE0E /* MaskContainerLayer.swift */, + 919EA980AF20A4FC75CE16B54373D398 /* MathKit.swift */, + 713A971DE11ED11949FB41259A01995C /* Merge.swift */, + 55C5A1F93EFADC2096EE6DC9303B38A8 /* NodeProperty.swift */, + 5AF2CED0DB3829D4D4BE54A78C1072DB /* NodePropertyMap.swift */, + F1E0BB0AD007791BD42D66DF1F4DB387 /* NullCompositionLayer.swift */, + B25B52A26AE822B9B2F58EA6B9E7E240 /* PassThroughOutputNode.swift */, + 096CBFAE6AD4730265460085F9186006 /* PathElement.swift */, + 7BC6506BF9A73061A58868C0D592B9B1 /* PathNode.swift */, + 940160C49FE72F93E569C21314AB30EE /* PathOutputNode.swift */, + 76D52B7A10D74C906F9C84CC976B1B2D /* PointValueProvider.swift */, + C80B62ED9C592FA542C65C50680BD2D3 /* PolygonNode.swift */, + 81EFFCF10BFC1B08CD7DBED57D297675 /* PrecompAsset.swift */, + 82AB7D6FD755ADF708F94AF789430203 /* PreCompLayerModel.swift */, + 2EE66424995BD90C997E2CEA9946F2AA /* PreCompositionLayer.swift */, + E9E21A4FEF3A460EFF2C8B38BBA65C25 /* Rectangle.swift */, + E20AA7DE19CA8DB5FD1FE6636C4C6E89 /* RectNode.swift */, + 9E173EE03A996655ED2311253DA58EB9 /* RenderNode.swift */, + 0F59122698AC6FE35BC3BC2998203C14 /* Repeater.swift */, + E3D75AB0F4CDA59E7BB16BD81745B956 /* Shape.swift */, + E10CAC47530EA0936ECF4F5B18F41899 /* ShapeCompositionLayer.swift */, + 7B96F646D146FE43BCB3C820D9573D99 /* ShapeContainerLayer.swift */, + B86684B080F0E3D4C1D38F093601783B /* ShapeItem.swift */, + 8AF96553AC1A4981ED8F74D3571CB86C /* ShapeLayerModel.swift */, + FBCF7000A4C9DC9C24B02BFE17451F85 /* ShapeNode.swift */, + FE6DB58D8654DC8B5F88E4D12FD74409 /* ShapeRenderLayer.swift */, + 8DBE44442938F2CBE3624C972232C24E /* ShapeTransform.swift */, + CEF8701D965CC5D013B9CC49B8B228E8 /* SingleValueProvider.swift */, + C1C78482FC811B81F0499146DB6F8592 /* SizeValueProvider.swift */, + B9C5DEABB0B24A9A6A4FB076F829A033 /* SolidCompositionLayer.swift */, + 187B43272E4EAEF4B35EA65BBA8B1334 /* SolidLayerModel.swift */, + 8EE689E19EA5C8EF636167214A787615 /* Star.swift */, + DA0CD727B37100536B0582E4ACA83844 /* StarNode.swift */, + 6BB9978D69E4ABC8F7AE8425D9E1F048 /* StringExtensions.swift */, + DBCD73AF1D7111668851A56E369B30AF /* Stroke.swift */, + 84C11F8849A9F9FA96193BDF6D47B305 /* StrokeNode.swift */, + DB0EBEFA02E716358C707E8E92A56CC2 /* StrokeRenderer.swift */, + 75A55C9E6101E233016AB66DD634B926 /* TextAnimator.swift */, + 9CF0A7BE4E58D6697CCF7D0FA0CBBDFB /* TextAnimatorNode.swift */, + 18C8E244ED61A9B57F738C5DB46FCC12 /* TextCompositionLayer.swift */, + 5DDC53D7CBF3A251E463BB224DD12DCE /* TextDocument.swift */, + 22BBD822E6408AD8D9D1D46DBCBA9381 /* TextLayer.swift */, + 7639C506BC1DBC0CA831E0E2A106D658 /* TextLayerModel.swift */, + A1BFB72AD30F0DE0585E804791B833D6 /* Transform.swift */, + F1CD9DD5A9448FED236DB4E8AAF6C8A4 /* Trim.swift */, + E0CB865665244183E4B9659E94101F9B /* TrimPathNode.swift */, + 3D5C4656A9953B89898D46D2942F6E89 /* UIColorExtension.swift */, + D80EB9AAB47357453596394BFD2FBD6D /* ValueContainer.swift */, + A422591030AC5B802703CAAFA3536ED3 /* Vectors.swift */, + 2AD410EF9494BB5D4662CBBE59C2C06C /* VectorsExtensions.swift */, + CA0DA1B170B1BF48603D3494C22C1A5A /* Support Files */, + ); + name = "lottie-ios"; + path = "lottie-ios"; + sourceTree = ""; + }; + 2A51557057FA173A61DBFFD784D3E10D /* MaterialComponents */ = { + isa = PBXGroup; + children = ( + 6668C74F59A9263767D39E92D8EC1380 /* AnimationTiming */, + B804F1C35AC6246E197C691500949F79 /* Availability */, + FDD60C62F8B0E342211EE4076CD31BEA /* Buttons */, + 7238C8EF7151CBF33AFBFA36C9499A2A /* Cards */, + 4CF444DEE5745EE8B1A83511D00B308C /* Elevation */, + 0CE2657B3BF086744C29E6B209CFDE15 /* Ink */, + CCFA8110E1E64DA6077C0338044C3E4B /* private */, + 69BA61372DCE0F8E82D5724DF36FFA19 /* Ripple */, + A144D5F9E55266835341542282BF0476 /* Shadow */, + D462E74D0157D8AFD873CF8DF7F4AF97 /* ShadowElevations */, + C50C54DDDB0DD81635159B9C32709868 /* ShadowLayer */, + A4690F5B79FFBD6E9DA16E0A3DB0C5AC /* ShapeLibrary */, + 6EFCCFBAFC9CA7F391382F9E21C659A9 /* Shapes */, + 6566FDA23C9DB31DF0C67300D4266218 /* Support Files */, + CAE3BBA14C7998BA5F99F91C08201D62 /* Typography */, + ); + name = MaterialComponents; + path = MaterialComponents; + sourceTree = ""; + }; + 420A7BACED161C14C7AD7697744FD6E6 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + B85D66BE51608559AC086C64C5C8BF7C /* Pods-ios */, + A890DC0BE85742E0FA551932F177000B /* Pods-iosTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 42C82A0937E7B91672FB4E7A426B9951 /* Base */ = { + isa = PBXGroup; + children = ( + 8D0323B0058AFC25122470F8D9558B7D /* MaterialIcons.h */, + C364DE9E9FEDDD730DBF09F77CB0B143 /* MDCIcons.h */, + 3A7AEC8A27ED687F3745E5FF8E0FEB75 /* MDCIcons.m */, + 3FCF28462DDA6182768A18DEE727FA6C /* MDCIcons+BundleLoader.h */, + ); + name = Base; + sourceTree = ""; + }; + 45CC7D2BF1DD5127F441F274153CD862 /* Application */ = { + isa = PBXGroup; + children = ( + 7CE46363B5ADC47CE9B57A3335A26016 /* MaterialApplication.h */, + B27333CDD645D2D5CF612BB67776A72B /* UIApplication+MDCAppExtensions.h */, + AC2834BC1BEF7C025DE72333BE447006 /* UIApplication+MDCAppExtensions.m */, + ); + name = Application; + sourceTree = ""; + }; + 4CF444DEE5745EE8B1A83511D00B308C /* Elevation */ = { + isa = PBXGroup; + children = ( + 8DD0AD806399CC6AE3AA2B4F75C77C23 /* MaterialElevation.h */, + 26833FC1284ED4F21560352EB2FA83B1 /* MDCElevatable.h */, + BF7CDBB13E9F0AC3175BDF383C468787 /* MDCElevationOverriding.h */, + D2F86DF24DDC31E73E6DC1AD1E8E22E6 /* UIColor+MaterialElevation.h */, + 8C64B5F902ABF8446821C23B3779E31D /* UIColor+MaterialElevation.m */, + DAA556AD9FDEF109652EAA16FF14D299 /* UIView+MaterialElevationResponding.h */, + 5EA8C38C23FD8FDD5FE62F2CFD7B55B9 /* UIView+MaterialElevationResponding.m */, + ); + name = Elevation; + sourceTree = ""; + }; + 54004DD572C0D1AC3C340551497E2194 /* Support Files */ = { + isa = PBXGroup; + children = ( + BBCBAB7ADDB2D552DB46A6DEE6C9F5A7 /* Chatto.modulemap */, + B93D411155688717E99B55AE5D57CF10 /* Chatto-dummy.m */, + C59B9A40073373B78D37C4937FD3E223 /* Chatto-Info.plist */, + B1F29114317DB38E4711B41B1C6376C9 /* Chatto-prefix.pch */, + DE1B915229A73A86A29689D51EBCEE8E /* Chatto-umbrella.h */, + 8186408B3030AB74BE1331A431FB83D4 /* Chatto.debug.xcconfig */, + C5494CF8D45C3C7880D1C7684088125B /* Chatto.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/Chatto"; + sourceTree = ""; + }; + 5C1CCF8EE2CEEFA1A058DC0BBC9FDF58 /* MDFInternationalization */ = { + isa = PBXGroup; + children = ( + B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */, + 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */, + FC7593B690F872386EAC6DA2F85BD36D /* MDFRTL.m */, + AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */, + 1A85C401C2117402508517940B47B11D /* NSLocale+MaterialRTL.m */, + 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */, + BC918096888E65550CADD29286F70E6B /* NSString+MaterialBidi.m */, + 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */, + 4DE016D373F57DCED6E20186B20A6596 /* UIImage+MaterialRTL.m */, + 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */, + A2783799D5308869A35311BBDF845364 /* UIView+MaterialRTL.m */, + 63B93E12B391EF766B0A1A6EBC6727C3 /* Support Files */, + ); + name = MDFInternationalization; + path = MDFInternationalization; + sourceTree = ""; + }; + 63B72EF56693213EBD10E1799FD5D999 /* Icons */ = { + isa = PBXGroup; + children = ( + 42C82A0937E7B91672FB4E7A426B9951 /* Base */, + F9CF631A6EA3425C2C202FD24CC328CC /* ic_check_circle */, + ); + name = Icons; + sourceTree = ""; + }; + 63B93E12B391EF766B0A1A6EBC6727C3 /* Support Files */ = { + isa = PBXGroup; + children = ( + E1470EA198559C0288DDF4068CAFB6ED /* MDFInternationalization.modulemap */, + 8F1F807B149E883C56B85C5C055D651D /* MDFInternationalization-dummy.m */, + 1D76179FC8700321E982CBF63CBE974C /* MDFInternationalization-Info.plist */, + 5278EC208A2BA0E35B57E4AE2C020987 /* MDFInternationalization-prefix.pch */, + B120981A19A9C956A2941EB064D1FD1D /* MDFInternationalization-umbrella.h */, + BE5AE749191583AB288E955A216FBDEE /* MDFInternationalization.debug.xcconfig */, + 43E52D204C785271C19FE614C61534A4 /* MDFInternationalization.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/MDFInternationalization"; + sourceTree = ""; + }; + 6566FDA23C9DB31DF0C67300D4266218 /* Support Files */ = { + isa = PBXGroup; + children = ( + 3198F273FE8A5D0B39F6AA64EB844FAD /* MaterialComponents.modulemap */, + 8F3ED95F3D3F150E9EF3F7B365B3BE9C /* MaterialComponents-dummy.m */, + 5FD3683EBDED4FBCF7CF58DBB4864E15 /* MaterialComponents-Info.plist */, + 3E7BEEB423FD0BB8206D7833EAFB5A94 /* MaterialComponents-prefix.pch */, + 6741DCC6A7B256467254EDFBBDF05B3F /* MaterialComponents-umbrella.h */, + 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */, + 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */, + BF9EDA0CCF4ACD15ECC442F77B0DD927 /* ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/MaterialComponents"; + sourceTree = ""; + }; + 6668C74F59A9263767D39E92D8EC1380 /* AnimationTiming */ = { + isa = PBXGroup; + children = ( + 37F289E6F2E521F6C7C3C1203689674C /* CAMediaTimingFunction+MDCAnimationTiming.h */, + A4D99A5054C55BC32C98A5EF91CB5BDD /* CAMediaTimingFunction+MDCAnimationTiming.m */, + B443745DA64B8F614899680B793F7108 /* MaterialAnimationTiming.h */, + E69B2A401E928B1CBB9CD4B02BEE853D /* UIView+MDCTimingFunction.h */, + 8D1A663AAA238F5A6580850607B58FB9 /* UIView+MDCTimingFunction.m */, + ); + name = AnimationTiming; + sourceTree = ""; + }; + 69BA61372DCE0F8E82D5724DF36FFA19 /* Ripple */ = { + isa = PBXGroup; + children = ( + 33FE8470F10646E30DF3366F83774F58 /* MaterialRipple.h */, + E81ECAF8BBE1DB54276ED819382CA9FA /* MDCRippleLayer.h */, + 2931B1BB799911DD00AA57C202BA2A6F /* MDCRippleLayer.m */, + 6F6DE9329322864658D938C211EAFA09 /* MDCRippleLayerDelegate.h */, + 62A2F94AFB07146E8A9A5DC4985F19BA /* MDCRippleTouchController.h */, + 5DC202126AC7DEA521D46EACE0A8CB33 /* MDCRippleTouchController.m */, + D6E67B621B39259F3CED1DE2F43E57AE /* MDCRippleTouchControllerDelegate.h */, + 31F194A86821D5801BEACFA2DADF397D /* MDCRippleView.h */, + 65D2F1380362B7EE62D2678F79650A3A /* MDCRippleView.m */, + A71810DBABD1DE3268F0A777230A85A2 /* MDCRippleViewDelegate.h */, + 3F6AA23B87CFACBF3F6DA8E1FFE48A3A /* MDCStatefulRippleView.h */, + 1BD15A4D6488FC7DCA7F0F18E0CAC1B9 /* MDCStatefulRippleView.m */, + ); + name = Ripple; + sourceTree = ""; + }; + 6EFCCFBAFC9CA7F391382F9E21C659A9 /* Shapes */ = { + isa = PBXGroup; + children = ( + 6A7B2BF1390CAC2A1A79D280BA2AA739 /* MaterialShapes.h */, + 7E28ED602A75DB92340D7444DB1995A1 /* MDCCornerTreatment.h */, + 2B2BD90586E1B7C4656D777C09F8F99D /* MDCCornerTreatment.m */, + 1E786DB3BAA72871D457F5DC195A08AD /* MDCEdgeTreatment.h */, + 19E09DB898B5D3E34265E0EE3D2FB559 /* MDCEdgeTreatment.m */, + 9D1132D92154CA46250E791A60FC2C43 /* MDCPathGenerator.h */, + 8246440519C85238FCFCF8F58392A86F /* MDCPathGenerator.m */, + 909F969A28FE09D5039EF1A274202B6D /* MDCRectangleShapeGenerator.h */, + 5FB83587411063DC53155C1A2C082DD3 /* MDCRectangleShapeGenerator.m */, + 4E0B142ACFD6D223F820B0A902B17ADB /* MDCShapedShadowLayer.h */, + FDEEADA23D6F4C3DFE04A4117338D768 /* MDCShapedShadowLayer.m */, + 4157CBEADF18154FD243E39C50468258 /* MDCShapedView.h */, + BBF566D92B960CE6B69285672FDE16A8 /* MDCShapedView.m */, + 46AE7E00528585497EA59CD94F669E3A /* MDCShapeGenerating.h */, + A287971E3E70E72ECCDFC5485A8B6D35 /* MDCShapeMediator.h */, + F6BB438AED2F3B6F61B65BEBABF41B1F /* MDCShapeMediator.m */, + ); + name = Shapes; + sourceTree = ""; + }; + 7238C8EF7151CBF33AFBFA36C9499A2A /* Cards */ = { + isa = PBXGroup; + children = ( + A6DCF5983D0F4C2FB0E19A0D320B789B /* MaterialCards.h */, + 11A09793C107A7840B6234B0400A787E /* MDCCard.h */, + 0D46DD556D1F791A1853E2A6E2506FF6 /* MDCCard.m */, + B518C0072DFC3DBA1FA3C49569B1F3DA /* MDCCardCollectionCell.h */, + 6A035D2874E49F84C6A8FF8FBF6503C8 /* MDCCardCollectionCell.m */, + 4FF269172DE0E3442E369BAF00EAB3CD /* UICollectionViewController+MDCCardReordering.h */, + 9A370E644FBCA6F70723C4C1D6720158 /* UICollectionViewController+MDCCardReordering.m */, + ); + name = Cards; + sourceTree = ""; + }; + 7D6083E3CF506A7CD7A7FDACF3C3F86F /* Math */ = { + isa = PBXGroup; + children = ( + 98EB1455F30B28A8F4F59C33E8CB38D2 /* MaterialMath.h */, + D3C80D0A870376D3DAB74C8D106BFF8C /* MaterialMathDummy.m */, + 4AE9A7C0A475863CB257C9866B1EB05E /* MDCMath.h */, + ); + name = Math; + sourceTree = ""; + }; + 8DBB1FD9C1111191D999111C75E2985E /* Products */ = { + isa = PBXGroup; + children = ( + 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */, + BE3893C08724F3D82CF1D199CF98F274 /* Chatto */, + 51BA97E8B5085EFFB47BC9C0B785CEA7 /* lottie-ios */, + A34157B3708EDB7D17422B3D92F591D2 /* MaterialComponents */, + 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */, + 9A2AC0B8BAC6EF061EBC28825653DF99 /* MDFInternationalization */, + FB8220FA4D9117310AEFC629A6EE4A08 /* MDFTextAccessibility */, + DB84425C5097649DB7039BCDFCA4DB6C /* Pods-ios */, + 924ED3C5CACD9B01ED8F6ACDCE0843EE /* Pods-iosTests */, + ); + name = Products; + sourceTree = ""; + }; + 97EE99652D0948DBD9E105410379C433 /* Chatto */ = { + isa = PBXGroup; + children = ( + 6102438DC85CE0EBEE3E8E37512F0869 /* BaseChatItemPresenter.swift */, + 30C3EF2AB0724BFD75D6FE28F5429702 /* BaseChatViewController.swift */, + D7F58CC76B154EE1FFCEBFD29458765F /* BaseChatViewController+CellPanGestureHandler.swift */, + EA378A26B99BBD94560AE1C8C02B11F6 /* BaseChatViewController+Changes.swift */, + 0594042CC23BC8F1D7592815BDBFC2C6 /* BaseChatViewController+Presenters.swift */, + 0314F72C05B7FF63F27BE4646BD8305E /* BaseChatViewController+Scrolling.swift */, + 6C4F064EEF86F1829D1B928D11963739 /* BaseChatViewControllerView.swift */, + 2C197D9AE91D805F5FFDD745A3332768 /* CellPanGestureHandler.swift */, + CA6F96BF2DFC0E11E6A55539B9225C94 /* ChatCollectionViewLayout.swift */, + BAA843D119FBBE1873DB868B2C42F47A /* ChatDataSourceProtocol.swift */, + 777B7794311A16E26C00B24639C365CB /* ChatItemCompanion.swift */, + 2FB631B982623359A1D4E17E34F6369B /* ChatItemCompanionCollection.swift */, + 06460E2CD793BBF2C535219D13A50C05 /* ChatItemPresenterFactory.swift */, + A488EF6B3932789A199EFF0E48669A8F /* ChatItemProtocolDefinitions.swift */, + FBD0995EC979D7375ABF14FB9A0A9EB0 /* ChatLayoutConfiguration.swift */, + BFA5A232C8B4D4671F587B33DCEF5FB4 /* CollectionChanges.swift */, + 6A9EA755014DD5DD4C27D7ECD93783FD /* DummyChatItemPresenter.swift */, + BA603856ED143A5A0536C7F9F8785FFF /* InputPositionControlling.swift */, + 97374F6103D866145B36386C14D1D8E5 /* KeyboardTracker.swift */, + 0F40483DF444914DE771891BCED1F593 /* ReplyFeedbackGenerator.swift */, + 536C19C18245423A6AFB8166C8F93F35 /* SerialTaskQueue.swift */, + 1E9DE4BB5450E42050B34C1355AB7E72 /* Utils.swift */, + 54004DD572C0D1AC3C340551497E2194 /* Support Files */, + ); + name = Chatto; + path = Chatto; + sourceTree = ""; + }; + A144D5F9E55266835341542282BF0476 /* Shadow */ = { + isa = PBXGroup; + children = ( + ABED2A4D6F3787803F5376C66CB9544C /* MaterialShadow.h */, + 9625E67A414049AFFBCF73EDB3F00B1B /* MDCShadow.h */, + 27B3D295F42CEAEA35B48C43AD477C61 /* MDCShadow.m */, + C73D5045364BD3202565B4FDB54AE37A /* MDCShadowsCollection.h */, + 5D714F498A6A5E7FC8FB92A3819A449A /* MDCShadowsCollection.m */, + ); + name = Shadow; + sourceTree = ""; + }; + A4690F5B79FFBD6E9DA16E0A3DB0C5AC /* ShapeLibrary */ = { + isa = PBXGroup; + children = ( + 90A761C8F692CCA4FDA5BFC5DEBACE74 /* MaterialShapeLibrary.h */, + 40FC4A25E6CBE6CD94D65C7642079986 /* MDCCornerTreatment+CornerTypeInitalizer.h */, + 24866442BD723570188362AFBC61ECA3 /* MDCCornerTreatment+CornerTypeInitalizer.m */, + 1A09CD4BB399191B8387A7D72E4B2909 /* MDCCurvedCornerTreatment.h */, + CBBE66515626EA5EE7282EF95C8275B3 /* MDCCurvedCornerTreatment.m */, + DB1E998F48C65751461A4CB1E13E45C5 /* MDCCurvedRectShapeGenerator.h */, + FEC9B0599DF18776D54CF802CB9FFFD9 /* MDCCurvedRectShapeGenerator.m */, + 43693CCEC265B75D660F71A9422AC97E /* MDCCutCornerTreatment.h */, + EA9BAA45A6BA523F01C4735963DDDADC /* MDCCutCornerTreatment.m */, + 2882BFF487CA5879D2F68C6B944595F5 /* MDCPillShapeGenerator.h */, + 2102D1FE11630B5091FE865DDD43F05A /* MDCPillShapeGenerator.m */, + 95DF7386D55C9F9642DC5077C341E35E /* MDCRoundedCornerTreatment.h */, + 3A16E9C361081323779110A2E0EE4F83 /* MDCRoundedCornerTreatment.m */, + 34EBCCA2E4424A06FB1C11FFF86AE0E2 /* MDCSlantedRectShapeGenerator.h */, + 3EEB4FCAEBF9555B90A5EC48FE3D6B4F /* MDCSlantedRectShapeGenerator.m */, + AC80A65D7F1D3E258FEC205047C3EB0A /* MDCTriangleEdgeTreatment.h */, + 3B5993FADA59638CD437C1C18E3DDA1D /* MDCTriangleEdgeTreatment.m */, + ); + name = ShapeLibrary; + sourceTree = ""; + }; + A890DC0BE85742E0FA551932F177000B /* Pods-iosTests */ = { + isa = PBXGroup; + children = ( + A9B79521A3553AD1005FA6A9670CEBF2 /* Pods-iosTests.modulemap */, + B9158E5A2523A49B53CFB65D466371AE /* Pods-iosTests-acknowledgements.markdown */, + 341DED9C8A182716EE1504F58E3BBA27 /* Pods-iosTests-acknowledgements.plist */, + F5922665F1DC85A6D8A92E2E648D19BE /* Pods-iosTests-dummy.m */, + E741DFF8BC3B7CB733B6417AE217AEC1 /* Pods-iosTests-frameworks.sh */, + 5923674729AC49BE14806A1AEB0FB8DE /* Pods-iosTests-Info.plist */, + 1CEC781B13B51E108C5658001DCCCD2A /* Pods-iosTests-umbrella.h */, + 72E3FDB351A03387E5608B6C84700D31 /* Pods-iosTests.debug.xcconfig */, + F9C9C35B39EFD7AD82253D0A2AD91B56 /* Pods-iosTests.release.xcconfig */, + ); + name = "Pods-iosTests"; + path = "Target Support Files/Pods-iosTests"; + sourceTree = ""; + }; + A95CB1598E998B48CD1D973159ECC08E /* Support Files */ = { + isa = PBXGroup; + children = ( + 32E84BC23BD4ED514C1BB73378A0EF16 /* Alamofire.modulemap */, + 7DF304359AEBCFADD6BBD420523B223F /* Alamofire-dummy.m */, + EA26F20034BDBF94780CF01106D4CD64 /* Alamofire-Info.plist */, + 9A247D7873C2106F7E0006C6D3FA0DC9 /* Alamofire-prefix.pch */, + 1742EDF4DDCBB582B88771AEB1201E1A /* Alamofire-umbrella.h */, + C43092A50861A2F799989439A7DCBCAF /* Alamofire.debug.xcconfig */, + 3CDCB1C8D529F8DE664D2E4C96BD3A7D /* Alamofire.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/Alamofire"; + sourceTree = ""; + }; + AABDEFB693E15B50C7698B1026DED0F3 /* Resources */ = { + isa = PBXGroup; + children = ( + 427AFF770A87FFC8BA489ECCD299D58F /* MaterialIcons_ic_check_circle.xcassets */, + ); + name = Resources; + sourceTree = ""; + }; + B804F1C35AC6246E197C691500949F79 /* Availability */ = { + isa = PBXGroup; + children = ( + E58F9B821896E6DE3629364AB9B21F35 /* MaterialAvailability.h */, + 3647DF7F6475E74ED3EB0CD6926B63DB /* MDCAvailability.h */, + ); + name = Availability; + sourceTree = ""; + }; + B85D66BE51608559AC086C64C5C8BF7C /* Pods-ios */ = { + isa = PBXGroup; + children = ( + A5ED19C47A3B111AEEAE6C09A8E508BB /* Pods-ios.modulemap */, + 9ECD5C7C84C99618AF2620886CE9964E /* Pods-ios-acknowledgements.markdown */, + BF467C869E0EBC17B9CB2B58A166300C /* Pods-ios-acknowledgements.plist */, + 100C94E08EE07B09E19F115713E5B184 /* Pods-ios-dummy.m */, + 25A7B53BBB25EA665175DB0281DBDE11 /* Pods-ios-frameworks.sh */, + 952959F2E1983393A6EE1CAF37FB84E1 /* Pods-ios-Info.plist */, + D720E1DADE8B1BB169230ADFA11012E3 /* Pods-ios-umbrella.h */, + 0C07D8193799950468B6CF3885E1193B /* Pods-ios.debug.xcconfig */, + B1F119B189824C232697C892AF030E98 /* Pods-ios.release.xcconfig */, + ); + name = "Pods-ios"; + path = "Target Support Files/Pods-ios"; + sourceTree = ""; + }; + B94D7768568A9992200DB461E8CF687F /* Frameworks */ = { + isa = PBXGroup; + children = ( + D21A228721E7A09BEF782E68D4232144 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + BB7FF24AAB31B14A5EC03B11DF4CCECB /* MDFTextAccessibility */ = { + isa = PBXGroup; + children = ( + F5CCE660C00123EB989E5679091A134A /* MDFColorCalculations.h */, + 3BEEEC76296F936D8FA70CA0604BB74F /* MDFColorCalculations.m */, + 7053BE770B19CADCC67259D2058FBFE5 /* MDFImageCalculations.h */, + 408FEF5380BBEF2A9A4EF9878452691D /* MDFImageCalculations.m */, + 8C2F6B0AED78566F963846D8D10A8CC9 /* MDFTextAccessibility.h */, + 14889A17A94EB87B76300716DD2A9F36 /* MDFTextAccessibility.m */, + 97A0DF460C679110DBBD0ED8B315B64D /* MDFTextAccessibility-Bridging-Header.h */, + 4ADBAC831F15371103E6075A7099776A /* NSArray+MDFUtils.h */, + A2C83FB5770E9E4004C80C5217E3CD69 /* NSArray+MDFUtils.m */, + 1AF48580728743ABEEC0DA59D34B78E2 /* Support Files */, + ); + name = MDFTextAccessibility; + path = MDFTextAccessibility; + sourceTree = ""; + }; + C50C54DDDB0DD81635159B9C32709868 /* ShadowLayer */ = { + isa = PBXGroup; + children = ( + B3F1C077ED9B83ED31FAF400299DC965 /* MaterialShadowLayer.h */, + 6ED311731D2D73DBC56836B34F3D634A /* MDCShadowLayer.h */, + 561E46EAC325F966EA9DBA55F0689607 /* MDCShadowLayer.m */, + ); + name = ShadowLayer; + sourceTree = ""; + }; + C7D23E365D4BA5B320803F40CDCBC339 /* Color */ = { + isa = PBXGroup; + children = ( + A575DBC967BD1B9824384DE5EAC71824 /* MaterialColor.h */, + 73500719BEBC67E98C2432BA726CF2BD /* UIColor+MaterialBlending.h */, + 1495FD5410C5BA9748915AE95726D978 /* UIColor+MaterialBlending.m */, + B829E8E11D02D1970F8C8FF989A82B64 /* UIColor+MaterialDynamic.h */, + 1555D5D35385492B8D027E811BE666CB /* UIColor+MaterialDynamic.m */, + ); + name = Color; + sourceTree = ""; + }; + CA0DA1B170B1BF48603D3494C22C1A5A /* Support Files */ = { + isa = PBXGroup; + children = ( + 1A9AE3381193F742879F523CCE471749 /* lottie-ios.modulemap */, + 1E5379887AF991A23AF0071E5BBA66A3 /* lottie-ios-dummy.m */, + 21ABC4AABBA66B5F87EF5A5C789FDC8F /* lottie-ios-Info.plist */, + 7821F9D3A25C026AA6AFACB2A720A953 /* lottie-ios-prefix.pch */, + 631CC31B1866E988A56C286024B8A805 /* lottie-ios-umbrella.h */, + 615BC7DD9F6466F845804084D0B12D66 /* lottie-ios.debug.xcconfig */, + 1CBA47FDA3E910C7EDFE324EFA897551 /* lottie-ios.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/lottie-ios"; + sourceTree = ""; + }; + CAE3BBA14C7998BA5F99F91C08201D62 /* Typography */ = { + isa = PBXGroup; + children = ( + 2FA60992213EE87D4775A402EF9DE39B /* MaterialTypography.h */, + 42A318810EC6F37DE9379EF21DE434C3 /* MDCFontScaler.h */, + 0D37C9BDBB809F1F5B2D0212FF9CFE99 /* MDCFontScaler.m */, + 87107EE4D974CAE959A375B662F4204F /* MDCFontTextStyle.h */, + 275AD0BC20557E9DC939C53DFF5BB2F3 /* MDCFontTraits.h */, + 789CAA7B6C1E469B4554820ADC2147D3 /* MDCFontTraits.m */, + 9DE63B387DA24E48BA2EDE84C68DC8DE /* MDCTypography.h */, + 5264280FEB93B40117162EB11A934890 /* MDCTypography.m */, + 8D638A90314C4C723DF58CBA2D02DCF3 /* MDCTypographyUtilities.h */, + 49FB87A2C6705951A697BFD94E3FFB81 /* MDCTypographyUtilities.m */, + B867765B8E02C5AE1B3C6A2B771DE65C /* UIFont+MaterialScalable.h */, + 8CE15CF0E1981C1CA722C1B5121C088C /* UIFont+MaterialScalable.m */, + 2AB8B4FEB07692832F02CED08212015A /* UIFont+MaterialSimpleEquality.h */, + 039BA34B5A5B9FE2B7971FE6E64557BB /* UIFont+MaterialSimpleEquality.m */, + 6E28A208954B54643898470FABAC2905 /* UIFont+MaterialTypography.h */, + D390D3DEF1579285A22574E5E6EA97D0 /* UIFont+MaterialTypography.m */, + F49594B80BEB495C857FE4A0C034A387 /* UIFont+MaterialTypographyPrivate.h */, + FE68F0EF6188796115A2408E20255E7C /* UIFont+MaterialTypographyPrivate.m */, + 4040341BD83B0FAE1A20FF1D71955BB5 /* UIFontDescriptor+MaterialTypography.h */, + EEBE2EE981029A2CA291F17EA8723B03 /* UIFontDescriptor+MaterialTypography.m */, + ); + name = Typography; + sourceTree = ""; + }; + CCFA8110E1E64DA6077C0338044C3E4B /* private */ = { + isa = PBXGroup; + children = ( + 45CC7D2BF1DD5127F441F274153CD862 /* Application */, + C7D23E365D4BA5B320803F40CDCBC339 /* Color */, + 63B72EF56693213EBD10E1799FD5D999 /* Icons */, + 7D6083E3CF506A7CD7A7FDACF3C3F86F /* Math */, + ); + name = private; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + B94D7768568A9992200DB461E8CF687F /* Frameworks */, + D284217121FE14E169EC541EA0329E52 /* Pods */, + 8DBB1FD9C1111191D999111C75E2985E /* Products */, + 420A7BACED161C14C7AD7697744FD6E6 /* Targets Support Files */, + ); + sourceTree = ""; + }; + D21A228721E7A09BEF782E68D4232144 /* iOS */ = { + isa = PBXGroup; + children = ( + 870DC3835055FF27A861C151FF69B97C /* CFNetwork.framework */, + 3117010738ED3E7B5080810EBF99FBBD /* CoreGraphics.framework */, + E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */, + EB57E704596E48FF3325A1F46B6E783C /* QuartzCore.framework */, + 617F2D7F52382550906F7F058A9681F9 /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + D284217121FE14E169EC541EA0329E52 /* Pods */ = { + isa = PBXGroup; + children = ( + 144970CAC0814448ACFB4EE458FC2F64 /* Alamofire */, + 97EE99652D0948DBD9E105410379C433 /* Chatto */, + 21AC7CA70041658C6EBD3A1F4775F58C /* lottie-ios */, + 2A51557057FA173A61DBFFD784D3E10D /* MaterialComponents */, + 5C1CCF8EE2CEEFA1A058DC0BBC9FDF58 /* MDFInternationalization */, + BB7FF24AAB31B14A5EC03B11DF4CCECB /* MDFTextAccessibility */, + ); + name = Pods; + sourceTree = ""; + }; + D462E74D0157D8AFD873CF8DF7F4AF97 /* ShadowElevations */ = { + isa = PBXGroup; + children = ( + 0056C47896966D18A6A1A5446B66D105 /* MaterialShadowElevations.h */, + D0F908B38DD2B1F35BBC0B8F214C6DC0 /* MaterialShadowElevationsDummy.m */, + 5BA153CC755CD4895140457FB4274997 /* MDCShadowElevations.h */, + ); + name = ShadowElevations; + sourceTree = ""; + }; + F9CF631A6EA3425C2C202FD24CC328CC /* ic_check_circle */ = { + isa = PBXGroup; + children = ( + DF79A6BB5BC283B6F1FFF7394D1ABD51 /* MaterialIcons+ic_check_circle.h */, + 2AF20B708D8368FA42456BB1F0DF92F7 /* MaterialIcons+ic_check_circle.m */, + AABDEFB693E15B50C7698B1026DED0F3 /* Resources */, + ); + name = ic_check_circle; + sourceTree = ""; + }; + FDD60C62F8B0E342211EE4076CD31BEA /* Buttons */ = { + isa = PBXGroup; + children = ( + 8DA90325405F33A5808A47E8BFFAEE6F /* MaterialButtons.h */, + 97BE17BC31FAF7F8B04D6EFFBBE78C80 /* MDCButton.h */, + F90CBF2D1339BB6CDDAFF78CC3B59761 /* MDCButton.m */, + 4C628C67659ABBADF02BA056396F87E1 /* MDCButton+Subclassing.h */, + 985D00F7F6511C37B9DECF5DBEB3B451 /* MDCFlatButton.h */, + 6828FC052E6421784F770E0D6078DDB6 /* MDCFlatButton.m */, + F873EC876A8574DA2C7425CA4C3BC418 /* MDCFloatingButton.h */, + C7C60015DC526524C5DC274704897564 /* MDCFloatingButton.m */, + 4838FCADE32426C4BC81299CE2906DE1 /* MDCFloatingButton+Animation.h */, + D8F1688277322547EA5D6CCDF2C94C1F /* MDCFloatingButton+Animation.m */, + 4148219036DBEF09983EE603792119B1 /* MDCFloatingButtonModeAnimator.h */, + 9A1732F4D5612B18CF4BCB5A0D5F9A8C /* MDCFloatingButtonModeAnimator.m */, + 1B239243820382C1C6E7038645E10958 /* MDCFloatingButtonModeAnimatorDelegate.h */, + 4BF5C7C1F8F18927BF9A4EBDE9886621 /* MDCRaisedButton.h */, + 9FF5E5B45041E7444D6447C17872668F /* MDCRaisedButton.m */, + ); + name = Buttons; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 14FF1799C5ADBC71E1DB963F2AF8853D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0B399DCF32F8FE4F09B03B6E7B65E0D1 /* Alamofire-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 283F66161952BCC65E708C095C437A15 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 023569941EF2F377A6F87A87062B479A /* MDFColorCalculations.h in Headers */, + 59DB1972DC0FB82B837041D7ADA10E9B /* MDFImageCalculations.h in Headers */, + E46C272C4B95CBC92CDA9A2519D37833 /* MDFTextAccessibility.h in Headers */, + EAAFAB74BE099CA0217F13D56892CC50 /* MDFTextAccessibility-Bridging-Header.h in Headers */, + 84F43C1B8B0B179BBFF86DE99BBF4422 /* MDFTextAccessibility-umbrella.h in Headers */, + 32E691399B269968E2DD2CAD9F66C4F9 /* NSArray+MDFUtils.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 415B9BA66497D555D9D735FC58884B30 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7D990BA3B83998A2A1ABD69E0F3D44CE /* Pods-ios-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4E88A7409AFD7F544020657A40A06594 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A0E0BC90F446AAFA84B16933CE45D172 /* CAMediaTimingFunction+MDCAnimationTiming.h in Headers */, + FC622CB054985417281FE0FB6755C6DD /* MaterialAnimationTiming.h in Headers */, + 85AF5816CC3948E8200711491BA938E5 /* MaterialApplication.h in Headers */, + D4939BB9A0101F96A5D863184AE21A7F /* MaterialAvailability.h in Headers */, + 27004AABBE0496F9D5C7B3D8450F657B /* MaterialButtons.h in Headers */, + 02E41CCFF8E90ACE9B3F5267F6FE3DB7 /* MaterialCards.h in Headers */, + 4B9A1C30219659C0AA8D1AE79832347B /* MaterialColor.h in Headers */, + 22AA981BFEFAFBA8C6B511348D60D284 /* MaterialComponents-umbrella.h in Headers */, + 9E361C56F23EEFCEBF62E79BF5165AD6 /* MaterialElevation.h in Headers */, + 41A27CED3C6DB6554EBDD9BA47356969 /* MaterialIcons.h in Headers */, + 785E547BC62FE180EDB4F80976888437 /* MaterialIcons+ic_check_circle.h in Headers */, + 2866EB6C78D5E0A3256F868DA33E06DA /* MaterialInk.h in Headers */, + 597AB8E13D0B44321059C630A47807D7 /* MaterialMath.h in Headers */, + E1B1195009B9F9BD2E5EFE2113CFE20A /* MaterialRipple.h in Headers */, + 9D880C5CFBE97A142FC516777217C492 /* MaterialShadow.h in Headers */, + 4B4B75F4B215FD3E854871B3E1F31CD3 /* MaterialShadowElevations.h in Headers */, + C52D6111A5A724CED4C0451F2D8D7F76 /* MaterialShadowLayer.h in Headers */, + 002C20BF2F798D2C64290D641171C7F3 /* MaterialShapeLibrary.h in Headers */, + 96A6F504929F868038D46B5B6A03038B /* MaterialShapes.h in Headers */, + FDE49FD4B92B698131BA846A877E4F5C /* MaterialTypography.h in Headers */, + E9CD4CB2CB433D9920922378F7886DAF /* MDCAvailability.h in Headers */, + 94C1D0EC92D3122730BAC6014910D19F /* MDCButton.h in Headers */, + AEDBDAA38F82EC2FA51759F782D4D5AC /* MDCButton+Subclassing.h in Headers */, + C78971D6EF0C7FFBCF847261C2015F62 /* MDCCard.h in Headers */, + D3B9D2340450E1A03FC812FED6DE209C /* MDCCardCollectionCell.h in Headers */, + A093909FE7269A84364560DDC9375F31 /* MDCCornerTreatment.h in Headers */, + 9DAA6E25378FE244F7D4451A5D35FB51 /* MDCCornerTreatment+CornerTypeInitalizer.h in Headers */, + 7429801C83C663E2AC03DF593EB09F26 /* MDCCurvedCornerTreatment.h in Headers */, + E80D58FB9E5F025ED34C4817E1D03D20 /* MDCCurvedRectShapeGenerator.h in Headers */, + 7B71F5D8CEF38A4E44B101984BF32A22 /* MDCCutCornerTreatment.h in Headers */, + 1D313F4071D041D30364BC379612A7C8 /* MDCEdgeTreatment.h in Headers */, + DF4526CA0220910B45398C91C207AB2A /* MDCElevatable.h in Headers */, + BC95E3D42356C72892097F3C727F8ED4 /* MDCElevationOverriding.h in Headers */, + F59346B37473C393BC5B9573CA75D311 /* MDCFlatButton.h in Headers */, + B713DE1857A1BE1020811FB842D67B1F /* MDCFloatingButton.h in Headers */, + 97030F2617BD73C118C8334624A84458 /* MDCFloatingButton+Animation.h in Headers */, + 5CDEA5D93507BB56778DFE0FF9D755B6 /* MDCFloatingButtonModeAnimator.h in Headers */, + 32DA6FCE9DD284D13AC2A4CE1259320D /* MDCFloatingButtonModeAnimatorDelegate.h in Headers */, + D04A750433B71EFFE369931A5990825E /* MDCFontScaler.h in Headers */, + 9FA8E4B6DCC77FC54C0E72979D87FDDA /* MDCFontTextStyle.h in Headers */, + 7187BA8BB4975F949B9A3E5413EF29E6 /* MDCFontTraits.h in Headers */, + 3C1AF825830E083E899D53A5C2F7E3EB /* MDCIcons.h in Headers */, + 59CA53BBA8B52B7565A5B31E205B5DBE /* MDCIcons+BundleLoader.h in Headers */, + 34426DF353C060CB74307404D7788A7F /* MDCInkGestureRecognizer.h in Headers */, + 048E7F185D045CEAB317FF30D739E39C /* MDCInkLayer.h in Headers */, + FAF429A0D030A7708AC3E1C890CDFBBC /* MDCInkLayerDelegate.h in Headers */, + 6BFC3EBA0293805C586DED141F57D440 /* MDCInkTouchController.h in Headers */, + CA13097C8726C13E7D19984C391E156B /* MDCInkTouchControllerDelegate.h in Headers */, + 5269279BA5DAB06E59635312FF2FCC76 /* MDCInkView.h in Headers */, + E47B9E036187569C65F248B6D527020E /* MDCInkViewDelegate.h in Headers */, + 8BBBA0F8098CEDCDE429C22E32810395 /* MDCLegacyInkLayer.h in Headers */, + 04FBE0F7CABE3A8D6913CC677B5557DB /* MDCLegacyInkLayer+Private.h in Headers */, + A957485EF720B71593D3C8160C44B9DF /* MDCLegacyInkLayerDelegate.h in Headers */, + B3CB521DFC1B2FC4C400466B2636C9DA /* MDCLegacyInkLayerRippleDelegate.h in Headers */, + E70A755D57CC8683634F611358142A89 /* MDCMath.h in Headers */, + 7A42366DBD41A248734C3A9973EC9DE9 /* MDCPathGenerator.h in Headers */, + 336037502C5A263DA6CC3831EF8EFD9F /* MDCPillShapeGenerator.h in Headers */, + BB9A0A0E2A295ECBE4DF5DD2BCC49A6E /* MDCRaisedButton.h in Headers */, + 0EDBF34C06554E9537C4DFCBAF24433E /* MDCRectangleShapeGenerator.h in Headers */, + 8B8F97130DA7C5ACCF2ADBD9A8B8C138 /* MDCRippleLayer.h in Headers */, + A4D811A62B4CD50FEF2943AB7A090055 /* MDCRippleLayerDelegate.h in Headers */, + 0F7ADF590B5BBABFEE77D9E562058102 /* MDCRippleTouchController.h in Headers */, + 370A0DE2184C02814935D61032C2E189 /* MDCRippleTouchControllerDelegate.h in Headers */, + D620FDB5BDD15F3DF283F6989DAEF5C7 /* MDCRippleView.h in Headers */, + 1A3B96F4D5840C14E50F8320D2E81542 /* MDCRippleViewDelegate.h in Headers */, + 74B1E17BF464786B34DEB58A508B48B4 /* MDCRoundedCornerTreatment.h in Headers */, + F129D1B53CBB7D4F4F19F7C651C9EF04 /* MDCShadow.h in Headers */, + 0828664E6967E49C11D8F03D40827F40 /* MDCShadowElevations.h in Headers */, + AA41EBE1B990DE355E95E626BCDE0D20 /* MDCShadowLayer.h in Headers */, + 378706EAE3C525A5A25FD652CF35ED3A /* MDCShadowsCollection.h in Headers */, + B59FA52B2FA220A61E7B371F6902BF61 /* MDCShapedShadowLayer.h in Headers */, + 70BC4C88A01730C10637E04170D18842 /* MDCShapedView.h in Headers */, + 87882C5A446E492E7BC82763056E299E /* MDCShapeGenerating.h in Headers */, + 6FF5AC95881C5994A17183FCD68D9760 /* MDCShapeMediator.h in Headers */, + AD9CE391F7F42CA08809086C88DA94F8 /* MDCSlantedRectShapeGenerator.h in Headers */, + F337417E09334F51565A55905275A05E /* MDCStatefulRippleView.h in Headers */, + 10F1A59D76699A85A3D3F9CE14D68340 /* MDCTriangleEdgeTreatment.h in Headers */, + 3CE69A673E4CCBBC2BF363C34B3E4702 /* MDCTypography.h in Headers */, + 1C63F278B31BE01E9A399C213588B1F5 /* MDCTypographyUtilities.h in Headers */, + 7514339E0BB67279B4151F7E7C9DF20D /* UIApplication+MDCAppExtensions.h in Headers */, + E06D3EE4B89C14DF0F1EACA6DB9DCF87 /* UICollectionViewController+MDCCardReordering.h in Headers */, + 731FB6C1A89663C6273A5ED2A93838A2 /* UIColor+MaterialBlending.h in Headers */, + F03C958A2DC83E362F4E76C1758F2E99 /* UIColor+MaterialDynamic.h in Headers */, + C1C2719D78A6372AF387756B6D2FB07B /* UIColor+MaterialElevation.h in Headers */, + 64A1B6BAE055F3BEAD2244B06B758B47 /* UIFont+MaterialScalable.h in Headers */, + A6487D3314DFDA70D3EF7D709AFD1A17 /* UIFont+MaterialSimpleEquality.h in Headers */, + 82558E399C6304091C91304BC4E9FAD1 /* UIFont+MaterialTypography.h in Headers */, + 74804BD8118FAA4C1A0F62E7A9E8F8B7 /* UIFont+MaterialTypographyPrivate.h in Headers */, + FCA5FAFF46860FFD052B5A96A4253915 /* UIFontDescriptor+MaterialTypography.h in Headers */, + 7EF5AA59D08C107D83A5E3FE51633211 /* UIView+MaterialElevationResponding.h in Headers */, + 0FF000CEEA87B583977819DE456F949B /* UIView+MDCTimingFunction.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4EA3A0C175DC3B08CF7DF0AC686D56CA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 114CD9D207171C75415C5B60E0609675 /* Pods-iosTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61AEBA9D4508B355E6E380F1D4D3988A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FFFCB73998E59CCA79007CF915D56E22 /* Chatto-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 82946A5BB19511200C787055222CC155 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1062EDD2C5C3B8D7A91592F639600D74 /* MDFInternationalization.h in Headers */, + 1DCF9A800AE274F89C29078A6E212B7D /* MDFInternationalization-umbrella.h in Headers */, + 68C81A493C4CD3F1D8425C3C12B6F1E3 /* MDFRTL.h in Headers */, + 3CE30BDD410305F4D3C8C13D12507A4A /* NSLocale+MaterialRTL.h in Headers */, + 9F64E84B54A721D525FB5FEB6B04AD92 /* NSString+MaterialBidi.h in Headers */, + F95134B340BDE8537CEDC91162AE776B /* UIImage+MaterialRTL.h in Headers */, + E56C430EEEEAF6E39FF0942CCD78A061 /* UIView+MaterialRTL.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DAE149930D25CBB9B05F744248F36732 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 535D24555149DB4F16003AEB62B2F4FE /* lottie-ios-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0B967D7F8561D42493EE289EC8D450D1 /* lottie-ios */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0358E8E05175A7E59A2AEECD12A1801E /* Build configuration list for PBXNativeTarget "lottie-ios" */; + buildPhases = ( + DAE149930D25CBB9B05F744248F36732 /* Headers */, + 37AE70FCA26952762C9821CF88074221 /* Sources */, + 78FC9E52ECE20C8C02B38B218A9F8D6C /* Frameworks */, + D3974EDA7759ECBA83AABE9D7FCFA671 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "lottie-ios"; + productName = Lottie; + productReference = 51BA97E8B5085EFFB47BC9C0B785CEA7 /* lottie-ios */; + productType = "com.apple.product-type.framework"; + }; + 2508BC3B908322C9E85E61AE743C9842 /* Chatto */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8FE49F8617D5D677A3808ABE3B492E82 /* Build configuration list for PBXNativeTarget "Chatto" */; + buildPhases = ( + 61AEBA9D4508B355E6E380F1D4D3988A /* Headers */, + 66C6B183F08D1564AE5191E32440E60D /* Sources */, + 28BA49E77F658083C4A924833FF0CF7D /* Frameworks */, + 3429006454DB433D0F4FBF2F4E05E02F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Chatto; + productName = Chatto; + productReference = BE3893C08724F3D82CF1D199CF98F274 /* Chatto */; + productType = "com.apple.product-type.framework"; + }; + 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */ = { + isa = PBXNativeTarget; + buildConfigurationList = 29235CC975477CF0AE111D8533A857C9 /* Build configuration list for PBXNativeTarget "MDFInternationalization" */; + buildPhases = ( + 82946A5BB19511200C787055222CC155 /* Headers */, + 85B3B64C90CFC972449F93DC4A812BBD /* Copy . Public Headers */, + 75C42EE808FFAC35041568CFE92847AD /* Sources */, + A563BE445A159E73000F25A2BC3B940E /* Frameworks */, + AED6EDF087DBE63D55C51AEE20F3373F /* Resources */, + 498506168756522E454E1B48EC2886A7 /* Create Symlinks to Header Folders */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MDFInternationalization; + productName = MDFInternationalization; + productReference = 9A2AC0B8BAC6EF061EBC28825653DF99 /* MDFInternationalization */; + productType = "com.apple.product-type.framework"; + }; + 7F4022CD4249985E761F745058CAB372 /* MaterialComponents-MaterialIcons_ic_check_circle */ = { + isa = PBXNativeTarget; + buildConfigurationList = 81EE041A2E698878FED9512843DEEE02 /* Build configuration list for PBXNativeTarget "MaterialComponents-MaterialIcons_ic_check_circle" */; + buildPhases = ( + 04C07BCDE98F7ACB2E4A7BB253EB79D4 /* Sources */, + A46E636AA9FD7591DACA5A99D724E081 /* Frameworks */, + 6ECA9F6DF30D309B065300A72DD99BC8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "MaterialComponents-MaterialIcons_ic_check_circle"; + productName = MaterialIcons_ic_check_circle; + productReference = 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */; + productType = "com.apple.product-type.bundle"; + }; + A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */ = { + isa = PBXNativeTarget; + buildConfigurationList = E9D2A6F4024608ED9B15BD3B46315019 /* Build configuration list for PBXNativeTarget "MDFTextAccessibility" */; + buildPhases = ( + 283F66161952BCC65E708C095C437A15 /* Headers */, + 2A5DC5F3AAB66AD530D07A5A2ED44DBF /* Sources */, + 052B815FEFBCEB5CBBA08741F810D971 /* Frameworks */, + 3EED30752BC2816FBBD27B8B9CBF4E0C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MDFTextAccessibility; + productName = MDFTextAccessibility; + productReference = FB8220FA4D9117310AEFC629A6EE4A08 /* MDFTextAccessibility */; + productType = "com.apple.product-type.framework"; + }; + B4723B5744013DF08BE3A5FEAA286944 /* MaterialComponents */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4D07C4D46B8088CB6927DB0DC7D38E87 /* Build configuration list for PBXNativeTarget "MaterialComponents" */; + buildPhases = ( + 4E88A7409AFD7F544020657A40A06594 /* Headers */, + 390B4779DF669199CC4F894284D2B29B /* Sources */, + B05B2D2B6059C5982EE1D520AA115030 /* Frameworks */, + 8CBB757F4B8EF000D844E9F6690D3F8E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8907A64A1B702FF7E8F6F71C8D6AE9A2 /* PBXTargetDependency */, + 12A0FCF3F802AE49C0D0CB64AA7E43A7 /* PBXTargetDependency */, + 6C1B5C5B5ECFC4AE40FE227FD0468177 /* PBXTargetDependency */, + ); + name = MaterialComponents; + productName = MaterialComponents; + productReference = A34157B3708EDB7D17422B3D92F591D2 /* MaterialComponents */; + productType = "com.apple.product-type.framework"; + }; + CB5AA7A4FA94F4765AE579D6F4491956 /* Pods-ios */ = { + isa = PBXNativeTarget; + buildConfigurationList = 994FA3DA95C9295DD26DDD2C48D33FDF /* Build configuration list for PBXNativeTarget "Pods-ios" */; + buildPhases = ( + 415B9BA66497D555D9D735FC58884B30 /* Headers */, + 621071A550AC52510305FD30232CA78F /* Sources */, + 1D5C89C35ED64587AA3F3CC9FB379C53 /* Frameworks */, + 6800757B2C01C036D46DC98BA04AFFE2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 14095B8F88A69E2127AC168198A47484 /* PBXTargetDependency */, + 824DAEEDCF96F9DE264359D187EE3D7C /* PBXTargetDependency */, + DA0287AB868FBE7422C398193D171599 /* PBXTargetDependency */, + BD0671634A3423E811F1923A94A58215 /* PBXTargetDependency */, + 05A590F9EC9D0B3EA47F7735F7CA587A /* PBXTargetDependency */, + ); + name = "Pods-ios"; + productName = Pods_ios; + productReference = DB84425C5097649DB7039BCDFCA4DB6C /* Pods-ios */; + productType = "com.apple.product-type.framework"; + }; + D9756ACF066CEFEBC5A1189E58A912E8 /* Pods-iosTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 04278286CEB96ED1B5A7A932E21D012F /* Build configuration list for PBXNativeTarget "Pods-iosTests" */; + buildPhases = ( + 4EA3A0C175DC3B08CF7DF0AC686D56CA /* Headers */, + 874513F819B24A84534CB3731FFFCB2E /* Sources */, + F9B2E1995C19B72DC80DBCAAE398BC33 /* Frameworks */, + BDEE1FCBE18460C6B864496DCB3ACF0D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 95A503489BD245D0353433835A3DD6A8 /* PBXTargetDependency */, + 8E26205BE12108119E077CD520CE9795 /* PBXTargetDependency */, + ); + name = "Pods-iosTests"; + productName = Pods_iosTests; + productReference = 924ED3C5CACD9B01ED8F6ACDCE0843EE /* Pods-iosTests */; + productType = "com.apple.product-type.framework"; + }; + EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9C98220D3187BF01A20E296DC128BED4 /* Build configuration list for PBXNativeTarget "Alamofire" */; + buildPhases = ( + 14FF1799C5ADBC71E1DB963F2AF8853D /* Headers */, + 5FE9836A67EA3E51CA889A1AB95BC874 /* Sources */, + 39D530C2A3085A1033683EC9AE0BC313 /* Frameworks */, + 93ECA2D9F79614966DFA76280ABFEF67 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Alamofire; + productName = Alamofire; + productReference = 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1300; + LastUpgradeCheck = 1300; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = 8DBB1FD9C1111191D999111C75E2985E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */, + 2508BC3B908322C9E85E61AE743C9842 /* Chatto */, + 0B967D7F8561D42493EE289EC8D450D1 /* lottie-ios */, + B4723B5744013DF08BE3A5FEAA286944 /* MaterialComponents */, + 7F4022CD4249985E761F745058CAB372 /* MaterialComponents-MaterialIcons_ic_check_circle */, + 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */, + A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */, + CB5AA7A4FA94F4765AE579D6F4491956 /* Pods-ios */, + D9756ACF066CEFEBC5A1189E58A912E8 /* Pods-iosTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3429006454DB433D0F4FBF2F4E05E02F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3EED30752BC2816FBBD27B8B9CBF4E0C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6800757B2C01C036D46DC98BA04AFFE2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6ECA9F6DF30D309B065300A72DD99BC8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B2CE1396D0B81AF08A5F6C886FF6E61C /* MaterialIcons_ic_check_circle.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8CBB757F4B8EF000D844E9F6690D3F8E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DC8E9D2FBC9CB113C32F1F2DD4DCA22 /* MaterialComponents-MaterialIcons_ic_check_circle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 93ECA2D9F79614966DFA76280ABFEF67 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AED6EDF087DBE63D55C51AEE20F3373F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDEE1FCBE18460C6B864496DCB3ACF0D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D3974EDA7759ECBA83AABE9D7FCFA671 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 498506168756522E454E1B48EC2886A7 /* Create Symlinks to Header Folders */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Create Symlinks to Header Folders"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"$CONFIGURATION_BUILD_DIR/$WRAPPER_NAME\" || exit 1\nif [ ! -d Versions ]; then\n # Not a versioned framework, so no need to do anything\n exit 0\nfi\n\npublic_path=\"${PUBLIC_HEADERS_FOLDER_PATH#$CONTENTS_FOLDER_PATH/}\"\nif [ ! -f \"$public_path\" ]; then\n ln -fs \"${PUBLIC_HEADERS_FOLDER_PATH#$WRAPPER_NAME/}\" \"$public_path\"\nfi\n\nprivate_path=\"${PRIVATE_HEADERS_FOLDER_PATH#$CONTENTS_FOLDER_PATH/}\"\nif [ ! -f \"$private_path\" ]; then\n ln -fs \"${PRIVATE_HEADERS_FOLDER_PATH#$WRAPPER_NAME/}\" \"$private_path\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 04C07BCDE98F7ACB2E4A7BB253EB79D4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2A5DC5F3AAB66AD530D07A5A2ED44DBF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3C51EC6664C4867B564D0B6604C7738E /* MDFColorCalculations.m in Sources */, + 0C7FEE5AB0CD94782BD549DCF9974205 /* MDFImageCalculations.m in Sources */, + D323E03646FFB2D4FA1C0633BE363EC0 /* MDFTextAccessibility.m in Sources */, + 92DA5E1124FFD63F1FD2CB5B3A106B3F /* MDFTextAccessibility-dummy.m in Sources */, + 47A9B68E4429C21FB25951BF68989FA8 /* NSArray+MDFUtils.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 37AE70FCA26952762C9821CF88074221 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40FA888B44A7961B13F0E7B332102CFC /* AnimatedButton.swift in Sources */, + 7D6751B1F74E12C7B85C8A86EF398113 /* AnimatedControl.swift in Sources */, + AA578A39DA8FF95C6E3B387DE3C8654A /* AnimatedSwitch.swift in Sources */, + 0711A9391B98D049C553FEF13D38C1AD /* Animation.swift in Sources */, + 185E3D77C15AC7CE8A4870A909237FCA /* AnimationCacheProvider.swift in Sources */, + DC21E754F631A9780BEF04CB9EC24C23 /* AnimationContainer.swift in Sources */, + 004D58D25F2EB03B40ADB43039641BC0 /* AnimationContext.swift in Sources */, + 654D4B8FB3BA49FB52CF733DF0F28CDF /* AnimationFontProvider.swift in Sources */, + EC2275BC165A2F54AC3498E0FDB085C9 /* AnimationImageProvider.swift in Sources */, + 335CCDCEBD9EB7CB33697888F3E0D6F8 /* AnimationKeypath.swift in Sources */, + 37E3D167D051B6E64ACCC71D8111CBE1 /* AnimationKeypathExtension.swift in Sources */, + BF61BD1B780F84EB855CDC8D07C70CF6 /* AnimationPublic.swift in Sources */, + D9D325BD0837A5B3F2C4368B4AF42D8D /* AnimationSubview.swift in Sources */, + 25C84FA79FF9DCAA88669C3380168F44 /* AnimationTextProvider.swift in Sources */, + 12AC4439EB9B96B847691B34BB75656D /* AnimationTime.swift in Sources */, + 2ADBB4BE80F61C7FF2AC39558549DDC1 /* AnimationView.swift in Sources */, + 0505586ED69AFD180618E639E5526C63 /* AnimationViewInitializers.swift in Sources */, + A6735D2299B4CAEA6BE631073D8BA734 /* AnimatorNode.swift in Sources */, + F659E0EBE31959B1758B3628AB4720B4 /* AnimatorNodeDebugging.swift in Sources */, + F8460445640FB3C461E58C1DFA2E0F16 /* AnyNodeProperty.swift in Sources */, + 35A265AD5CA62B93ED1BE1780D22B92B /* AnyValueContainer.swift in Sources */, + 9476C7CB756F02F62FF85F3AC6A74FDE /* AnyValueProvider.swift in Sources */, + 626F1E2369BBC5E226FCB50B0B3D74A2 /* Asset.swift in Sources */, + 8C59CE48E9D049159ECD769482024C7A /* AssetLibrary.swift in Sources */, + 1A7D0E6F5105EC32427DBB1983EC27BE /* BezierPath.swift in Sources */, + 4D06CA57EA8BFCEC35A0E97BEA2DF034 /* Bundle.swift in Sources */, + 856565C8AF270085E80E72C5D81FA186 /* BundleImageProvider.swift in Sources */, + AABFEA684B7B708D497FCACE7A03FFF7 /* CGFloatExtensions.swift in Sources */, + C32D94C3DBA969C96E803632C0F15D5E /* Color.swift in Sources */, + 5E479B4E6701DA0CEA6D0A57C50DD9DC /* ColorExtension.swift in Sources */, + 7C49833872601FCD2511E3717A726467 /* ColorValueProvider.swift in Sources */, + D1662BB63BC57C2FEAA5E16F73DFB9C9 /* CompatibleAnimationKeypath.swift in Sources */, + 92D5715E90A1540B39AFE10F3D2A5443 /* CompatibleAnimationView.swift in Sources */, + DB65627B75E519F4B1B65221BA00176A /* CompositionLayer.swift in Sources */, + B7964B8982C863F7AA9A9A0051387976 /* CompositionLayersInitializer.swift in Sources */, + EB1C650699E9DA20023D24CCB95944D4 /* CompoundBezierPath.swift in Sources */, + D681FB9211B3EB99507A7659A926AD3B /* CurveVertex.swift in Sources */, + A9778B7C3AC696E47DF7D19ABEBA80C9 /* DashPattern.swift in Sources */, + C16B4056D4AF47CF2BDF2B30E9ED27B1 /* Ellipse.swift in Sources */, + A76309F8030847107CE541B3B173FC24 /* EllipseNode.swift in Sources */, + C58D6FE775EF73EB56A0DB0F81E9F505 /* FilepathImageProvider.swift in Sources */, + B5C83F05C6D42CD1F1AA5322952A475F /* FillI.swift in Sources */, + 6BC837979F6FD58ABE2029174745CF8F /* FillNode.swift in Sources */, + 06894D1A68123F284D70772314BBC171 /* FillRenderer.swift in Sources */, + A9B10FC18D020818728E285314FDEDAD /* FloatValueProvider.swift in Sources */, + A32194DA20A525E2A0DD2C1DC780FF99 /* Font.swift in Sources */, + 3ED06C6A277226FD2D9410BAC8E6649D /* Glyph.swift in Sources */, + B5EB74E6B21E71690D59818B99FD9F4B /* GradientFill.swift in Sources */, + 6CEDCE10C0318529B41437117724D2A3 /* GradientFillNode.swift in Sources */, + FF82E104EC48B26651A60B944BFFA297 /* GradientFillRenderer.swift in Sources */, + 461BEAD29EE3D6E33BCDD8951CA80FD3 /* GradientStroke.swift in Sources */, + 8917A3295F44CCF46EEB66EC69B6DC31 /* GradientStrokeNode.swift in Sources */, + 5E5703B9B2DCBD225054E143EDE69080 /* GradientStrokeRenderer.swift in Sources */, + D28B26A308824EEAE6D48615A1E0D14C /* GradientValueProvider.swift in Sources */, + 388CB67650DEC60578CD193C099F93C7 /* Group.swift in Sources */, + 388951286A46DDEBC11081395CE19424 /* GroupInterpolator.swift in Sources */, + 489308FC142DC204B40A895C3A6F3A1B /* GroupNode.swift in Sources */, + 84C8B10EB6AFC1DB7574650206F73019 /* GroupOutputNode.swift in Sources */, + B50CAE951FE6B2B57ADDE94632B53B82 /* ImageAsset.swift in Sources */, + BECE55736F8FE0964E095D93B134E4E0 /* ImageCompositionLayer.swift in Sources */, + 509492048834DCECFB5FAC619B80CDBA /* ImageLayerModel.swift in Sources */, + 49E32757F5D405E67A46B71E862DE633 /* Interpolatable.swift in Sources */, + A9613D5848A69B5554E49963050E189E /* InterpolatableExtensions.swift in Sources */, + EB0960E12A2CB2EDF98C11A39F506A15 /* InvertedMatteLayer.swift in Sources */, + 42DD01B84A5FF2063B4C6ECA671FCE3E /* ItemsExtension.swift in Sources */, + 0E97277DFF7EA15585CCD7FD4F881799 /* KeyedDecodingContainerExtensions.swift in Sources */, + AFE7E0A95861830B7BE0CFCF493F662E /* Keyframe.swift in Sources */, + 1C994289D94896DEAE034CD8A0B4CDF0 /* KeyframeExtensions.swift in Sources */, + B061007C0C097FF93364AC9411CAFCDD /* KeyframeGroup.swift in Sources */, + 728FBE5D5AFDA125AB4D0FF6F66DDDA0 /* KeyframeInterpolator.swift in Sources */, + 3730BD7F19450F90CC632D633517DCD8 /* KeypathSearchable.swift in Sources */, + F1665937EE88175C13BD066950658EDB /* LayerDebugging.swift in Sources */, + DEBE3EC1FC8DCA47B2FFC71153B0636B /* LayerFontProvider.swift in Sources */, + 45E483574827876CEA2E3D2DE49257BF /* LayerImageProvider.swift in Sources */, + 874F69B4F2476462C2E8952A231DEF39 /* LayerModel.swift in Sources */, + CE150C3D6D3F9D4DA4F25E6B2831A385 /* LayerTextProvider.swift in Sources */, + F79954C7D48B356B32ACA8BDDFDD183E /* LayerTransformNode.swift in Sources */, + FF11F9BF6D01BF9AFD49B1831C74E77F /* lottie-ios-dummy.m in Sources */, + 2E8A20C40A0F48D1F8C0806CE740853D /* LottieView.swift in Sources */, + 3C044F4DA0AAF294B5B40D8D61F76067 /* LRUAnimationCache.swift in Sources */, + 442D9429E34A98231CFCDE3F82B30DE5 /* Marker.swift in Sources */, + DC6BF07D4BE73BA3084613035CD88F56 /* Mask.swift in Sources */, + C6464DD1F08A953F238EAD6328090708 /* MaskContainerLayer.swift in Sources */, + CA9F252FDC0DCF869BDE8A27E5B03BA0 /* MathKit.swift in Sources */, + 2CE76602B51D001D59FCC5CA12BEC37B /* Merge.swift in Sources */, + 092666672EA5B706BA5DAEAE9B49444F /* NodeProperty.swift in Sources */, + 7B90B8B1E94AA7BCB6F8195B232C0504 /* NodePropertyMap.swift in Sources */, + EB4388F68E8E658447BDAC97AE5805C2 /* NullCompositionLayer.swift in Sources */, + C97B9E8FA09F8CDA9458EC1387BBB6A2 /* PassThroughOutputNode.swift in Sources */, + E2BFA4A6A408809ECF183418A7368E1C /* PathElement.swift in Sources */, + 059F26929B3EB05770116463CC9DE9BD /* PathNode.swift in Sources */, + 59E047AE8E4BE5C88696952252BF2AF4 /* PathOutputNode.swift in Sources */, + 1899682FBADFB9FAE8EBCF416FD2C577 /* PointValueProvider.swift in Sources */, + CD82340C0739E932322EB77FBCFA6D29 /* PolygonNode.swift in Sources */, + CFD442A95C590C28AA710A2A228F9F4B /* PrecompAsset.swift in Sources */, + 22FFC6C3AAFCD260E0DAB32352B704A9 /* PreCompLayerModel.swift in Sources */, + CEDB69EFF8FEDC065680C80B49BDC5EE /* PreCompositionLayer.swift in Sources */, + C02D5ACD4E1A80E8AE4FFC958652C0AA /* Rectangle.swift in Sources */, + 51BE4D64CB57A36659FD73D354F810F0 /* RectNode.swift in Sources */, + 274C601104AC3151F7A65A4F15A82F3D /* RenderNode.swift in Sources */, + DB44F781C343FE7260506125A2B217D5 /* Repeater.swift in Sources */, + F457424CFB7849798D80D62A5EF98BB0 /* Shape.swift in Sources */, + B1F8084190567650796949754C0D25E7 /* ShapeCompositionLayer.swift in Sources */, + D2EB1D5874469F96E26EF47829867231 /* ShapeContainerLayer.swift in Sources */, + 5DEC3A3BBE20B44348CE23832360AB86 /* ShapeItem.swift in Sources */, + 4FAB52F6DBCC8A48A6FC5B865CF4984D /* ShapeLayerModel.swift in Sources */, + 49D5F0C6A4922BCF3D0B41DFDCE905CA /* ShapeNode.swift in Sources */, + 8AEE5FA821A6A42749CFDF8DD6CDF413 /* ShapeRenderLayer.swift in Sources */, + 3A8BFA61B606E1EA5527EF2D18D49D4D /* ShapeTransform.swift in Sources */, + 99D85B4B92B509E63CB8C3056CF601C5 /* SingleValueProvider.swift in Sources */, + 569D15C26BA16947069372B7D6269C51 /* SizeValueProvider.swift in Sources */, + 629777492DD9FCEC0E6BADDED7A06292 /* SolidCompositionLayer.swift in Sources */, + 6C945C98391E1347EE968F36DABA4B3D /* SolidLayerModel.swift in Sources */, + 4AF0F0425EE13873D1A3D72C87FD0CFE /* Star.swift in Sources */, + 40F06CD846C60BCA8E500808BFC5D8AB /* StarNode.swift in Sources */, + BD275ABF1DF797F0313866144001AE2D /* StringExtensions.swift in Sources */, + 9D3A2386D2F126134C1A61111BAF2B8C /* Stroke.swift in Sources */, + 6A6B387C7487CCA8B700C94CAE4297C3 /* StrokeNode.swift in Sources */, + 7004F49ED8B75EF71E86E1BB0BF7F31B /* StrokeRenderer.swift in Sources */, + 0716AAD8964E0F60518EF92178614471 /* TextAnimator.swift in Sources */, + 20F80E96D387808DB7427A1249E8EADF /* TextAnimatorNode.swift in Sources */, + 84DA7DB4EF4E0E8B628B429882B14FBB /* TextCompositionLayer.swift in Sources */, + A3D5AADA1B068C272F4C41E9771EE195 /* TextDocument.swift in Sources */, + 8143C58AA2F1E117E2A7BF20E9582EF3 /* TextLayer.swift in Sources */, + 2A94738462743EA303248E706CF448BB /* TextLayerModel.swift in Sources */, + C6A87EEB51FF906E9FF947949211D34E /* Transform.swift in Sources */, + D66484BFD46F95EE237049C7F9150C76 /* Trim.swift in Sources */, + 86DB9FBEAE3DE9E248D74A04EEE10875 /* TrimPathNode.swift in Sources */, + EC9E770804940B15285BD22E29A6FCBA /* UIColorExtension.swift in Sources */, + 4FC0BE22D1389E78DCDC8515F3C1F124 /* ValueContainer.swift in Sources */, + 0A4178C93B7F1DF931BF92BC4D971841 /* Vectors.swift in Sources */, + 1F3D77EAFD74D2CFE5C43D46FD93C6D8 /* VectorsExtensions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 390B4779DF669199CC4F894284D2B29B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7154A1C49053E5493963962660A03513 /* CAMediaTimingFunction+MDCAnimationTiming.m in Sources */, + CA24AD420264A0A9E0481C2F2E062AB0 /* MaterialComponents-dummy.m in Sources */, + E990970757CDEBE53FA26AA40A17BD7E /* MaterialIcons+ic_check_circle.m in Sources */, + 593F9E60F9CA978FD7DEF9A6C0EB917D /* MaterialMathDummy.m in Sources */, + E43BAEAC9F260161234AFCA6EE7CA2B9 /* MaterialShadowElevationsDummy.m in Sources */, + 1378724980581463BB5A2AD8A3B39BC3 /* MDCButton.m in Sources */, + 9535D614A8A0FD3B56CA7E8BACA11451 /* MDCCard.m in Sources */, + 3B214DA1F9E0A2F6BAA4DF248BE271AE /* MDCCardCollectionCell.m in Sources */, + 69200AC3BAAF8F7878D72847AB8DDB8C /* MDCCornerTreatment.m in Sources */, + D9F13F6D976BDA43867D98A5F1CF6BF1 /* MDCCornerTreatment+CornerTypeInitalizer.m in Sources */, + 6A7604C990C5DFF551C625E6EBC1E93A /* MDCCurvedCornerTreatment.m in Sources */, + 39874777CA1F53BB8BF51F9C0DF90173 /* MDCCurvedRectShapeGenerator.m in Sources */, + A76F7B04181F8D8A0F931424F54C4C07 /* MDCCutCornerTreatment.m in Sources */, + 75A51A634B4414C4BC6CE7DA089CAEC8 /* MDCEdgeTreatment.m in Sources */, + 4F63D1C9A3C378378BEBA8171E0EB4B3 /* MDCFlatButton.m in Sources */, + ABFFB41EC17E1CC5B8A7B0266718DAE1 /* MDCFloatingButton.m in Sources */, + 0D9E44B0B2AC6D75ACB5D1D1B8EB6090 /* MDCFloatingButton+Animation.m in Sources */, + 74129ED9A3E24DFAFFC726872335B23E /* MDCFloatingButtonModeAnimator.m in Sources */, + 4CD67A5130FE795F570FA71E4A0F0817 /* MDCFontScaler.m in Sources */, + D0CC26A37DD0CD758EA556DD00D6503F /* MDCFontTraits.m in Sources */, + CBC6305777E98D5C59FC5ACAB00A17D1 /* MDCIcons.m in Sources */, + 34F8E92AE1B47864EB2828243BEA8378 /* MDCInkGestureRecognizer.m in Sources */, + 437EF01383D730AE242D947B9B58AA18 /* MDCInkLayer.m in Sources */, + 1BA653A4F6BE2F4443363A09C3E99628 /* MDCInkTouchController.m in Sources */, + 61FC00C7FCA912D5B60ACFCD7B93CB32 /* MDCInkView.m in Sources */, + 0F1B88139EBAE43C4D04C4D238AE8F51 /* MDCLegacyInkLayer.m in Sources */, + 2155754362430305C54CBCC65CC1A1A1 /* MDCPathGenerator.m in Sources */, + 3064376167F8EFF6F63AEC652D55E9A0 /* MDCPillShapeGenerator.m in Sources */, + 3F016AAA33E691084D294F7A12AC56AD /* MDCRaisedButton.m in Sources */, + 138A6746D98577BC9AEFBA7E0DBCE9A7 /* MDCRectangleShapeGenerator.m in Sources */, + B4B385550E3C5121D46697EB10E83C02 /* MDCRippleLayer.m in Sources */, + 8E9D0B3DD987E4503F62401A7F412FCA /* MDCRippleTouchController.m in Sources */, + 7873F0E585A1DD314E9A681B5E4C050C /* MDCRippleView.m in Sources */, + 9165FD4860C36068CF81600ED9992F02 /* MDCRoundedCornerTreatment.m in Sources */, + 1C3510C6893F019ABE94367A4EE9D07E /* MDCShadow.m in Sources */, + 24B503B9E52E65303577FE6910BF3D15 /* MDCShadowLayer.m in Sources */, + 482827C9A937BDC9B86EC162974D8F3E /* MDCShadowsCollection.m in Sources */, + DAF43358D7A8ED2F222994AA7F1EDED6 /* MDCShapedShadowLayer.m in Sources */, + E1149D3565B395487CF0EB4487448957 /* MDCShapedView.m in Sources */, + EED90CBEBBCB86F3FE4215070F7737E0 /* MDCShapeMediator.m in Sources */, + 960892840B75C67C133BAA7E39C35D55 /* MDCSlantedRectShapeGenerator.m in Sources */, + 3B8C4CEB676840C42DE451C65C4D79E9 /* MDCStatefulRippleView.m in Sources */, + 41C21FEBC88B3A3F97B18DBDF929C751 /* MDCTriangleEdgeTreatment.m in Sources */, + 17CEA056B7594689546EAB74E595D060 /* MDCTypography.m in Sources */, + 77F503B82A75DA59A61413C1D5371422 /* MDCTypographyUtilities.m in Sources */, + 6DADCA7F70AA8718696BB357CD2D6140 /* UIApplication+MDCAppExtensions.m in Sources */, + 48A55C7FBBE4B4F30AEA67E3A50006EE /* UICollectionViewController+MDCCardReordering.m in Sources */, + 11E325AD2BE51DA7C48EB54B7737B4BF /* UIColor+MaterialBlending.m in Sources */, + AB0E5CD170B9656EA06269823DD89CB1 /* UIColor+MaterialDynamic.m in Sources */, + 490142072F8EE916442517A5884282E0 /* UIColor+MaterialElevation.m in Sources */, + 0B6124DD33313BE9E4A71126F8F3DB75 /* UIFont+MaterialScalable.m in Sources */, + 822EE6C99BED7B9C6A92C87D7717EECC /* UIFont+MaterialSimpleEquality.m in Sources */, + E5F04CB5F20F4E13DCFBC88AB7506E55 /* UIFont+MaterialTypography.m in Sources */, + 12017489E0E497EC8A12E9418643AA6D /* UIFont+MaterialTypographyPrivate.m in Sources */, + D0C349CE9DB7214843BCDE9B8A4E4AF9 /* UIFontDescriptor+MaterialTypography.m in Sources */, + F363D238E6734C6503D7E8AACFB6F01A /* UIView+MaterialElevationResponding.m in Sources */, + 71EEB4CB0D6FF002E43D958FAE52AF63 /* UIView+MDCTimingFunction.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5FE9836A67EA3E51CA889A1AB95BC874 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A664924D6CCE2922A3F81EC932F4D476 /* AFError.swift in Sources */, + FEDBAD32E2EDA85AD6E362B82892A74A /* Alamofire.swift in Sources */, + 1773084DECF68CADD45567FBEC56036D /* Alamofire-dummy.m in Sources */, + A3153333FC136836B0028E6AB2A56BEE /* AlamofireExtended.swift in Sources */, + F36D96A4346C90A2D11CB3B6A2ECF4CF /* AuthenticationInterceptor.swift in Sources */, + 13E62623092B680C6A5C349D48B8A4FD /* CachedResponseHandler.swift in Sources */, + 97584BC08D2B494417BDEE268CFF38C9 /* Combine.swift in Sources */, + DCD0C33A2B50811D53CF68F021284B47 /* DispatchQueue+Alamofire.swift in Sources */, + 9CFDA7C92E0EEA31F709663B0E727ABA /* EventMonitor.swift in Sources */, + 52BE6F747C26DF2A24532458E55DC10F /* HTTPHeaders.swift in Sources */, + 02621C4B82398D0657F474E21493A3A2 /* HTTPMethod.swift in Sources */, + C16A047C4E8D856309A486182A490993 /* MultipartFormData.swift in Sources */, + 30A331CD9286145E92DB11D671664C63 /* MultipartUpload.swift in Sources */, + B6473B8E8353317F75D6800D4F7054CB /* NetworkReachabilityManager.swift in Sources */, + 471611F482CDC15BF464E3BA9CB83968 /* Notifications.swift in Sources */, + 2550F0D474DE846FEC5C76CBE85F927E /* OperationQueue+Alamofire.swift in Sources */, + 8B9CDBE3FFD712120CD66DD8B06C44E4 /* ParameterEncoder.swift in Sources */, + 4634BA717BFCE522E5B42304C6A78B5D /* ParameterEncoding.swift in Sources */, + 02DB462B121245593CE653B9B377F970 /* Protected.swift in Sources */, + DD58A00EACBEE274C381B491519C6B8C /* RedirectHandler.swift in Sources */, + E0C65E16219718869CD2AFCA2C5465CB /* Request.swift in Sources */, + F63BE0585331CAA3482EF736803F8243 /* RequestInterceptor.swift in Sources */, + 1D17B83410DC98911D539F2BD5254C05 /* RequestTaskMap.swift in Sources */, + C7F66519CE6148F21D7DB11423F1D34D /* Response.swift in Sources */, + 941822CDF68EB8F4D49F150457A82616 /* ResponseSerialization.swift in Sources */, + A4F1202CE5BBE79F3BBCAE3D2B16BC03 /* Result+Alamofire.swift in Sources */, + 512FAFBD71830F126224C033B6C45F4E /* RetryPolicy.swift in Sources */, + 8F9E1EEF2FE52E3231A769722D5C4148 /* ServerTrustEvaluation.swift in Sources */, + EC11B17DA78F7EEBEBC3EFAF68C6DF9F /* Session.swift in Sources */, + 688337B18659C4BF722F87AFC4FEEF81 /* SessionDelegate.swift in Sources */, + E1769C267E82B0C24FE0FFBF949F0A6E /* StringEncoding+Alamofire.swift in Sources */, + 5E594FA3290D3D70F500572D0AC100DB /* URLConvertible+URLRequestConvertible.swift in Sources */, + F5D2A31C7EB1DE010771140B6E7ABAD8 /* URLEncodedFormEncoder.swift in Sources */, + B89D1C69742F61878115334A1D2DFFE7 /* URLRequest+Alamofire.swift in Sources */, + E857ADCAD7B647883D5B2AEC3F16D1D5 /* URLSessionConfiguration+Alamofire.swift in Sources */, + D15FEA31AA9625BBF041FB91E48A9995 /* Validation.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 621071A550AC52510305FD30232CA78F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E5AF92BC5AD820D750CDDE0DDC887D2 /* Pods-ios-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 66C6B183F08D1564AE5191E32440E60D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A557FFD62CE14E4D6B85C238D6D367 /* BaseChatItemPresenter.swift in Sources */, + DA24453A3C4C0EFDC25A253712BCEFB6 /* BaseChatViewController.swift in Sources */, + 31B827F3FA1AB67F3139AA92AA3C9C88 /* BaseChatViewController+CellPanGestureHandler.swift in Sources */, + 415CF97552CF15ABEB0FBF16BB7CBC0C /* BaseChatViewController+Changes.swift in Sources */, + F0501EA15D82306F07F094CA2558130F /* BaseChatViewController+Presenters.swift in Sources */, + A94F1967A266EA1EB3E4910073BE41F4 /* BaseChatViewController+Scrolling.swift in Sources */, + 98E4EFD77D2015E422923ED1EAD4AF7F /* BaseChatViewControllerView.swift in Sources */, + 8AC8E7FC1C4A48B36484927306556F31 /* CellPanGestureHandler.swift in Sources */, + 267ED94E779B638FF06A3796A5DBA7E5 /* ChatCollectionViewLayout.swift in Sources */, + 847D96F05D6EF0D2AC3E54036A72843F /* ChatDataSourceProtocol.swift in Sources */, + C4DA75B4F7DD07F64D5B2FB7B83421D5 /* ChatItemCompanion.swift in Sources */, + C5248490BD1AB45374D2AFA6BC3C5C32 /* ChatItemCompanionCollection.swift in Sources */, + 252655EF3425EC8EF3154477B9B4C3F1 /* ChatItemPresenterFactory.swift in Sources */, + 3C1C2FE0D3AD4E5B2EEDF80C83AD822F /* ChatItemProtocolDefinitions.swift in Sources */, + 7F1DEF5C995E93F3EEDEE6562BD3FBF4 /* ChatLayoutConfiguration.swift in Sources */, + 7D3E0B70A1031AF6F52BB2F2704C5D7A /* Chatto-dummy.m in Sources */, + D6245FA6A979B1028595B60789B07701 /* CollectionChanges.swift in Sources */, + 8C802F4EE8500BDBC0D000E200B162F2 /* DummyChatItemPresenter.swift in Sources */, + 74C2C803859AE1F9EDBB8BC6D027ACE9 /* InputPositionControlling.swift in Sources */, + ED10CCA16008FE6450460EADE24C758B /* KeyboardTracker.swift in Sources */, + 198CE4B8F3B6704CB4AD916821DC22A1 /* ReplyFeedbackGenerator.swift in Sources */, + 49A404FC51F1F7CDCDF7FD0112F00A5F /* SerialTaskQueue.swift in Sources */, + EDC23A30E84A45747FC576F67901CCE3 /* Utils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 75C42EE808FFAC35041568CFE92847AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 25ABCCB506CD51393434BE44F446AD5F /* MDFInternationalization-dummy.m in Sources */, + BF7F9976FA80C821BC13E58E30E94B5B /* MDFRTL.m in Sources */, + 8C050C985EF60CCC3B9E25050BE20F3E /* NSLocale+MaterialRTL.m in Sources */, + 124EEAE22B8077371D55FDB1AC4F4D30 /* NSString+MaterialBidi.m in Sources */, + 03219B99BEEC9AB447188784DFDF4E19 /* UIImage+MaterialRTL.m in Sources */, + 984167BC563C943B94B43A01DECBEAB0 /* UIView+MaterialRTL.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 874513F819B24A84534CB3731FFFCB2E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 496713FA3DBA4082C7948ECF7349A992 /* Pods-iosTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 05A590F9EC9D0B3EA47F7735F7CA587A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MaterialComponents; + target = B4723B5744013DF08BE3A5FEAA286944 /* MaterialComponents */; + targetProxy = 86D033B1EAA6DB4A9250A038CF62AF07 /* PBXContainerItemProxy */; + }; + 12A0FCF3F802AE49C0D0CB64AA7E43A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MDFTextAccessibility; + target = A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */; + targetProxy = D397BCB4986AFF763539DD0013C5417D /* PBXContainerItemProxy */; + }; + 14095B8F88A69E2127AC168198A47484 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Alamofire; + target = EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */; + targetProxy = 6ED33D3BA173DAE7C34C14C6974F4E02 /* PBXContainerItemProxy */; + }; + 6C1B5C5B5ECFC4AE40FE227FD0468177 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MaterialComponents-MaterialIcons_ic_check_circle"; + target = 7F4022CD4249985E761F745058CAB372 /* MaterialComponents-MaterialIcons_ic_check_circle */; + targetProxy = 09ADAB9C1EBB34AFF27286BC540ECBF9 /* PBXContainerItemProxy */; + }; + 824DAEEDCF96F9DE264359D187EE3D7C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Chatto; + target = 2508BC3B908322C9E85E61AE743C9842 /* Chatto */; + targetProxy = 37B8EC58BCB778F91D6C37656EDA3111 /* PBXContainerItemProxy */; + }; + 8907A64A1B702FF7E8F6F71C8D6AE9A2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MDFInternationalization; + target = 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */; + targetProxy = 7678DE8865A337D774511B701E4FB23B /* PBXContainerItemProxy */; + }; + 8E26205BE12108119E077CD520CE9795 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "lottie-ios"; + target = 0B967D7F8561D42493EE289EC8D450D1 /* lottie-ios */; + targetProxy = 547A50F181286054E7D1AD178F2C9D36 /* PBXContainerItemProxy */; + }; + 95A503489BD245D0353433835A3DD6A8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-ios"; + target = CB5AA7A4FA94F4765AE579D6F4491956 /* Pods-ios */; + targetProxy = 1E07159C86DCD357DDD6438B3A657BB7 /* PBXContainerItemProxy */; + }; + BD0671634A3423E811F1923A94A58215 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MDFTextAccessibility; + target = A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */; + targetProxy = CCFB263D6803CCB26550A6888F18657A /* PBXContainerItemProxy */; + }; + DA0287AB868FBE7422C398193D171599 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MDFInternationalization; + target = 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */; + targetProxy = 8AAE22C70F1836AC9C87C67A00D249D2 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0134A33392E024A08A8B77B12A149AAC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MaterialComponents"; + IBSC_MODULE = MaterialComponents; + INFOPLIST_FILE = "Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + PRODUCT_NAME = MaterialIcons_ic_check_circle; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 082649ABF65D30C0B8732BDDADE9DC4C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 43E52D204C785271C19FE614C61534A4 /* MDFInternationalization.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization.modulemap"; + PRODUCT_MODULE_NAME = MDFInternationalization; + PRODUCT_NAME = MDFInternationalization; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 0D7C086888D375AA26A6F1AC139D726E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MaterialComponents/MaterialComponents-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MaterialComponents/MaterialComponents-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MaterialComponents/MaterialComponents.modulemap"; + PRODUCT_MODULE_NAME = MaterialComponents; + PRODUCT_NAME = MaterialComponents; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 2A7DA26AF0A7C5162BE1469DC8E89D03 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3CDCB1C8D529F8DE664D2E4C96BD3A7D /* Alamofire.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; + PRODUCT_MODULE_NAME = Alamofire; + PRODUCT_NAME = Alamofire; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.3; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 330BE68AFFEED76FA4D133838B94A1C1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1CBA47FDA3E910C7EDFE324EFA897551 /* lottie-ios.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/lottie-ios/lottie-ios-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/lottie-ios/lottie-ios-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/lottie-ios/lottie-ios.modulemap"; + PRODUCT_MODULE_NAME = Lottie; + PRODUCT_NAME = Lottie; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.4; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 4EA1C47EA1053331DBCA33FF4B9D3A0D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BE5AE749191583AB288E955A216FBDEE /* MDFInternationalization.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization.modulemap"; + PRODUCT_MODULE_NAME = MDFInternationalization; + PRODUCT_NAME = MDFInternationalization; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5CA0DF9E981B8FEA7F0E9A5D37503B13 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CEE878F352FF70522E2B4041CB792B24 /* MDFTextAccessibility.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-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/MDFTextAccessibility/MDFTextAccessibility.modulemap"; + PRODUCT_MODULE_NAME = MDFTextAccessibility; + PRODUCT_NAME = MDFTextAccessibility; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6847C40F99A8980CD7758FB325464959 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C5494CF8D45C3C7880D1C7684088125B /* Chatto.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Chatto/Chatto-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Chatto/Chatto-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Chatto/Chatto.modulemap"; + PRODUCT_MODULE_NAME = Chatto; + PRODUCT_NAME = Chatto; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.3; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 79CAC264ADFE5B001A5B7C3B7E3C344F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C43092A50861A2F799989439A7DCBCAF /* Alamofire.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; + PRODUCT_MODULE_NAME = Alamofire; + PRODUCT_NAME = Alamofire; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.3; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 8DE5143C03248BB6CD542DE3963D6F3A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 9476DECF90EB61954A99CD8B31CE09BE /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0C07D8193799950468B6CF3885E1193B /* Pods-ios.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-ios/Pods-ios-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-ios/Pods-ios.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 96964714EF72EDBB552C46AEF5FE7534 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B1F119B189824C232697C892AF030E98 /* Pods-ios.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-ios/Pods-ios-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-ios/Pods-ios.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 975BE5E092809620E2F8FB75D1F36C85 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 615BC7DD9F6466F845804084D0B12D66 /* lottie-ios.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/lottie-ios/lottie-ios-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/lottie-ios/lottie-ios-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/lottie-ios/lottie-ios.modulemap"; + PRODUCT_MODULE_NAME = Lottie; + PRODUCT_NAME = Lottie; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.4; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9E406C6AAF85E580207CD97B0044DEAB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + A2E59AE2DABA2794E4EC62B9981FF842 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0EA7FAE72F27B4A66C2CAEEC25C31634 /* MDFTextAccessibility.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-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/MDFTextAccessibility/MDFTextAccessibility.modulemap"; + PRODUCT_MODULE_NAME = MDFTextAccessibility; + PRODUCT_NAME = MDFTextAccessibility; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + B53E854E91BA2A628572DC2785525912 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MaterialComponents/MaterialComponents-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MaterialComponents/MaterialComponents-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MaterialComponents/MaterialComponents.modulemap"; + PRODUCT_MODULE_NAME = MaterialComponents; + PRODUCT_NAME = MaterialComponents; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + B6F84F36E13ABE8EBBD6B23191196B4F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8186408B3030AB74BE1331A431FB83D4 /* Chatto.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Chatto/Chatto-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Chatto/Chatto-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Chatto/Chatto.modulemap"; + PRODUCT_MODULE_NAME = Chatto; + PRODUCT_NAME = Chatto; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.3; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CBE8964E5C394F9774D2231A5C783AB9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 72E3FDB351A03387E5608B6C84700D31 /* Pods-iosTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + EAFEC7653599177751A2AF5990C7A92B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MaterialComponents"; + IBSC_MODULE = MaterialComponents; + INFOPLIST_FILE = "Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + PRODUCT_NAME = MaterialIcons_ic_check_circle; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + EBDD9D76E91A50F71A2A604AD4F544B4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F9C9C35B39EFD7AD82253D0A2AD91B56 /* Pods-iosTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0358E8E05175A7E59A2AEECD12A1801E /* Build configuration list for PBXNativeTarget "lottie-ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 975BE5E092809620E2F8FB75D1F36C85 /* Debug */, + 330BE68AFFEED76FA4D133838B94A1C1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 04278286CEB96ED1B5A7A932E21D012F /* Build configuration list for PBXNativeTarget "Pods-iosTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CBE8964E5C394F9774D2231A5C783AB9 /* Debug */, + EBDD9D76E91A50F71A2A604AD4F544B4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 29235CC975477CF0AE111D8533A857C9 /* Build configuration list for PBXNativeTarget "MDFInternationalization" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4EA1C47EA1053331DBCA33FF4B9D3A0D /* Debug */, + 082649ABF65D30C0B8732BDDADE9DC4C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DE5143C03248BB6CD542DE3963D6F3A /* Debug */, + 9E406C6AAF85E580207CD97B0044DEAB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4D07C4D46B8088CB6927DB0DC7D38E87 /* Build configuration list for PBXNativeTarget "MaterialComponents" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B53E854E91BA2A628572DC2785525912 /* Debug */, + 0D7C086888D375AA26A6F1AC139D726E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 81EE041A2E698878FED9512843DEEE02 /* Build configuration list for PBXNativeTarget "MaterialComponents-MaterialIcons_ic_check_circle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EAFEC7653599177751A2AF5990C7A92B /* Debug */, + 0134A33392E024A08A8B77B12A149AAC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8FE49F8617D5D677A3808ABE3B492E82 /* Build configuration list for PBXNativeTarget "Chatto" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B6F84F36E13ABE8EBBD6B23191196B4F /* Debug */, + 6847C40F99A8980CD7758FB325464959 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 994FA3DA95C9295DD26DDD2C48D33FDF /* Build configuration list for PBXNativeTarget "Pods-ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9476DECF90EB61954A99CD8B31CE09BE /* Debug */, + 96964714EF72EDBB552C46AEF5FE7534 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9C98220D3187BF01A20E296DC128BED4 /* Build configuration list for PBXNativeTarget "Alamofire" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 79CAC264ADFE5B001A5B7C3B7E3C344F /* Debug */, + 2A7DA26AF0A7C5162BE1469DC8E89D03 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E9D2A6F4024608ED9B15BD3B46315019 /* Build configuration list for PBXNativeTarget "MDFTextAccessibility" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5CA0DF9E981B8FEA7F0E9A5D37503B13 /* Debug */, + A2E59AE2DABA2794E4EC62B9981FF842 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme new file mode 100644 index 00000000..3658e118 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme new file mode 100644 index 00000000..adbd96b8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme new file mode 100644 index 00000000..7c7a9df3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme new file mode 100644 index 00000000..d8d7eb75 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme new file mode 100644 index 00000000..97a4eede --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme new file mode 100644 index 00000000..fc44e3c9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme new file mode 100644 index 00000000..db1b9261 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme new file mode 100644 index 00000000..7d3b5359 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme new file mode 100644 index 00000000..67c96f89 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..8b2e7909 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,56 @@ + + + + + SchemeUserState + + Alamofire.xcscheme + + isShown + + + Chatto.xcscheme + + isShown + + + MDFInternationalization.xcscheme + + isShown + + + MDFTextAccessibility.xcscheme + + isShown + + + MaterialComponents-MaterialIcons_ic_check_circle.xcscheme + + isShown + + + MaterialComponents.xcscheme + + isShown + + + Pods-ios.xcscheme + + isShown + + + Pods-iosTests.xcscheme + + isShown + + + lottie-ios.xcscheme + + isShown + + + + SuppressBuildableAutocreation + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist new file mode 100644 index 00000000..f6f37a1a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 5.4.3 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m new file mode 100644 index 00000000..a6c45942 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Alamofire : NSObject +@end +@implementation PodsDummy_Alamofire +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h new file mode 100644 index 00000000..00014e3c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double AlamofireVersionNumber; +FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig new file mode 100644 index 00000000..7d169c44 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap new file mode 100644 index 00000000..d1f125fa --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap @@ -0,0 +1,6 @@ +framework module Alamofire { + umbrella header "Alamofire-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig new file mode 100644 index 00000000..7d169c44 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist new file mode 100644 index 00000000..c26f36f0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 4.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m new file mode 100644 index 00000000..94fb320b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Chatto : NSObject +@end +@implementation PodsDummy_Chatto +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h new file mode 100644 index 00000000..ef0320a4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double ChattoVersionNumber; +FOUNDATION_EXPORT const unsigned char ChattoVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig new file mode 100644 index 00000000..2464a471 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Chatto +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Chatto +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap new file mode 100644 index 00000000..cbc152f5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap @@ -0,0 +1,6 @@ +framework module Chatto { + umbrella header "Chatto-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig new file mode 100644 index 00000000..2464a471 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Chatto +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Chatto +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist new file mode 100644 index 00000000..45226757 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m new file mode 100644 index 00000000..15ac2499 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MDFInternationalization : NSObject +@end +@implementation PodsDummy_MDFInternationalization +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h new file mode 100644 index 00000000..09365d06 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h @@ -0,0 +1,22 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "MDFInternationalization.h" +#import "MDFRTL.h" +#import "NSLocale+MaterialRTL.h" +#import "NSString+MaterialBidi.h" +#import "UIImage+MaterialRTL.h" +#import "UIView+MaterialRTL.h" + +FOUNDATION_EXPORT double MDFInternationalizationVersionNumber; +FOUNDATION_EXPORT const unsigned char MDFInternationalizationVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig new file mode 100644 index 00000000..4e047ef0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFInternationalization +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap new file mode 100644 index 00000000..0c635178 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap @@ -0,0 +1,6 @@ +framework module MDFInternationalization { + umbrella header "MDFInternationalization-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig new file mode 100644 index 00000000..4e047ef0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFInternationalization +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist new file mode 100644 index 00000000..bdac57c6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.0.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m new file mode 100644 index 00000000..1c4041ac --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MDFTextAccessibility : NSObject +@end +@implementation PodsDummy_MDFTextAccessibility +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h new file mode 100644 index 00000000..dc28d78e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h @@ -0,0 +1,17 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "MDFTextAccessibility.h" + +FOUNDATION_EXPORT double MDFTextAccessibilityVersionNumber; +FOUNDATION_EXPORT const unsigned char MDFTextAccessibilityVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig new file mode 100644 index 00000000..9b8318df --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFTextAccessibility +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap new file mode 100644 index 00000000..db04c5fb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap @@ -0,0 +1,6 @@ +framework module MDFTextAccessibility { + umbrella header "MDFTextAccessibility-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig new file mode 100644 index 00000000..9b8318df --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFTextAccessibility +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist new file mode 100644 index 00000000..08011709 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 124.2.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m new file mode 100644 index 00000000..6ba91fdc --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MaterialComponents : NSObject +@end +@implementation PodsDummy_MaterialComponents +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h new file mode 100644 index 00000000..16b8cd3a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h @@ -0,0 +1,92 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "CAMediaTimingFunction+MDCAnimationTiming.h" +#import "MaterialAnimationTiming.h" +#import "UIView+MDCTimingFunction.h" +#import "MaterialAvailability.h" +#import "MDCAvailability.h" +#import "MaterialButtons.h" +#import "MDCButton.h" +#import "MDCFlatButton.h" +#import "MDCFloatingButton+Animation.h" +#import "MDCFloatingButton.h" +#import "MDCRaisedButton.h" +#import "MaterialCards.h" +#import "MDCCard.h" +#import "MDCCardCollectionCell.h" +#import "UICollectionViewController+MDCCardReordering.h" +#import "MaterialElevation.h" +#import "MDCElevatable.h" +#import "MDCElevationOverriding.h" +#import "UIColor+MaterialElevation.h" +#import "UIView+MaterialElevationResponding.h" +#import "MaterialInk.h" +#import "MDCInkGestureRecognizer.h" +#import "MDCInkTouchController.h" +#import "MDCInkTouchControllerDelegate.h" +#import "MDCInkView.h" +#import "MDCInkViewDelegate.h" +#import "MaterialRipple.h" +#import "MDCRippleTouchController.h" +#import "MDCRippleTouchControllerDelegate.h" +#import "MDCRippleView.h" +#import "MDCRippleViewDelegate.h" +#import "MDCStatefulRippleView.h" +#import "MaterialShadow.h" +#import "MDCShadow.h" +#import "MDCShadowsCollection.h" +#import "MaterialShadowElevations.h" +#import "MDCShadowElevations.h" +#import "MaterialShadowLayer.h" +#import "MDCShadowLayer.h" +#import "MaterialShapeLibrary.h" +#import "MDCCornerTreatment+CornerTypeInitalizer.h" +#import "MDCCurvedCornerTreatment.h" +#import "MDCCurvedRectShapeGenerator.h" +#import "MDCCutCornerTreatment.h" +#import "MDCPillShapeGenerator.h" +#import "MDCRoundedCornerTreatment.h" +#import "MDCSlantedRectShapeGenerator.h" +#import "MDCTriangleEdgeTreatment.h" +#import "MaterialShapes.h" +#import "MDCCornerTreatment.h" +#import "MDCEdgeTreatment.h" +#import "MDCPathGenerator.h" +#import "MDCRectangleShapeGenerator.h" +#import "MDCShapedShadowLayer.h" +#import "MDCShapedView.h" +#import "MDCShapeGenerating.h" +#import "MDCShapeMediator.h" +#import "MaterialTypography.h" +#import "MDCFontScaler.h" +#import "MDCFontTextStyle.h" +#import "MDCTypography.h" +#import "UIFont+MaterialScalable.h" +#import "UIFont+MaterialSimpleEquality.h" +#import "UIFont+MaterialTypography.h" +#import "UIFontDescriptor+MaterialTypography.h" +#import "MaterialApplication.h" +#import "UIApplication+MDCAppExtensions.h" +#import "MaterialColor.h" +#import "UIColor+MaterialBlending.h" +#import "UIColor+MaterialDynamic.h" +#import "MaterialIcons.h" +#import "MDCIcons+BundleLoader.h" +#import "MDCIcons.h" +#import "MaterialIcons+ic_check_circle.h" +#import "MaterialMath.h" +#import "MDCMath.h" + +FOUNDATION_EXPORT double MaterialComponentsVersionNumber; +FOUNDATION_EXPORT const unsigned char MaterialComponentsVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig new file mode 100644 index 00000000..48c72dea --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "MDFInternationalization" -framework "MDFTextAccessibility" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MaterialComponents +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap new file mode 100644 index 00000000..052d8ae7 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap @@ -0,0 +1,6 @@ +framework module MaterialComponents { + umbrella header "MaterialComponents-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig new file mode 100644 index 00000000..48c72dea --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "MDFInternationalization" -framework "MDFTextAccessibility" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MaterialComponents +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist new file mode 100644 index 00000000..e4e4dafb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 124.2.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown new file mode 100644 index 00000000..7537c191 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown @@ -0,0 +1,670 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Alamofire + +Copyright (c) 2014-2021 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. + + +## Chatto + +The MIT License (MIT) + +Copyright (c) 2015 Badoo Development + +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. + + + +## MDFInternationalization + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +## MDFTextAccessibility + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +## MaterialComponents + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Generated by CocoaPods - https://cocoapods.org diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist new file mode 100644 index 00000000..316f230d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist @@ -0,0 +1,726 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2014-2021 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. + + License + MIT + Title + Alamofire + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Badoo Development + +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. + + + License + MIT + Title + Chatto + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache License, Version 2.0 + Title + MDFInternationalization + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache License, Version 2.0 + Title + MDFTextAccessibility + Type + PSGroupSpecifier + + + FooterText + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache 2.0 + Title + MaterialComponents + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m new file mode 100644 index 00000000..f8f6a16c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_ios : NSObject +@end +@implementation PodsDummy_Pods_ios +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 00000000..9ad26baa --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,6 @@ +${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh +${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework +${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework +${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework +${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework +${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 00000000..46772e9f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1,5 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Chatto.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFInternationalization.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFTextAccessibility.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist new file mode 100644 index 00000000..9ad26baa --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,6 @@ +${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh +${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework +${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework +${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework +${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework +${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist new file mode 100644 index 00000000..46772e9f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist @@ -0,0 +1,5 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Chatto.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFInternationalization.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFTextAccessibility.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh new file mode 100755 index 00000000..e8ff2d45 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh @@ -0,0 +1,194 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + mkdir -p "${DWARF_DSYM_FOLDER_PATH}" + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework" + install_framework "${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework" + install_framework "${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework" + install_framework "${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework" + install_framework "${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework" + install_framework "${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework" + install_framework "${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h new file mode 100644 index 00000000..aed76e33 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_iosVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_iosVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig new file mode 100644 index 00000000..5b054a3d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap new file mode 100644 index 00000000..b2424665 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap @@ -0,0 +1,6 @@ +framework module Pods_ios { + umbrella header "Pods-ios-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig new file mode 100644 index 00000000..5b054a3d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown new file mode 100644 index 00000000..368ad77a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown @@ -0,0 +1,208 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## lottie-ios + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Airbnb, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Generated by CocoaPods - https://cocoapods.org diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist new file mode 100644 index 00000000..74f98b65 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist @@ -0,0 +1,240 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Airbnb, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + License + Apache + Title + lottie-ios + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m new file mode 100644 index 00000000..cb3a6f24 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_iosTests : NSObject +@end +@implementation PodsDummy_Pods_iosTests +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 00000000..2a9a41f1 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh +${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 00000000..d7bcf7a8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist new file mode 100644 index 00000000..2a9a41f1 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh +${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist new file mode 100644 index 00000000..d7bcf7a8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh new file mode 100755 index 00000000..eb1b8ff2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh @@ -0,0 +1,186 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + mkdir -p "${DWARF_DSYM_FOLDER_PATH}" + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h new file mode 100644 index 00000000..1733239c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_iosTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_iosTestsVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig new file mode 100644 index 00000000..d0bf2ecb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "CoreGraphics" -framework "Lottie" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" -framework "QuartzCore" -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap new file mode 100644 index 00000000..0948081a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_iosTests { + umbrella header "Pods-iosTests-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig new file mode 100644 index 00000000..d0bf2ecb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig @@ -0,0 +1,15 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "CoreGraphics" -framework "Lottie" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" -framework "QuartzCore" -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist new file mode 100644 index 00000000..3ac477e6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.3.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m new file mode 100644 index 00000000..67e66c90 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_lottie_ios : NSObject +@end +@implementation PodsDummy_lottie_ios +@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h new file mode 100644 index 00000000..287f9db5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double LottieVersionNumber; +FOUNDATION_EXPORT const unsigned char LottieVersionString[]; + diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig new file mode 100644 index 00000000..bc9cd344 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "QuartzCore" -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/lottie-ios +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap new file mode 100644 index 00000000..494806f0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap @@ -0,0 +1,6 @@ +framework module Lottie { + umbrella header "lottie-ios-umbrella.h" + + export * + module * { export * } +} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig new file mode 100644 index 00000000..bc9cd344 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig @@ -0,0 +1,14 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "QuartzCore" -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/lottie-ios +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/LICENSE b/test/fixtures/cocoapods/Pods/lottie-ios/LICENSE new file mode 100644 index 00000000..55bb1787 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Airbnb, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/README.md b/test/fixtures/cocoapods/Pods/lottie-ios/README.md new file mode 100644 index 00000000..21432266 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/README.md @@ -0,0 +1,114 @@ +# Lottie for iOS, macOS (and [Android](https://github.com/airbnb/lottie-android) and [React Native](https://github.com/airbnb/lottie-react-native)) +[![Version](https://img.shields.io/cocoapods/v/lottie-ios.svg?style=flat)](https://cocoapods.org/pods/lottie-ios)[![License](https://img.shields.io/cocoapods/l/lottie-ios.svg?style=flat)](https://cocoapods.org/pods/lottie-ios)[![Platform](https://img.shields.io/cocoapods/p/lottie-ios.svg?style=flat)](https://cocoapods.org/pods/lottie-ios) + +# View documentation, FAQ, help, examples, and more at [airbnb.io/lottie](http://airbnb.io/lottie/) + +Lottie is a mobile library for Android and iOS that natively renders vector based animations and art in realtime with minimal code. + +Lottie loads and renders animations and vectors exported in the bodymovin JSON format. Bodymovin JSON can be created and exported from After Effects with [bodymovin](https://github.com/bodymovin/bodymovin), Sketch with [Lottie Sketch Export](https://github.com/buba447/Lottie-Sketch-Export), and from [Haiku](https://www.haiku.ai). + +For the first time, designers can create **and ship** beautiful animations without an engineer painstakingly recreating it by hand. +Since the animation is backed by JSON they are extremely small in size but can be large in complexity! +Animations can be played, resized, looped, sped up, slowed down, reversed, and even interactively scrubbed. +Lottie can play or loop just a portion of the animation as well, the possibilities are endless! +Animations can even be ***changed at runtime*** in various ways! Change the color, position or any keyframable value! +Lottie also supports native UIViewController Transitions out of the box! + +Here is just a small sampling of the power of Lottie + +![Example1](_Gifs/Examples1.gif) +![Example2](_Gifs/Examples2.gif) + + + +![Example3](_Gifs/Examples3.gif) + +![Abcs](_Gifs/Examples4.gif) + +## Installing Lottie +Lottie supports [CocoaPods](https://cocoapods.org/) and [Carthage](https://github.com/Carthage/Carthage) (Both dynamic and static). Lottie is written in ***Swift 4.2***. +### Github Repo + +You can pull the [Lottie Github Repo](https://github.com/airbnb/lottie-ios/) and include the Lottie.xcodeproj to build a dynamic or static library. + +### CocoaPods +Add the pod to your Podfile: +```ruby +pod 'lottie-ios' +``` + +And then run: +```ruby +pod install +``` +After installing the cocoapod into your project import Lottie with +```swift +import Lottie +``` +### Carthage +Add Lottie to your Cartfile: +``` +github "airbnb/lottie-ios" "master" +``` + +And then run: +``` +carthage update +``` +In your application targets “General” tab under the “Linked Frameworks and Libraries” section, drag and drop lottie-ios.framework from the Carthage/Build/iOS directory that `carthage update` produced. + +### Swift Package Manager +``` swift +// swift-tools-version:5.1 + +import PackageDescription + +let package = Package( + name: "YourTestProject", + platforms: [ + .iOS(.v12), + ], + dependencies: [ + .package(name: "Lottie", url: "https://github.com/airbnb/lottie-ios.git", from: "3.2.1") + ], + targets: [ + .target(name: "YourTestProject", dependencies: ["Lottie"]) + ] +) +``` +And then import wherever needed: ```import Lottie``` + +#### Adding it to an existent iOS Project via Swift Package Manager + +1. Using Xcode 11 go to File > Swift Packages > Add Package Dependency +2. Paste the project URL: https://github.com/airbnb/lottie-ios +3. Click on next and select the project target +4. Don't forget to set `DEAD_CODE_STRIPPING = NO` in your `Build Settings` (https://bugs.swift.org/plugins/servlet/mobile#issue/SR-11564) + +If you have doubts, please, check the following links: + +[How to use](https://developer.apple.com/videos/play/wwdc2019/408/) + +[Creating Swift Packages](https://developer.apple.com/videos/play/wwdc2019/410/) + +After successfully retrieved the package and added it to your project, just import `Lottie` and you can get the full benefits of it. + +----- + +### Objective-C Support + +As of 3.0 Lottie has been completely rewritten in Swift! + +For Objective-C support please use Lottie 2.5.3. Alternatively an Objective-C branch exists and is still active. + +The official objective c branch can be found here: + +[Objective-C Branch](https://github.com/airbnb/lottie-ios/tree/lottie/objectiveC) + +Also check out the documentation regarding it here: + +[iOS Migration](http://airbnb.io/lottie/#/ios-migration) + +### Data collection + +The Lottie SDK does not collect any data. We provide this notice to help you fill out [App Privacy Details](https://developer.apple.com/app-store/app-privacy-details/). diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift new file mode 100644 index 00000000..543202c0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift @@ -0,0 +1,244 @@ +// +// AnimationContainer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/24/19. +// + +import Foundation +import QuartzCore + +// MARK: - AnimationContainer + +/** + The base animation container. + + This layer holds a single composition container and allows for animation of + the currentFrame property. + */ +final class AnimationContainer: CALayer { + + // MARK: Lifecycle + + init( + animation: Animation, + imageProvider: AnimationImageProvider, + textProvider: AnimationTextProvider, + fontProvider: AnimationFontProvider) + { + layerImageProvider = LayerImageProvider(imageProvider: imageProvider, assets: animation.assetLibrary?.imageAssets) + layerTextProvider = LayerTextProvider(textProvider: textProvider) + layerFontProvider = LayerFontProvider(fontProvider: fontProvider) + animationLayers = [] + super.init() + bounds = animation.bounds + let layers = animation.layers.initializeCompositionLayers( + assetLibrary: animation.assetLibrary, + layerImageProvider: layerImageProvider, + textProvider: textProvider, + fontProvider: fontProvider, + frameRate: CGFloat(animation.framerate)) + + var imageLayers = [ImageCompositionLayer]() + var textLayers = [TextCompositionLayer]() + + var mattedLayer: CompositionLayer? = nil + + for layer in layers.reversed() { + layer.bounds = bounds + animationLayers.append(layer) + if let imageLayer = layer as? ImageCompositionLayer { + imageLayers.append(imageLayer) + } + if let textLayer = layer as? TextCompositionLayer { + textLayers.append(textLayer) + } + if let matte = mattedLayer { + /// The previous layer requires this layer to be its matte + matte.matteLayer = layer + mattedLayer = nil + continue + } + if + let matte = layer.matteType, + matte == .add || matte == .invert + { + /// We have a layer that requires a matte. + mattedLayer = layer + } + addSublayer(layer) + } + + layerImageProvider.addImageLayers(imageLayers) + layerImageProvider.reloadImages() + layerTextProvider.addTextLayers(textLayers) + layerTextProvider.reloadTexts() + layerFontProvider.addTextLayers(textLayers) + layerFontProvider.reloadTexts() + setNeedsDisplay() + } + + /// For CAAnimation Use + public override init(layer: Any) { + animationLayers = [] + layerImageProvider = LayerImageProvider(imageProvider: BlankImageProvider(), assets: nil) + layerTextProvider = LayerTextProvider(textProvider: DefaultTextProvider()) + layerFontProvider = LayerFontProvider(fontProvider: DefaultFontProvider()) + super.init(layer: layer) + + guard let animationLayer = layer as? AnimationContainer else { return } + + currentFrame = animationLayer.currentFrame + + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Public + + public var respectAnimationFrameRate: Bool = false + + // MARK: CALayer Animations + + override public class func needsDisplay(forKey key: String) -> Bool { + if key == "currentFrame" { + return true + } + return super.needsDisplay(forKey: key) + } + + override public func action(forKey event: String) -> CAAction? { + if event == "currentFrame" { + let animation = CABasicAnimation(keyPath: event) + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + animation.fromValue = presentation()?.currentFrame + return animation + } + return super.action(forKey: event) + } + + public override func display() { + guard Thread.isMainThread else { return } + var newFrame: CGFloat + if + let animationKeys = animationKeys(), + !animationKeys.isEmpty + { + newFrame = presentation()?.currentFrame ?? currentFrame + } else { + // We ignore the presentation's frame if there's no animation in the layer. + newFrame = currentFrame + } + if respectAnimationFrameRate { + newFrame = floor(newFrame) + } + animationLayers.forEach( { $0.displayWithFrame(frame: newFrame, forceUpdates: false) }) + } + + // MARK: Internal + + /// The animatable Current Frame Property + @NSManaged var currentFrame: CGFloat + + var animationLayers: ContiguousArray + + var imageProvider: AnimationImageProvider { + get { + layerImageProvider.imageProvider + } + set { + layerImageProvider.imageProvider = newValue + } + } + + var renderScale: CGFloat = 1 { + didSet { + animationLayers.forEach({ $0.renderScale = renderScale }) + } + } + + var textProvider: AnimationTextProvider { + get { layerTextProvider.textProvider } + set { layerTextProvider.textProvider = newValue } + } + + var fontProvider: AnimationFontProvider { + get { layerFontProvider.fontProvider } + set { layerFontProvider.fontProvider = newValue } + } + + func reloadImages() { + layerImageProvider.reloadImages() + } + + /// Forces the view to update its drawing. + func forceDisplayUpdate() { + animationLayers.forEach( { $0.displayWithFrame(frame: currentFrame, forceUpdates: true) }) + } + + func logHierarchyKeypaths() { + print("Lottie: Logging Animation Keypaths") + animationLayers.forEach({ $0.logKeypaths(for: nil) }) + } + + func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) { + for layer in animationLayers { + if let foundProperties = layer.nodeProperties(for: keypath) { + for property in foundProperties { + property.setProvider(provider: valueProvider) + } + layer.displayWithFrame(frame: presentation()?.currentFrame ?? currentFrame, forceUpdates: true) + } + } + } + + func getValue(for keypath: AnimationKeypath, atFrame: CGFloat?) -> Any? { + for layer in animationLayers { + if + let foundProperties = layer.nodeProperties(for: keypath), + let first = foundProperties.first + { + return first.valueProvider.value(frame: atFrame ?? currentFrame) + } + } + return nil + } + + func layer(for keypath: AnimationKeypath) -> CALayer? { + for layer in animationLayers { + if let foundLayer = layer.layer(for: keypath) { + return foundLayer + } + } + return nil + } + + func animatorNodes(for keypath: AnimationKeypath) -> [AnimatorNode]? { + var results = [AnimatorNode]() + for layer in animationLayers { + if let nodes = layer.animatorNodes(for: keypath) { + results.append(contentsOf: nodes) + } + } + if results.count == 0 { + return nil + } + return results + } + + // MARK: Fileprivate + + fileprivate let layerImageProvider: LayerImageProvider + fileprivate let layerTextProvider: LayerTextProvider + fileprivate let layerFontProvider: LayerFontProvider +} + +// MARK: - BlankImageProvider + +fileprivate class BlankImageProvider: AnimationImageProvider { + func imageForAsset(asset _: ImageAsset) -> CGImage? { + nil + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift new file mode 100644 index 00000000..954c9ddc --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift @@ -0,0 +1,160 @@ +// +// LayerContainer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/22/19. +// + +import Foundation +import QuartzCore + +// MARK: - CompositionLayer + +/** + The base class for a child layer of CompositionContainer + */ +class CompositionLayer: CALayer, KeypathSearchable { + + // MARK: Lifecycle + + init(layer: LayerModel, size: CGSize) { + transformNode = LayerTransformNode(transform: layer.transform) + if let masks = layer.masks { + maskLayer = MaskContainerLayer(masks: masks) + } else { + maskLayer = nil + } + matteType = layer.matte + inFrame = layer.inFrame.cgFloat + outFrame = layer.outFrame.cgFloat + timeStretch = layer.timeStretch.cgFloat + startFrame = layer.startTime.cgFloat + keypathName = layer.name + childKeypaths = [transformNode.transformProperties] + super.init() + anchorPoint = .zero + actions = [ + "opacity" : NSNull(), + "transform" : NSNull(), + "bounds" : NSNull(), + "anchorPoint" : NSNull(), + "sublayerTransform" : NSNull(), + ] + + contentsLayer.anchorPoint = .zero + contentsLayer.bounds = CGRect(origin: .zero, size: size) + contentsLayer.actions = [ + "opacity" : NSNull(), + "transform" : NSNull(), + "bounds" : NSNull(), + "anchorPoint" : NSNull(), + "sublayerTransform" : NSNull(), + "hidden" : NSNull(), + ] + addSublayer(contentsLayer) + + if let maskLayer = maskLayer { + contentsLayer.mask = maskLayer + } + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? CompositionLayer else { + fatalError("Wrong Layer Class") + } + transformNode = layer.transformNode + matteType = layer.matteType + inFrame = layer.inFrame + outFrame = layer.outFrame + timeStretch = layer.timeStretch + startFrame = layer.startFrame + keypathName = layer.keypathName + childKeypaths = [transformNode.transformProperties] + maskLayer = nil + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + weak var layerDelegate: CompositionLayerDelegate? + + let transformNode: LayerTransformNode + + let contentsLayer = CALayer() + + let maskLayer: MaskContainerLayer? + + let matteType: MatteType? + + let inFrame: CGFloat + let outFrame: CGFloat + let startFrame: CGFloat + let timeStretch: CGFloat + + // MARK: Keypath Searchable + + let keypathName: String + + final var childKeypaths: [KeypathSearchable] + + var renderScale: CGFloat = 1 { + didSet { + updateRenderScale() + } + } + + var matteLayer: CompositionLayer? { + didSet { + if let matte = matteLayer { + if let type = matteType, type == .invert { + mask = InvertedMatteLayer(inputMatte: matte) + } else { + mask = matte + } + } else { + mask = nil + } + } + } + + var keypathProperties: [String: AnyNodeProperty] { + [:] + } + + var keypathLayer: CALayer? { + contentsLayer + } + + final func displayWithFrame(frame: CGFloat, forceUpdates: Bool) { + transformNode.updateTree(frame, forceUpdates: forceUpdates) + let layerVisible = frame.isInRangeOrEqual(inFrame, outFrame) + /// Only update contents if current time is within the layers time bounds. + if layerVisible { + displayContentsWithFrame(frame: frame, forceUpdates: forceUpdates) + maskLayer?.updateWithFrame(frame: frame, forceUpdates: forceUpdates) + } + contentsLayer.transform = transformNode.globalTransform + contentsLayer.opacity = transformNode.opacity + contentsLayer.isHidden = !layerVisible + layerDelegate?.frameUpdated(frame: frame) + } + + func displayContentsWithFrame(frame _: CGFloat, forceUpdates _: Bool) { + /// To be overridden by subclass + } + + func updateRenderScale() { + contentsScale = renderScale + } +} + +// MARK: - CompositionLayerDelegate + +protocol CompositionLayerDelegate: AnyObject { + func frameUpdated(frame: CGFloat) +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift new file mode 100644 index 00000000..b1be9800 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift @@ -0,0 +1,50 @@ +// +// ImageCompositionLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +final class ImageCompositionLayer: CompositionLayer { + + // MARK: Lifecycle + + init(imageLayer: ImageLayerModel, size: CGSize) { + imageReferenceID = imageLayer.referenceID + super.init(layer: imageLayer, size: size) + contentsLayer.masksToBounds = true + contentsLayer.contentsGravity = CALayerContentsGravity.resize + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? ImageCompositionLayer else { + fatalError("init(layer:) Wrong Layer Class") + } + imageReferenceID = layer.imageReferenceID + image = nil + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + let imageReferenceID: String + + var image: CGImage? = nil { + didSet { + if let image = image { + contentsLayer.contents = image + } else { + contentsLayer.contents = nil + } + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift new file mode 100644 index 00000000..464316bd --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift @@ -0,0 +1,190 @@ +// +// MaskContainerLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import Foundation +import QuartzCore + +extension MaskMode { + var usableMode: MaskMode { + switch self { + case .add: + return .add + case .subtract: + return .subtract + case .intersect: + return .intersect + case .lighten: + return .add + case .darken: + return .darken + case .difference: + return .intersect + case .none: + return .none + } + } +} + +// MARK: - MaskContainerLayer + +final class MaskContainerLayer: CALayer { + + // MARK: Lifecycle + + init(masks: [Mask]) { + super.init() + anchorPoint = .zero + var containerLayer = CALayer() + var firstObject: Bool = true + for mask in masks { + let maskLayer = MaskLayer(mask: mask) + maskLayers.append(maskLayer) + if mask.mode.usableMode == .none { + continue + } else if mask.mode.usableMode == .add || firstObject { + firstObject = false + containerLayer.addSublayer(maskLayer) + } else { + containerLayer.mask = maskLayer + let newContainer = CALayer() + newContainer.addSublayer(containerLayer) + containerLayer = newContainer + } + } + addSublayer(containerLayer) + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? MaskContainerLayer else { + fatalError("init(layer:) Wrong Layer Class") + } + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + func updateWithFrame(frame: CGFloat, forceUpdates: Bool) { + maskLayers.forEach({ $0.updateWithFrame(frame: frame, forceUpdates: forceUpdates) }) + } + + // MARK: Fileprivate + + fileprivate var maskLayers: [MaskLayer] = [] +} + +extension CGRect { + static var veryLargeRect: CGRect { + CGRect( + x: -100_000_000, + y: -100_000_000, + width: 200_000_000, + height: 200_000_000) + } +} + +// MARK: - MaskLayer + +fileprivate class MaskLayer: CALayer { + + // MARK: Lifecycle + + init(mask: Mask) { + properties = MaskNodeProperties(mask: mask) + super.init() + addSublayer(maskLayer) + anchorPoint = .zero + maskLayer.fillColor = mask.mode == .add ? CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 0, 0, 1]) : + CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 1, 0, 1]) + maskLayer.fillRule = CAShapeLayerFillRule.evenOdd + actions = [ + "opacity" : NSNull(), + ] + + } + + override init(layer: Any) { + properties = nil + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + let properties: MaskNodeProperties? + + let maskLayer = CAShapeLayer() + + func updateWithFrame(frame: CGFloat, forceUpdates: Bool) { + guard let properties = properties else { return } + if properties.opacity.needsUpdate(frame: frame) || forceUpdates { + properties.opacity.update(frame: frame) + opacity = Float(properties.opacity.value.cgFloatValue) + } + + if properties.shape.needsUpdate(frame: frame) || forceUpdates { + properties.shape.update(frame: frame) + properties.expansion.update(frame: frame) + + let shapePath = properties.shape.value.cgPath() + var path = shapePath + if + properties.mode.usableMode == .subtract && !properties.inverted || + (properties.mode.usableMode == .add && properties.inverted) + { + /// Add a bounds rect to invert the mask + let newPath = CGMutablePath() + newPath.addRect(CGRect.veryLargeRect) + newPath.addPath(shapePath) + path = newPath + } + maskLayer.path = path + } + + } +} + +// MARK: - MaskNodeProperties + +fileprivate class MaskNodeProperties: NodePropertyMap { + + // MARK: Lifecycle + + init(mask: Mask) { + mode = mask.mode + inverted = mask.inverted + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.opacity.keyframes)) + shape = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.shape.keyframes)) + expansion = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.expansion.keyframes)) + propertyMap = [ + "Opacity" : opacity, + "Shape" : shape, + "Expansion" : expansion, + ] + properties = Array(propertyMap.values) + } + + // MARK: Internal + + var propertyMap: [String: AnyNodeProperty] + + var properties: [AnyNodeProperty] + + let mode: MaskMode + let inverted: Bool + + let opacity: NodeProperty + let shape: NodeProperty + let expansion: NodeProperty +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift new file mode 100644 index 00000000..3fdf1637 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift @@ -0,0 +1,28 @@ +// +// NullCompositionLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import Foundation + +final class NullCompositionLayer: CompositionLayer { + + init(layer: LayerModel) { + super.init(layer: layer, size: .zero) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? NullCompositionLayer else { + fatalError("init(layer:) Wrong Layer Class") + } + super.init(layer: layer) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift new file mode 100644 index 00000000..d0722deb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift @@ -0,0 +1,121 @@ +// +// PreCompositionLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import Foundation +import QuartzCore + +final class PreCompositionLayer: CompositionLayer { + + // MARK: Lifecycle + + init( + precomp: PreCompLayerModel, + asset: PrecompAsset, + layerImageProvider: LayerImageProvider, + textProvider: AnimationTextProvider, + fontProvider: AnimationFontProvider, + assetLibrary: AssetLibrary?, + frameRate: CGFloat) + { + animationLayers = [] + if let keyframes = precomp.timeRemapping?.keyframes { + remappingNode = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframes)) + } else { + remappingNode = nil + } + self.frameRate = frameRate + super.init(layer: precomp, size: CGSize(width: precomp.width, height: precomp.height)) + bounds = CGRect(origin: .zero, size: CGSize(width: precomp.width, height: precomp.height)) + contentsLayer.masksToBounds = true + contentsLayer.bounds = bounds + + let layers = asset.layers.initializeCompositionLayers( + assetLibrary: assetLibrary, + layerImageProvider: layerImageProvider, + textProvider: textProvider, + fontProvider: fontProvider, + frameRate: frameRate) + + var imageLayers = [ImageCompositionLayer]() + + var mattedLayer: CompositionLayer? = nil + + for layer in layers.reversed() { + layer.bounds = bounds + animationLayers.append(layer) + if let imageLayer = layer as? ImageCompositionLayer { + imageLayers.append(imageLayer) + } + if let matte = mattedLayer { + /// The previous layer requires this layer to be its matte + matte.matteLayer = layer + mattedLayer = nil + continue + } + if + let matte = layer.matteType, + matte == .add || matte == .invert + { + /// We have a layer that requires a matte. + mattedLayer = layer + } + contentsLayer.addSublayer(layer) + } + + childKeypaths.append(contentsOf: layers) + + layerImageProvider.addImageLayers(imageLayers) + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? PreCompositionLayer else { + fatalError("init(layer:) Wrong Layer Class") + } + frameRate = layer.frameRate + remappingNode = nil + animationLayers = [] + + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + let frameRate: CGFloat + let remappingNode: NodeProperty? + + override var keypathProperties: [String: AnyNodeProperty] { + guard let remappingNode = remappingNode else { + return super.keypathProperties + } + return ["Time Remap" : remappingNode] + } + + override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { + let localFrame: CGFloat + if let remappingNode = remappingNode { + remappingNode.update(frame: frame) + localFrame = remappingNode.value.cgFloatValue * frameRate + } else { + localFrame = (frame - startFrame) / timeStretch + } + animationLayers.forEach( { $0.displayWithFrame(frame: localFrame, forceUpdates: forceUpdates) }) + } + + override func updateRenderScale() { + super.updateRenderScale() + animationLayers.forEach( { $0.renderScale = renderScale } ) + } + + // MARK: Fileprivate + + fileprivate var animationLayers: [CompositionLayer] +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift new file mode 100644 index 00000000..ebcf2a85 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift @@ -0,0 +1,60 @@ +// +// ShapeLayerContainer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/22/19. +// + +import CoreGraphics +import Foundation + +/** + A CompositionLayer responsible for initializing and rendering shapes + */ +final class ShapeCompositionLayer: CompositionLayer { + + // MARK: Lifecycle + + init(shapeLayer: ShapeLayerModel) { + let results = shapeLayer.items.initializeNodeTree() + let renderContainer = ShapeContainerLayer() + self.renderContainer = renderContainer + rootNode = results.rootNode + super.init(layer: shapeLayer, size: .zero) + contentsLayer.addSublayer(renderContainer) + for container in results.renderContainers { + renderContainer.insertRenderLayer(container) + } + rootNode?.updateTree(0, forceUpdates: true) + childKeypaths.append(contentsOf: results.childrenNodes) + } + + override init(layer: Any) { + guard let layer = layer as? ShapeCompositionLayer else { + fatalError("init(layer:) wrong class.") + } + rootNode = nil + renderContainer = nil + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + let rootNode: AnimatorNode? + let renderContainer: ShapeContainerLayer? + + override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { + rootNode?.updateTree(frame, forceUpdates: forceUpdates) + renderContainer?.markRenderUpdates(forFrame: frame) + } + + override func updateRenderScale() { + super.updateRenderScale() + renderContainer?.renderScale = renderScale + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift new file mode 100644 index 00000000..76d6f86c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift @@ -0,0 +1,57 @@ +// +// SolidCompositionLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import Foundation +import QuartzCore + +final class SolidCompositionLayer: CompositionLayer { + + // MARK: Lifecycle + + init(solid: SolidLayerModel) { + let components = solid.colorHex.hexColorComponents() + colorProperty = + NodeProperty(provider: SingleValueProvider(Color( + r: Double(components.red), + g: Double(components.green), + b: Double(components.blue), + a: 1))) + + super.init(layer: solid, size: .zero) + solidShape.path = CGPath(rect: CGRect(x: 0, y: 0, width: solid.width, height: solid.height), transform: nil) + contentsLayer.addSublayer(solidShape) + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? SolidCompositionLayer else { + fatalError("init(layer:) Wrong Layer Class") + } + colorProperty = layer.colorProperty + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + let colorProperty: NodeProperty? + let solidShape = CAShapeLayer() + + override var keypathProperties: [String: AnyNodeProperty] { + guard let colorProperty = colorProperty else { return super.keypathProperties } + return ["Color" : colorProperty] + } + + override func displayContentsWithFrame(frame: CGFloat, forceUpdates _: Bool) { + guard let colorProperty = colorProperty else { return } + colorProperty.update(frame: frame) + solidShape.fillColor = colorProperty.value.cgColorValue + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift new file mode 100644 index 00000000..2ae50bb7 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift @@ -0,0 +1,149 @@ +// +// TextCompositionLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import CoreGraphics +import CoreText +import Foundation +import QuartzCore + +/// Needed for NSMutableParagraphStyle... +#if os(OSX) +import AppKit +#else +import UIKit +#endif + +extension TextJustification { + var textAlignment: NSTextAlignment { + switch self { + case .left: + return .left + case .right: + return .right + case .center: + return .center + } + } + + var caTextAlignement: CATextLayerAlignmentMode { + switch self { + case .left: + return .left + case .right: + return .right + case .center: + return .center + } + } +} + +// MARK: - TextCompositionLayer + +final class TextCompositionLayer: CompositionLayer { + + // MARK: Lifecycle + + init(textLayer: TextLayerModel, textProvider: AnimationTextProvider, fontProvider: AnimationFontProvider) { + var rootNode: TextAnimatorNode? + for animator in textLayer.animators { + rootNode = TextAnimatorNode(parentNode: rootNode, textAnimator: animator) + } + self.rootNode = rootNode + textDocument = KeyframeInterpolator(keyframes: textLayer.text.keyframes) + + self.textProvider = textProvider + self.fontProvider = fontProvider + + super.init(layer: textLayer, size: .zero) + contentsLayer.addSublayer(self.textLayer) + self.textLayer.masksToBounds = false + self.textLayer.isGeometryFlipped = true + + if let rootNode = rootNode { + childKeypaths.append(rootNode) + } + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(layer: Any) { + /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init + guard let layer = layer as? TextCompositionLayer else { + fatalError("init(layer:) Wrong Layer Class") + } + rootNode = nil + textDocument = nil + + textProvider = DefaultTextProvider() + fontProvider = DefaultFontProvider() + + super.init(layer: layer) + } + + // MARK: Internal + + let rootNode: TextAnimatorNode? + let textDocument: KeyframeInterpolator? + + let textLayer = TextLayer() + var textProvider: AnimationTextProvider + var fontProvider: AnimationFontProvider + + override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { + guard let textDocument = textDocument else { return } + + textLayer.contentsScale = renderScale + + let documentUpdate = textDocument.hasUpdate(frame: frame) + let animatorUpdate = rootNode?.updateContents(frame, forceLocalUpdate: forceUpdates) ?? false + guard documentUpdate == true || animatorUpdate == true else { return } + + rootNode?.rebuildOutputs(frame: frame) + + // Get Text Attributes + let text = textDocument.value(frame: frame) as! TextDocument + let strokeColor = rootNode?.textOutputNode.strokeColor ?? text.strokeColorData?.cgColorValue + let strokeWidth = rootNode?.textOutputNode.strokeWidth ?? CGFloat(text.strokeWidth ?? 0) + let tracking = (CGFloat(text.fontSize) * (rootNode?.textOutputNode.tracking ?? CGFloat(text.tracking))) / 1000.0 + let matrix = rootNode?.textOutputNode.xform ?? CATransform3DIdentity + let textString = textProvider.textFor(keypathName: keypathName, sourceText: text.text) + let ctFont = fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize)) + + // Set all of the text layer options + textLayer.text = textString + textLayer.font = ctFont + textLayer.alignment = text.justification.textAlignment + textLayer.lineHeight = CGFloat(text.lineHeight) + textLayer.tracking = tracking + + if let fillColor = rootNode?.textOutputNode.fillColor { + textLayer.fillColor = fillColor + } else if let fillColor = text.fillColorData?.cgColorValue { + textLayer.fillColor = fillColor + } else { + textLayer.fillColor = nil + } + + textLayer.preferredSize = text.textFrameSize?.sizeValue + textLayer.strokeOnTop = text.strokeOverFill ?? false + textLayer.strokeWidth = strokeWidth + textLayer.strokeColor = strokeColor + textLayer.sizeToFit() + + textLayer.opacity = Float(rootNode?.textOutputNode.opacity ?? 1) + textLayer.transform = CATransform3DIdentity + textLayer.position = text.textFramePosition?.pointValue ?? CGPoint.zero + textLayer.transform = matrix + } + + override func updateRenderScale() { + super.updateRenderScale() + textLayer.contentsScale = renderScale + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift new file mode 100644 index 00000000..fb4aa2b3 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift @@ -0,0 +1,90 @@ +// +// CompositionLayersInitializer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import CoreGraphics +import Foundation + +extension Array where Element == LayerModel { + + func initializeCompositionLayers( + assetLibrary: AssetLibrary?, + layerImageProvider: LayerImageProvider, + textProvider: AnimationTextProvider, + fontProvider: AnimationFontProvider, + frameRate: CGFloat) -> [CompositionLayer] + { + var compositionLayers = [CompositionLayer]() + var layerMap = [Int : CompositionLayer]() + + /// Organize the assets into a dictionary of [ID : ImageAsset] + var childLayers = [LayerModel]() + + for layer in self { + if layer.hidden == true { + let genericLayer = NullCompositionLayer(layer: layer) + compositionLayers.append(genericLayer) + layerMap[layer.index] = genericLayer + } else if let shapeLayer = layer as? ShapeLayerModel { + let shapeContainer = ShapeCompositionLayer(shapeLayer: shapeLayer) + compositionLayers.append(shapeContainer) + layerMap[layer.index] = shapeContainer + } else if let solidLayer = layer as? SolidLayerModel { + let solidContainer = SolidCompositionLayer(solid: solidLayer) + compositionLayers.append(solidContainer) + layerMap[layer.index] = solidContainer + } else if + let precompLayer = layer as? PreCompLayerModel, + let assetLibrary = assetLibrary, + let precompAsset = assetLibrary.precompAssets[precompLayer.referenceID] + { + let precompContainer = PreCompositionLayer( + precomp: precompLayer, + asset: precompAsset, + layerImageProvider: layerImageProvider, + textProvider: textProvider, + fontProvider: fontProvider, + assetLibrary: assetLibrary, + frameRate: frameRate) + compositionLayers.append(precompContainer) + layerMap[layer.index] = precompContainer + } else if + let imageLayer = layer as? ImageLayerModel, + let assetLibrary = assetLibrary, + let imageAsset = assetLibrary.imageAssets[imageLayer.referenceID] + { + let imageContainer = ImageCompositionLayer( + imageLayer: imageLayer, + size: CGSize(width: imageAsset.width, height: imageAsset.height)) + compositionLayers.append(imageContainer) + layerMap[layer.index] = imageContainer + } else if let textLayer = layer as? TextLayerModel { + let textContainer = TextCompositionLayer(textLayer: textLayer, textProvider: textProvider, fontProvider: fontProvider) + compositionLayers.append(textContainer) + layerMap[layer.index] = textContainer + } else { + let genericLayer = NullCompositionLayer(layer: layer) + compositionLayers.append(genericLayer) + layerMap[layer.index] = genericLayer + } + if layer.parent != nil { + childLayers.append(layer) + } + } + + /// Now link children with their parents + for layerModel in childLayers { + if let parentID = layerModel.parent { + let childLayer = layerMap[layerModel.index] + let parentLayer = layerMap[parentID] + childLayer?.transformNode.parentNode = parentLayer?.transformNode + } + } + + return compositionLayers + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift new file mode 100644 index 00000000..962cfdca --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift @@ -0,0 +1,61 @@ +// +// InvertedMatteLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/28/19. +// + +import Foundation +import QuartzCore + +/** + A layer that inverses the alpha output of its input layer. + + WARNING: This is experimental and probably not very performant. + */ +final class InvertedMatteLayer: CALayer, CompositionLayerDelegate { + + // MARK: Lifecycle + + init(inputMatte: CompositionLayer) { + self.inputMatte = inputMatte + super.init() + inputMatte.layerDelegate = self + anchorPoint = .zero + bounds = inputMatte.bounds + setNeedsDisplay() + } + + override init(layer: Any) { + guard let layer = layer as? InvertedMatteLayer else { + fatalError("init(layer:) wrong class.") + } + inputMatte = nil + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + let inputMatte: CompositionLayer? + let wrapperLayer = CALayer() + + func frameUpdated(frame _: CGFloat) { + setNeedsDisplay() + displayIfNeeded() + } + + override func draw(in ctx: CGContext) { + guard let inputMatte = inputMatte else { return } + guard let fillColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 1]) + else { return } + ctx.setFillColor(fillColor) + ctx.fill(bounds) + ctx.setBlendMode(.destinationOut) + inputMatte.render(in: ctx) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift new file mode 100644 index 00000000..dda3f372 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift @@ -0,0 +1,41 @@ +// +// LayerFontProvider.swift +// Lottie +// +// Created by Brandon Withrow on 8/5/20. +// Copyright © 2020 YurtvilleProds. All rights reserved. +// + +import Foundation + +/// Connects a LottieFontProvider to a group of text layers +final class LayerFontProvider { + + // MARK: Lifecycle + + init(fontProvider: AnimationFontProvider) { + self.fontProvider = fontProvider + textLayers = [] + reloadTexts() + } + + // MARK: Internal + + fileprivate(set) var textLayers: [TextCompositionLayer] + + var fontProvider: AnimationFontProvider { + didSet { + reloadTexts() + } + } + + func addTextLayers(_ layers: [TextCompositionLayer]) { + textLayers += layers + } + + func reloadTexts() { + textLayers.forEach { + $0.fontProvider = fontProvider + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift new file mode 100644 index 00000000..53bf9297 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift @@ -0,0 +1,53 @@ +// +// LayerImageProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import Foundation + +/// Connects a LottieImageProvider to a group of image layers +final class LayerImageProvider { + + // MARK: Lifecycle + + init(imageProvider: AnimationImageProvider, assets: [String: ImageAsset]?) { + self.imageProvider = imageProvider + imageLayers = [ImageCompositionLayer]() + if let assets = assets { + imageAssets = assets + } else { + imageAssets = [:] + } + reloadImages() + } + + // MARK: Internal + + fileprivate(set) var imageLayers: [ImageCompositionLayer] + let imageAssets: [String: ImageAsset] + + var imageProvider: AnimationImageProvider { + didSet { + reloadImages() + } + } + + func addImageLayers(_ layers: [ImageCompositionLayer]) { + for layer in layers { + if imageAssets[layer.imageReferenceID] != nil { + /// Found a linking asset in our asset library. Add layer + imageLayers.append(layer) + } + } + } + + func reloadImages() { + for imageLayer in imageLayers { + if let asset = imageAssets[imageLayer.imageReferenceID] { + imageLayer.image = imageProvider.imageForAsset(asset: asset) + } + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift new file mode 100644 index 00000000..ce766a51 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift @@ -0,0 +1,40 @@ +// +// LayerTextProvider.swift +// lottie-ios-iOS +// +// Created by Alexandr Goncharov on 07/06/2019. +// + +import Foundation + +/// Connects a LottieTextProvider to a group of text layers +final class LayerTextProvider { + + // MARK: Lifecycle + + init(textProvider: AnimationTextProvider) { + self.textProvider = textProvider + textLayers = [] + reloadTexts() + } + + // MARK: Internal + + fileprivate(set) var textLayers: [TextCompositionLayer] + + var textProvider: AnimationTextProvider { + didSet { + reloadTexts() + } + } + + func addTextLayers(_ layers: [TextCompositionLayer]) { + textLayers += layers + } + + func reloadTexts() { + textLayers.forEach { + $0.textProvider = textProvider + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift new file mode 100644 index 00000000..335b7808 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift @@ -0,0 +1,144 @@ +// +// LayerTransformPropertyMap.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +// MARK: - LayerTransformProperties + +final class LayerTransformProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(transform: Transform) { + + anchor = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.anchorPoint.keyframes)) + scale = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.scale.keyframes)) + rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotation.keyframes)) + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.opacity.keyframes)) + + var propertyMap: [String: AnyNodeProperty] = [ + "Anchor Point" : anchor, + "Scale" : scale, + "Rotation" : rotation, + "Opacity" : opacity, + ] + + if + let positionKeyframesX = transform.positionX?.keyframes, + let positionKeyframesY = transform.positionY?.keyframes + { + let xPosition: NodeProperty = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframesX)) + let yPosition: NodeProperty = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframesY)) + propertyMap["X Position"] = xPosition + propertyMap["Y Position"] = yPosition + positionX = xPosition + positionY = yPosition + position = nil + } else if let positionKeyframes = transform.position?.keyframes { + let position: NodeProperty = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframes)) + propertyMap["Position"] = position + self.position = position + positionX = nil + positionY = nil + } else { + position = nil + positionY = nil + positionX = nil + } + + keypathProperties = propertyMap + properties = Array(propertyMap.values) + } + + // MARK: Internal + + let keypathProperties: [String: AnyNodeProperty] + var keypathName: String = "Transform" + + let properties: [AnyNodeProperty] + + let anchor: NodeProperty + let scale: NodeProperty + let rotation: NodeProperty + let position: NodeProperty? + let positionX: NodeProperty? + let positionY: NodeProperty? + let opacity: NodeProperty + + var childKeypaths: [KeypathSearchable] { + [] + } +} + +// MARK: - LayerTransformNode + +class LayerTransformNode: AnimatorNode { + + // MARK: Lifecycle + + init(transform: Transform) { + transformProperties = LayerTransformProperties(transform: transform) + } + + // MARK: Internal + + let outputNode: NodeOutput = PassThroughOutputNode(parent: nil) + + let transformProperties: LayerTransformProperties + + var parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + var isEnabled: Bool = true + + var opacity: Float = 1 + var localTransform: CATransform3D = CATransform3DIdentity + var globalTransform: CATransform3D = CATransform3DIdentity + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + transformProperties + } + + func shouldRebuildOutputs(frame _: CGFloat) -> Bool { + hasLocalUpdates || hasUpstreamUpdates + } + + func rebuildOutputs(frame _: CGFloat) { + opacity = Float(transformProperties.opacity.value.cgFloatValue) * 0.01 + + let position: CGPoint + if let point = transformProperties.position?.value.pointValue { + position = point + } else if + let xPos = transformProperties.positionX?.value.cgFloatValue, + let yPos = transformProperties.positionY?.value.cgFloatValue + { + position = CGPoint(x: xPos, y: yPos) + } else { + position = .zero + } + + localTransform = CATransform3D.makeTransform( + anchor: transformProperties.anchor.value.pointValue, + position: position, + scale: transformProperties.scale.value.sizeValue, + rotation: transformProperties.rotation.value.cgFloatValue, + skew: nil, + skewAxis: nil) + + if let parentNode = parentNode as? LayerTransformNode { + globalTransform = CATransform3DConcat(localTransform, parentNode.globalTransform) + } else { + globalTransform = localTransform + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift new file mode 100644 index 00000000..1cb911da --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift @@ -0,0 +1,321 @@ +// +// TextLayer.swift +// Pods +// +// Created by Brandon Withrow on 8/3/20. +// + +import CoreGraphics +import CoreText +import Foundation +import QuartzCore +/// Needed for NSMutableParagraphStyle... +#if os(OSX) +import AppKit +#else +import UIKit +#endif + +// MARK: - TextLayer + +final class TextLayer: CALayer { + + // MARK: Public + + public var text: String? { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var font: CTFont? { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var alignment: NSTextAlignment = .left { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var lineHeight: CGFloat = 0 { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var tracking: CGFloat = 0 { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var fillColor: CGColor? { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var strokeColor: CGColor? { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var strokeWidth: CGFloat = 0 { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public var strokeOnTop: Bool = false { + didSet { + setNeedsLayout() + setNeedsDisplay() + } + } + + public var preferredSize: CGSize? { + didSet { + needsContentUpdate = true + setNeedsLayout() + setNeedsDisplay() + } + } + + public func sizeToFit() { + updateTextContent() + bounds = drawingRect + anchorPoint = drawingAnchor + setNeedsLayout() + setNeedsDisplay() + } + + // MARK: Internal + + override func action(forKey _: String) -> CAAction? { + nil + } + + override func draw(in ctx: CGContext) { + guard let attributedString = attributedString else { return } + updateTextContent() + guard fillFrameSetter != nil || strokeFrameSetter != nil else { return } + + ctx.textMatrix = .identity + ctx.setAllowsAntialiasing(true) + ctx.setAllowsFontSmoothing(true) + ctx.setAllowsFontSubpixelPositioning(true) + ctx.setAllowsFontSubpixelQuantization(true) + + ctx.setShouldAntialias(true) + ctx.setShouldSmoothFonts(true) + ctx.setShouldSubpixelPositionFonts(true) + ctx.setShouldSubpixelQuantizeFonts(true) + + if contentsAreFlipped() { + ctx.translateBy(x: 0, y: drawingRect.height) + ctx.scaleBy(x: 1.0, y: -1.0) + } + + let drawingPath = CGPath(rect: drawingRect, transform: nil) + + let fillFrame: CTFrame? + if let setter = fillFrameSetter { + fillFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, attributedString.length), drawingPath, nil) + } else { + fillFrame = nil + } + + let strokeFrame: CTFrame? + if let setter = strokeFrameSetter { + strokeFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, attributedString.length), drawingPath, nil) + } else { + strokeFrame = nil + } + + if !strokeOnTop, let strokeFrame = strokeFrame { + CTFrameDraw(strokeFrame, ctx) + } + + if let fillFrame = fillFrame { + CTFrameDraw(fillFrame, ctx) + } + + if strokeOnTop, let strokeFrame = strokeFrame { + CTFrameDraw(strokeFrame, ctx) + } + } + + // MARK: Private + + private var drawingRect: CGRect = .zero + private var drawingAnchor: CGPoint = .zero + private var fillFrameSetter: CTFramesetter? + private var attributedString: NSAttributedString? + private var strokeFrameSetter: CTFramesetter? + private var needsContentUpdate: Bool = false + + // Draws Debug colors for the font alignment. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) + private func drawDebug(_ ctx: CGContext) { + if let font = font { + let ascent = CTFontGetAscent(font) + let descent = CTFontGetDescent(font) + let capHeight = CTFontGetCapHeight(font) + let leading = CTFontGetLeading(font) + + // Ascent Red + ctx.setFillColor(CGColor(srgbRed: 1, green: 0, blue: 0, alpha: 0.5)) + ctx.fill(CGRect(x: 0, y: 0, width: drawingRect.width, height: ascent)) + + // Descent Blue + ctx.setFillColor(CGColor(srgbRed: 0, green: 0, blue: 1, alpha: 0.5)) + ctx.fill(CGRect(x: 0, y: ascent, width: drawingRect.width, height: descent)) + + // Leading Yellow + ctx.setFillColor(CGColor(srgbRed: 1, green: 1, blue: 0, alpha: 0.5)) + ctx.fill(CGRect(x: 0, y: ascent + descent, width: drawingRect.width, height: leading)) + + // Cap height Green + ctx.setFillColor(CGColor(srgbRed: 0, green: 1, blue: 0, alpha: 0.5)) + ctx.fill(CGRect(x: 0, y: ascent - capHeight, width: drawingRect.width, height: capHeight)) + + if drawingRect.height - ascent + descent + leading > 0 { + // Remainder + ctx.setFillColor(CGColor(srgbRed: 0, green: 1, blue: 1, alpha: 0.5)) + ctx + .fill(CGRect( + x: 0, + y: ascent + descent + leading, + width: drawingRect.width, + height: drawingRect.height - ascent + descent + leading)) + } + } + } + + private func updateTextContent() { + guard needsContentUpdate else { return } + needsContentUpdate = false + guard let font = font, let text = text, text.count > 0, fillColor != nil || strokeColor != nil else { + drawingRect = .zero + drawingAnchor = .zero + attributedString = nil + fillFrameSetter = nil + strokeFrameSetter = nil + return + } + + // Get Font properties + let ascent = CTFontGetAscent(font) + let descent = CTFontGetDescent(font) + let capHeight = CTFontGetCapHeight(font) + let leading = CTFontGetLeading(font) + let minLineHeight = -(ascent + descent + leading) + + // Calculate line spacing + let lineSpacing = max(CGFloat(minLineHeight) + lineHeight, CGFloat(minLineHeight)) + // Build Attributes + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = lineSpacing + paragraphStyle.lineHeightMultiple = 1 + paragraphStyle.maximumLineHeight = ascent + descent + leading + paragraphStyle.alignment = alignment + paragraphStyle.lineBreakMode = NSLineBreakMode.byWordWrapping + var attributes: [NSAttributedString.Key: Any] = [ + NSAttributedString.Key.ligature: 0, + NSAttributedString.Key.font: font, + NSAttributedString.Key.kern: tracking, + NSAttributedString.Key.paragraphStyle: paragraphStyle, + ] + + if let fillColor = fillColor { + attributes[NSAttributedString.Key.foregroundColor] = fillColor + } + + let attrString = NSAttributedString(string: text, attributes: attributes) + attributedString = attrString + + if fillColor != nil { + let setter = CTFramesetterCreateWithAttributedString(attrString as CFAttributedString) + fillFrameSetter = setter + } else { + fillFrameSetter = nil + } + + if let strokeColor = strokeColor { + attributes[NSAttributedString.Key.foregroundColor] = nil + attributes[NSAttributedString.Key.strokeWidth] = strokeWidth + attributes[NSAttributedString.Key.strokeColor] = strokeColor + let strokeAttributedString = NSAttributedString(string: text, attributes: attributes) + strokeFrameSetter = CTFramesetterCreateWithAttributedString(strokeAttributedString as CFAttributedString) + } else { + strokeFrameSetter = nil + strokeWidth = 0 + } + + guard let setter = fillFrameSetter ?? strokeFrameSetter else { + return + } + + // Calculate drawing size and anchor offset + let textAnchor: CGPoint + if let preferredSize = preferredSize { + drawingRect = CGRect(origin: .zero, size: preferredSize) + drawingRect.size.height += (ascent - capHeight) + drawingRect.size.height += descent + textAnchor = CGPoint(x: 0, y: ascent - capHeight) + } else { + let size = CTFramesetterSuggestFrameSizeWithConstraints( + setter, + CFRange(location: 0, length: attrString.length), + nil, + CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude), + nil) + switch alignment { + case .left: + textAnchor = CGPoint(x: 0, y: ascent) + case .right: + textAnchor = CGPoint(x: size.width, y: ascent) + case .center: + textAnchor = CGPoint(x: size.width * 0.5, y: ascent) + default: + textAnchor = .zero + } + drawingRect = CGRect( + x: 0, + y: 0, + width: ceil(size.width), + height: ceil(size.height)) + } + + // Now Calculate Anchor + drawingAnchor = CGPoint( + x: textAnchor.x.remap(fromLow: 0, fromHigh: drawingRect.size.width, toLow: 0, toHigh: 1), + y: textAnchor.y.remap(fromLow: 0, fromHigh: drawingRect.size.height, toLow: 0, toHigh: 1)) + + if fillFrameSetter != nil && strokeFrameSetter != nil { + drawingRect.size.width += strokeWidth + drawingRect.size.height += strokeWidth + } + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift new file mode 100644 index 00000000..45a74c9c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift @@ -0,0 +1,116 @@ +// +// Animation.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/7/19. +// + +import Foundation + +// MARK: - CoordinateSpace + +public enum CoordinateSpace: Int, Codable { + case type2d + case type3d +} + +// MARK: - Animation + +/** + The `Animation` model is the top level model object in Lottie. + + An `Animation` holds all of the animation data backing a Lottie Animation. + Codable, see JSON schema [here](https://github.com/airbnb/lottie-web/tree/master/docs/json). + */ +public final class Animation: Codable { + + // MARK: Lifecycle + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Animation.CodingKeys.self) + version = try container.decode(String.self, forKey: .version) + type = try container.decodeIfPresent(CoordinateSpace.self, forKey: .type) ?? .type2d + startFrame = try container.decode(AnimationFrameTime.self, forKey: .startFrame) + endFrame = try container.decode(AnimationFrameTime.self, forKey: .endFrame) + framerate = try container.decode(Double.self, forKey: .framerate) + width = try container.decode(Int.self, forKey: .width) + height = try container.decode(Int.self, forKey: .height) + layers = try container.decode([LayerModel].self, ofFamily: LayerType.self, forKey: .layers) + glyphs = try container.decodeIfPresent([Glyph].self, forKey: .glyphs) + fonts = try container.decodeIfPresent(FontList.self, forKey: .fonts) + assetLibrary = try container.decodeIfPresent(AssetLibrary.self, forKey: .assetLibrary) + markers = try container.decodeIfPresent([Marker].self, forKey: .markers) + + if let markers = markers { + var markerMap: [String: Marker] = [:] + for marker in markers { + markerMap[marker.name] = marker + } + self.markerMap = markerMap + } else { + markerMap = nil + } + } + + // MARK: Public + + /// The start time of the composition in frameTime. + public let startFrame: AnimationFrameTime + + /// The end time of the composition in frameTime. + public let endFrame: AnimationFrameTime + + /// The frame rate of the composition. + public let framerate: Double + + /// Return all marker names, in order, or an empty list if none are specified + public var markerNames: [String] { + guard let markers = markers else { return [] } + return markers.map { $0.name } + } + + // MARK: Internal + + enum CodingKeys: String, CodingKey { + case version = "v" + case type = "ddd" + case startFrame = "ip" + case endFrame = "op" + case framerate = "fr" + case width = "w" + case height = "h" + case layers = "layers" + case glyphs = "chars" + case fonts = "fonts" + case assetLibrary = "assets" + case markers = "markers" + } + + /// The version of the JSON Schema. + let version: String + + /// The coordinate space of the composition. + let type: CoordinateSpace + + /// The height of the composition in points. + let width: Int + + /// The width of the composition in points. + let height: Int + + /// The list of animation layers + let layers: [LayerModel] + + /// The list of glyphs used for text rendering + let glyphs: [Glyph]? + + /// The list of fonts used for text rendering + let fonts: FontList? + + /// Asset Library + let assetLibrary: AssetLibrary? + + /// Markers + let markers: [Marker]? + let markerMap: [String: Marker]? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift new file mode 100644 index 00000000..e6bca025 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift @@ -0,0 +1,33 @@ +// +// Asset.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +public class Asset: Codable { + + // MARK: Lifecycle + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Asset.CodingKeys.self) + if let id = try? container.decode(String.self, forKey: .id) { + self.id = id + } else { + id = String(try container.decode(Int.self, forKey: .id)) + } + } + + // MARK: Public + + /// The ID of the asset + public let id: String + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case id + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift new file mode 100644 index 00000000..dd0d6d48 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift @@ -0,0 +1,52 @@ +// +// AssetLibrary.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +final class AssetLibrary: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + var container = try decoder.unkeyedContainer() + var containerForKeys = container + + var decodedAssets = [String : Asset]() + + var imageAssets = [String : ImageAsset]() + var precompAssets = [String : PrecompAsset]() + + while !container.isAtEnd { + let keyContainer = try containerForKeys.nestedContainer(keyedBy: PrecompAsset.CodingKeys.self) + if keyContainer.contains(.layers) { + let precompAsset = try container.decode(PrecompAsset.self) + decodedAssets[precompAsset.id] = precompAsset + precompAssets[precompAsset.id] = precompAsset + } else { + let imageAsset = try container.decode(ImageAsset.self) + decodedAssets[imageAsset.id] = imageAsset + imageAssets[imageAsset.id] = imageAsset + } + } + assets = decodedAssets + self.precompAssets = precompAssets + self.imageAssets = imageAssets + } + + // MARK: Internal + + /// The Assets + let assets: [String: Asset] + + let imageAssets: [String: ImageAsset] + let precompAssets: [String: PrecompAsset] + + func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + try container.encode(contentsOf: Array(assets.values)) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift new file mode 100644 index 00000000..284cfbf0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift @@ -0,0 +1,53 @@ +// +// ImageAsset.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +public final class ImageAsset: Asset { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ImageAsset.CodingKeys.self) + name = try container.decode(String.self, forKey: .name) + directory = try container.decode(String.self, forKey: .directory) + width = try container.decode(Double.self, forKey: .width) + height = try container.decode(Double.self, forKey: .height) + try super.init(from: decoder) + } + + // MARK: Public + + /// Image name + public let name: String + + /// Image Directory + public let directory: String + + /// Image Size + public let width: Double + + public let height: Double + + override public func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(name, forKey: .name) + try container.encode(directory, forKey: .directory) + try container.encode(width, forKey: .width) + try container.encode(height, forKey: .height) + } + + // MARK: Internal + + enum CodingKeys: String, CodingKey { + case name = "p" + case directory = "u" + case width = "w" + case height = "h" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift new file mode 100644 index 00000000..fd76fe93 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift @@ -0,0 +1,34 @@ +// +// PrecompAsset.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +final class PrecompAsset: Asset { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: PrecompAsset.CodingKeys.self) + layers = try container.decode([LayerModel].self, ofFamily: LayerType.self, forKey: .layers) + try super.init(from: decoder) + } + + // MARK: Internal + + enum CodingKeys: String, CodingKey { + case layers + } + + /// Layers of the precomp + let layers: [LayerModel] + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(layers, forKey: .layers) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift new file mode 100644 index 00000000..d1747033 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift @@ -0,0 +1,21 @@ +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit +#endif + +extension Bundle { + func getAnimationData(_ name: String, subdirectory: String? = nil) throws -> Data? { + // Check for files in the bundle at the given path + if let url = url(forResource: name, withExtension: "json", subdirectory: subdirectory) { + return try Data(contentsOf: url) + } + + // Check for data assets (not available on macOS) + #if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) + let assetKey = subdirectory != nil ? "\(subdirectory ?? "")/\(name)" : name + return NSDataAsset(name: assetKey, bundle: self)?.data + #else + return nil + #endif + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift new file mode 100644 index 00000000..c0d8d755 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift @@ -0,0 +1,44 @@ +// From: https://medium.com/@kewindannerfjordremeczki/swift-4-0-decodable-heterogeneous-collections-ecc0e6b468cf + +import Foundation + +// MARK: - ClassFamily + +/// To support a new class family, create an enum that conforms to this protocol and contains the different types. +protocol ClassFamily: Decodable { + /// The discriminator key. + static var discriminator: Discriminator { get } + + /// Returns the class type of the object corresponding to the value. + func getType() -> AnyObject.Type +} + +// MARK: - Discriminator + +/// Discriminator key enum used to retrieve discriminator fields in JSON payloads. +enum Discriminator: String, CodingKey { + case type = "ty" +} + +extension KeyedDecodingContainer { + + /// Decode a heterogeneous list of objects for a given family. + /// - Parameters: + /// - heterogeneousType: The decodable type of the list. + /// - family: The ClassFamily enum for the type family. + /// - key: The CodingKey to look up the list in the current container. + /// - Returns: The resulting list of heterogeneousType elements. + func decode(_: [T].Type, ofFamily family: U.Type, forKey key: K) throws -> [T] { + var container = try nestedUnkeyedContainer(forKey: key) + var list = [T]() + var tmpContainer = container + while !container.isAtEnd { + let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self) + let family: U = try typeContainer.decode(U.self, forKey: U.discriminator) + if let type = family.getType() as? T.Type { + list.append(try tmpContainer.decode(type)) + } + } + return list + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift new file mode 100644 index 00000000..c704f724 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift @@ -0,0 +1,145 @@ +// +// Keyframe.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/7/19. +// + +import CoreGraphics +import Foundation + +// MARK: - Keyframe + +/** + Keyframe represents a point in time and is the container for datatypes. + Note: This is a parent class and should not be used directly. + */ +final class Keyframe { + + // MARK: Lifecycle + + /// Initialize a value-only keyframe with no time data. + init( + _ value: T, + spatialInTangent: Vector3D? = nil, + spatialOutTangent: Vector3D? = nil) + { + self.value = value + time = 0 + isHold = true + inTangent = nil + outTangent = nil + self.spatialInTangent = spatialInTangent + self.spatialOutTangent = spatialOutTangent + } + + /// Initialize a keyframe + init( + value: T, + time: Double, + isHold: Bool, + inTangent: Vector2D?, + outTangent: Vector2D?, + spatialInTangent: Vector3D? = nil, + spatialOutTangent: Vector3D? = nil) + { + self.value = value + self.time = CGFloat(time) + self.isHold = isHold + self.outTangent = outTangent + self.inTangent = inTangent + self.spatialInTangent = spatialInTangent + self.spatialOutTangent = spatialOutTangent + } + + // MARK: Internal + + /// The value of the keyframe + let value: T + /// The time in frames of the keyframe. + let time: CGFloat + /// A hold keyframe freezes interpolation until the next keyframe that is not a hold. + let isHold: Bool + /// The in tangent for the time interpolation curve. + let inTangent: Vector2D? + /// The out tangent for the time interpolation curve. + let outTangent: Vector2D? + + /// The spacial in tangent of the vector. + let spatialInTangent: Vector3D? + /// The spacial out tangent of the vector. + let spatialOutTangent: Vector3D? +} + +// MARK: - KeyframeData + +/** + A generic class used to parse and remap keyframe json. + + Keyframe json has a couple of different variations and formats depending on the + type of keyframea and also the version of the JSON. By parsing the raw data + we can reconfigure it into a constant format. + */ +final class KeyframeData: Codable { + + // MARK: Lifecycle + + init( + startValue: T?, + endValue: T?, + time: Double?, + hold: Int?, + inTangent: Vector2D?, + outTangent: Vector2D?, + spatialInTangent: Vector3D?, + spatialOutTangent: Vector3D?) + { + self.startValue = startValue + self.endValue = endValue + self.time = time + self.hold = hold + self.inTangent = inTangent + self.outTangent = outTangent + self.spatialInTangent = spatialInTangent + self.spatialOutTangent = spatialOutTangent + } + + // MARK: Internal + + enum CodingKeys: String, CodingKey { + case startValue = "s" + case endValue = "e" + case time = "t" + case hold = "h" + case inTangent = "i" + case outTangent = "o" + case spatialInTangent = "ti" + case spatialOutTangent = "to" + } + + /// The start value of the keyframe + let startValue: T? + /// The End value of the keyframe. Note: Newer versions animation json do not have this field. + let endValue: T? + /// The time in frames of the keyframe. + let time: Double? + /// A hold keyframe freezes interpolation until the next keyframe that is not a hold. + let hold: Int? + + /// The in tangent for the time interpolation curve. + let inTangent: Vector2D? + /// The out tangent for the time interpolation curve. + let outTangent: Vector2D? + + /// The spacial in tangent of the vector. + let spatialInTangent: Vector3D? + /// The spacial out tangent of the vector. + let spatialOutTangent: Vector3D? + + var isHold: Bool { + if let hold = hold { + return hold > 0 + } + return false + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift new file mode 100644 index 00000000..cb0b6189 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift @@ -0,0 +1,120 @@ +// +// KeyframeGroup.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/14/19. +// + +import Foundation + +/** + Used for coding/decoding a group of Keyframes by type. + + Keyframe data is wrapped in a dictionary { "k" : KeyframeData }. + The keyframe data can either be an array of keyframes or, if no animation is present, the raw value. + This helper object is needed to properly decode the json. + */ + +final class KeyframeGroup: Codable where T: Codable, T: Interpolatable { + + // MARK: Lifecycle + + init(keyframes: ContiguousArray>) { + self.keyframes = keyframes + } + + init(_ value: T) { + keyframes = [Keyframe(value)] + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: KeyframeWrapperKey.self) + + if let keyframeData: T = try? container.decode(T.self, forKey: .keyframeData) { + /// Try to decode raw value; No keyframe data. + keyframes = [Keyframe(keyframeData)] + } else { + /** + Decode and array of keyframes. + + Body Movin and Lottie deal with keyframes in different ways. + + A keyframe object in Body movin defines a span of time with a START + and an END, from the current keyframe time to the next keyframe time. + + A keyframe object in Lottie defines a singular point in time/space. + This point has an in-tangent and an out-tangent. + + To properly decode this we must iterate through keyframes while holding + reference to the previous keyframe. + */ + + var keyframesContainer = try container.nestedUnkeyedContainer(forKey: .keyframeData) + var keyframes = ContiguousArray>() + var previousKeyframeData: KeyframeData? + while !keyframesContainer.isAtEnd { + // Ensure that Time and Value are present. + + let keyframeData = try keyframesContainer.decode(KeyframeData.self) + + guard + let value: T = keyframeData.startValue ?? previousKeyframeData?.endValue, + let time = keyframeData.time else + { + /// Missing keyframe data. JSON must be corrupt. + throw DecodingError.dataCorruptedError( + forKey: KeyframeWrapperKey.keyframeData, + in: container, + debugDescription: "Missing keyframe data.") + } + + keyframes.append(Keyframe( + value: value, + time: time, + isHold: keyframeData.isHold, + inTangent: previousKeyframeData?.inTangent, + outTangent: keyframeData.outTangent, + spatialInTangent: previousKeyframeData?.spatialInTangent, + spatialOutTangent: keyframeData.spatialOutTangent)) + previousKeyframeData = keyframeData + } + self.keyframes = keyframes + } + } + + // MARK: Internal + + let keyframes: ContiguousArray> + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: KeyframeWrapperKey.self) + + if keyframes.count == 1 { + let keyframe = keyframes[0] + try container.encode(keyframe.value, forKey: .keyframeData) + } else { + var keyframeContainer = container.nestedUnkeyedContainer(forKey: .keyframeData) + + for i in 1..( + startValue: keyframe.value, + endValue: nextKeyframe.value, + time: Double(keyframe.time), + hold: keyframe.isHold ? 1 : nil, + inTangent: nextKeyframe.inTangent, + outTangent: keyframe.outTangent, + spatialInTangent: nil, + spatialOutTangent: nil) + try keyframeContainer.encode(keyframeData) + } + } + } + + // MARK: Private + + private enum KeyframeWrapperKey: String, CodingKey { + case keyframeData = "k" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift new file mode 100644 index 00000000..92cdcfae --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift @@ -0,0 +1,37 @@ +// +// ImageLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// A layer that holds an image. +final class ImageLayerModel: LayerModel { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ImageLayerModel.CodingKeys.self) + referenceID = try container.decode(String.self, forKey: .referenceID) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The reference ID of the image. + let referenceID: String + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(referenceID, forKey: .referenceID) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case referenceID = "refId" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift new file mode 100644 index 00000000..506e6a11 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift @@ -0,0 +1,166 @@ +// +// Layer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/7/19. +// + +import Foundation + +// MARK: - LayerType + ClassFamily + +/// Used for mapping a heterogeneous list to classes for parsing. +extension LayerType: ClassFamily { + static var discriminator: Discriminator = .type + + func getType() -> AnyObject.Type { + switch self { + case .precomp: + return PreCompLayerModel.self + case .solid: + return SolidLayerModel.self + case .image: + return ImageLayerModel.self + case .null: + return LayerModel.self + case .shape: + return ShapeLayerModel.self + case .text: + return TextLayerModel.self + } + } +} + +// MARK: - LayerType + +public enum LayerType: Int, Codable { + case precomp + case solid + case image + case null + case shape + case text + + public init(from decoder: Decoder) throws { + self = try LayerType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .null + } +} + +// MARK: - MatteType + +public enum MatteType: Int, Codable { + case none + case add + case invert + case unknown +} + +// MARK: - BlendMode + +public enum BlendMode: Int, Codable { + case normal + case multiply + case screen + case overlay + case darken + case lighten + case colorDodge + case colorBurn + case hardLight + case softLight + case difference + case exclusion + case hue + case saturation + case color + case luminosity +} + +// MARK: - LayerModel + +/** + A base top container for shapes, images, and other view objects. + */ +class LayerModel: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: LayerModel.CodingKeys.self) + name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer" + index = try container.decodeIfPresent(Int.self, forKey: .index) ?? 0 + type = try container.decode(LayerType.self, forKey: .type) + coordinateSpace = try container.decodeIfPresent(CoordinateSpace.self, forKey: .coordinateSpace) ?? .type2d + inFrame = try container.decode(Double.self, forKey: .inFrame) + outFrame = try container.decode(Double.self, forKey: .outFrame) + startTime = try container.decode(Double.self, forKey: .startTime) + transform = try container.decode(Transform.self, forKey: .transform) + parent = try container.decodeIfPresent(Int.self, forKey: .parent) + blendMode = try container.decodeIfPresent(BlendMode.self, forKey: .blendMode) ?? .normal + masks = try container.decodeIfPresent([Mask].self, forKey: .masks) + timeStretch = try container.decodeIfPresent(Double.self, forKey: .timeStretch) ?? 1 + matte = try container.decodeIfPresent(MatteType.self, forKey: .matte) + hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false + } + + // MARK: Internal + + /// The readable name of the layer + let name: String + + /// The index of the layer + let index: Int + + /// The type of the layer. + let type: LayerType + + /// The coordinate space + let coordinateSpace: CoordinateSpace + + /// The in time of the layer in frames. + let inFrame: Double + /// The out time of the layer in frames. + let outFrame: Double + + /// The start time of the layer in frames. + let startTime: Double + + /// The transform of the layer + let transform: Transform + + /// The index of the parent layer, if applicable. + let parent: Int? + + /// The blending mode for the layer + let blendMode: BlendMode + + /// An array of masks for the layer. + let masks: [Mask]? + + /// A number that stretches time by a multiplier + let timeStretch: Double + + /// The type of matte if any. + let matte: MatteType? + + let hidden: Bool + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case name = "nm" + case index = "ind" + case type = "ty" + case coordinateSpace = "ddd" + case inFrame = "ip" + case outFrame = "op" + case startTime = "st" + case transform = "ks" + case parent = "parent" + case blendMode = "bm" + case masks = "masksProperties" + case timeStretch = "sr" + case matte = "tt" + case hidden = "hd" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift new file mode 100644 index 00000000..9eed00c0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift @@ -0,0 +1,55 @@ +// +// PreCompLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// A layer that holds another animation composition. +final class PreCompLayerModel: LayerModel { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: PreCompLayerModel.CodingKeys.self) + referenceID = try container.decode(String.self, forKey: .referenceID) + timeRemapping = try container.decodeIfPresent(KeyframeGroup.self, forKey: .timeRemapping) + width = try container.decode(Double.self, forKey: .width) + height = try container.decode(Double.self, forKey: .height) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The reference ID of the precomp. + let referenceID: String + + /// A value that remaps time over time. + let timeRemapping: KeyframeGroup? + + /// Precomp Width + let width: Double + + /// Precomp Height + let height: Double + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(referenceID, forKey: .referenceID) + try container.encode(timeRemapping, forKey: .timeRemapping) + try container.encode(width, forKey: .width) + try container.encode(height, forKey: .height) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case referenceID = "refId" + case timeRemapping = "tm" + case width = "w" + case height = "h" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift new file mode 100644 index 00000000..04d9c648 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift @@ -0,0 +1,37 @@ +// +// ShapeLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// A layer that holds vector shape objects. +final class ShapeLayerModel: LayerModel { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ShapeLayerModel.CodingKeys.self) + items = try container.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .items) + try super.init(from: decoder) + } + + // MARK: Internal + + /// A list of shape items. + let items: [ShapeItem] + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(items, forKey: .items) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case items = "shapes" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift new file mode 100644 index 00000000..b6d3669a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift @@ -0,0 +1,49 @@ +// +// SolidLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// A layer that holds a solid color. +final class SolidLayerModel: LayerModel { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: SolidLayerModel.CodingKeys.self) + colorHex = try container.decode(String.self, forKey: .colorHex) + width = try container.decode(Double.self, forKey: .width) + height = try container.decode(Double.self, forKey: .height) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The color of the solid in Hex // Change to value provider. + let colorHex: String + + /// The Width of the color layer + let width: Double + + /// The height of the color layer + let height: Double + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(colorHex, forKey: .colorHex) + try container.encode(width, forKey: .width) + try container.encode(height, forKey: .height) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case colorHex = "sc" + case width = "sw" + case height = "sh" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift new file mode 100644 index 00000000..1fac9bfe --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift @@ -0,0 +1,49 @@ +// +// TextLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// A layer that holds text. +final class TextLayerModel: LayerModel { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: TextLayerModel.CodingKeys.self) + let textContainer = try container.nestedContainer(keyedBy: TextCodingKeys.self, forKey: .textGroup) + text = try textContainer.decode(KeyframeGroup.self, forKey: .text) + animators = try textContainer.decode([TextAnimator].self, forKey: .animators) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The text for the layer + let text: KeyframeGroup + + /// Text animators + let animators: [TextAnimator] + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + var textContainer = container.nestedContainer(keyedBy: TextCodingKeys.self, forKey: .textGroup) + try textContainer.encode(text, forKey: .text) + try textContainer.encode(animators, forKey: .animators) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case textGroup = "t" + } + + private enum TextCodingKeys: String, CodingKey { + case text = "d" + case animators = "a" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift new file mode 100644 index 00000000..07299fd6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift @@ -0,0 +1,29 @@ +// +// DashPattern.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/22/19. +// + +import Foundation + +// MARK: - DashElementType + +enum DashElementType: String, Codable { + case offset = "o" + case dash = "d" + case gap = "g" +} + +// MARK: - DashElement + +final class DashElement: Codable { + + enum CodingKeys: String, CodingKey { + case type = "n" + case value = "v" + } + + let type: DashElementType + let value: KeyframeGroup +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift new file mode 100644 index 00000000..aaf15997 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift @@ -0,0 +1,23 @@ +// +// Marker.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +/// A time marker +final class Marker: Codable { + + enum CodingKeys: String, CodingKey { + case name = "cm" + case frameTime = "tm" + } + + /// The Marker Name + let name: String + + /// The Frame time of the marker + let frameTime: AnimationFrameTime +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift new file mode 100644 index 00000000..75b0af39 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift @@ -0,0 +1,56 @@ +// +// Mask.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - MaskMode + +enum MaskMode: String, Codable { + case add = "a" + case subtract = "s" + case intersect = "i" + case lighten = "l" + case darken = "d" + case difference = "f" + case none = "n" +} + +// MARK: - Mask + +final class Mask: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Mask.CodingKeys.self) + mode = try container.decodeIfPresent(MaskMode.self, forKey: .mode) ?? .add + opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) + shape = try container.decode(KeyframeGroup.self, forKey: .shape) + inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false + expansion = try container.decodeIfPresent(KeyframeGroup.self, forKey: .expansion) ?? KeyframeGroup(Vector1D(0)) + } + + // MARK: Internal + + enum CodingKeys: String, CodingKey { + case mode = "mode" + case opacity = "o" + case inverted = "inv" + case shape = "pt" + case expansion = "x" + } + + let mode: MaskMode + + let opacity: KeyframeGroup + + let shape: KeyframeGroup + + let inverted: Bool + + let expansion: KeyframeGroup +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift new file mode 100644 index 00000000..46322d64 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift @@ -0,0 +1,111 @@ +// +// Transform.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/7/19. +// + +import Foundation + +/// The animatable transform for a layer. Controls position, rotation, scale, and opacity. +final class Transform: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + /** + This manual override of decode is required because we want to throw an error + in the case that there is not position data. + */ + let container = try decoder.container(keyedBy: Transform.CodingKeys.self) + + // AnchorPoint + anchorPoint = try container + .decodeIfPresent(KeyframeGroup.self, forKey: .anchorPoint) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) + + // Position + if container.contains(.positionX), container.contains(.positionY) { + // Position dimensions are split into two keyframe groups + positionX = try container.decode(KeyframeGroup.self, forKey: .positionX) + positionY = try container.decode(KeyframeGroup.self, forKey: .positionY) + position = nil + } else if let positionKeyframes = try? container.decode(KeyframeGroup.self, forKey: .position) { + // Position dimensions are a single keyframe group. + position = positionKeyframes + positionX = nil + positionY = nil + } else if + let positionContainer = try? container.nestedContainer(keyedBy: PositionCodingKeys.self, forKey: .position), + let positionX = try? positionContainer.decode(KeyframeGroup.self, forKey: .positionX), + let positionY = try? positionContainer.decode(KeyframeGroup.self, forKey: .positionY) + { + /// Position keyframes are split and nested. + self.positionX = positionX + self.positionY = positionY + position = nil + } else { + /// Default value. + position = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) + positionX = nil + positionY = nil + } + + // Scale + scale = try container + .decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) + + // Rotation + if let rotationZ = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotationZ) { + rotation = rotationZ + } else { + rotation = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) + } + rotationZ = nil + + // Opacity + opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) + } + + // MARK: Internal + + enum CodingKeys: String, CodingKey { + case anchorPoint = "a" + case position = "p" + case positionX = "px" + case positionY = "py" + case scale = "s" + case rotation = "r" + case rotationZ = "rz" + case opacity = "o" + } + + enum PositionCodingKeys: String, CodingKey { + case split = "s" + case positionX = "x" + case positionY = "y" + } + + /// The anchor point of the transform. + let anchorPoint: KeyframeGroup + + /// The position of the transform. This is nil if the position data was split. + let position: KeyframeGroup? + + /// The positionX of the transform. This is nil if the position property is set. + let positionX: KeyframeGroup? + + /// The positionY of the transform. This is nil if the position property is set. + let positionY: KeyframeGroup? + + /// The scale of the transform + let scale: KeyframeGroup + + /// The rotation of the transform. Note: This is single dimensional rotation. + let rotation: KeyframeGroup + + /// The opacity of the transform. + let opacity: KeyframeGroup + + /// Should always be nil. + let rotationZ: KeyframeGroup? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift new file mode 100644 index 00000000..b207167e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift @@ -0,0 +1,59 @@ +// +// EllipseItem.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - PathDirection + +enum PathDirection: Int, Codable { + case clockwise = 1 + case userSetClockwise = 2 + case counterClockwise = 3 +} + +// MARK: - Ellipse + +/// An item that define an ellipse shape +final class Ellipse: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Ellipse.CodingKeys.self) + direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise + position = try container.decode(KeyframeGroup.self, forKey: .position) + size = try container.decode(KeyframeGroup.self, forKey: .size) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The direction of the ellipse. + let direction: PathDirection + + /// The position of the ellipse + let position: KeyframeGroup + + /// The size of the ellipse + let size: KeyframeGroup + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(direction, forKey: .direction) + try container.encode(position, forKey: .position) + try container.encode(size, forKey: .size) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case direction = "d" + case position = "p" + case size = "s" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift new file mode 100644 index 00000000..c3d558f2 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift @@ -0,0 +1,58 @@ +// +// FillShape.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - FillRule + +enum FillRule: Int, Codable { + case none + case nonZeroWinding + case evenOdd +} + +// MARK: - Fill + +/// An item that defines a fill render +final class Fill: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Fill.CodingKeys.self) + opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) + color = try container.decode(KeyframeGroup.self, forKey: .color) + fillRule = try container.decodeIfPresent(FillRule.self, forKey: .fillRule) ?? .nonZeroWinding + try super.init(from: decoder) + } + + // MARK: Internal + + /// The opacity of the fill + let opacity: KeyframeGroup + + /// The color keyframes for the fill + let color: KeyframeGroup + + let fillRule: FillRule + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(opacity, forKey: .opacity) + try container.encode(color, forKey: .color) + try container.encode(fillRule, forKey: .fillRule) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case opacity = "o" + case color = "c" + case fillRule = "r" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift new file mode 100644 index 00000000..9809c454 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift @@ -0,0 +1,95 @@ +// +// GradientFill.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - GradientType + +enum GradientType: Int, Codable { + case none + case linear + case radial +} + +// MARK: - GradientFill + +/// An item that define a gradient fill +final class GradientFill: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: GradientFill.CodingKeys.self) + opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) + startPoint = try container.decode(KeyframeGroup.self, forKey: .startPoint) + endPoint = try container.decode(KeyframeGroup.self, forKey: .endPoint) + gradientType = try container.decode(GradientType.self, forKey: .gradientType) + highlightLength = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightLength) + highlightAngle = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightAngle) + let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) + colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors) + numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The opacity of the fill + let opacity: KeyframeGroup + + /// The start of the gradient + let startPoint: KeyframeGroup + + /// The end of the gradient + let endPoint: KeyframeGroup + + /// The type of gradient + let gradientType: GradientType + + /// Gradient Highlight Length. Only if type is Radial + let highlightLength: KeyframeGroup? + + /// Highlight Angle. Only if type is Radial + let highlightAngle: KeyframeGroup? + + /// The number of color points in the gradient + let numberOfColors: Int + + /// The Colors of the gradient. + let colors: KeyframeGroup<[Double]> + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(opacity, forKey: .opacity) + try container.encode(startPoint, forKey: .startPoint) + try container.encode(endPoint, forKey: .endPoint) + try container.encode(gradientType, forKey: .gradientType) + try container.encodeIfPresent(highlightLength, forKey: .highlightLength) + try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle) + var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) + try colorsContainer.encode(numberOfColors, forKey: .numberOfColors) + try colorsContainer.encode(colors, forKey: .colors) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case opacity = "o" + case startPoint = "s" + case endPoint = "e" + case gradientType = "t" + case highlightLength = "h" + case highlightAngle = "a" + case colors = "g" + } + + private enum GradientDataKeys: String, CodingKey { + case numberOfColors = "p" + case colors = "k" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift new file mode 100644 index 00000000..eb0d6161 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift @@ -0,0 +1,136 @@ +// +// GradientStroke.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - LineCap + +enum LineCap: Int, Codable { + case none + case butt + case round + case square +} + +// MARK: - LineJoin + +enum LineJoin: Int, Codable { + case none + case miter + case round + case bevel +} + +// MARK: - GradientStroke + +/// An item that define an ellipse shape +final class GradientStroke: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: GradientStroke.CodingKeys.self) + opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) + startPoint = try container.decode(KeyframeGroup.self, forKey: .startPoint) + endPoint = try container.decode(KeyframeGroup.self, forKey: .endPoint) + gradientType = try container.decode(GradientType.self, forKey: .gradientType) + highlightLength = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightLength) + highlightAngle = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightAngle) + width = try container.decode(KeyframeGroup.self, forKey: .width) + lineCap = try container.decodeIfPresent(LineCap.self, forKey: .lineCap) ?? .round + lineJoin = try container.decodeIfPresent(LineJoin.self, forKey: .lineJoin) ?? .round + miterLimit = try container.decodeIfPresent(Double.self, forKey: .miterLimit) ?? 4 + // TODO Decode Color Objects instead of array. + let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) + colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors) + numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors) + dashPattern = try container.decodeIfPresent([DashElement].self, forKey: .dashPattern) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The opacity of the fill + let opacity: KeyframeGroup + + /// The start of the gradient + let startPoint: KeyframeGroup + + /// The end of the gradient + let endPoint: KeyframeGroup + + /// The type of gradient + let gradientType: GradientType + + /// Gradient Highlight Length. Only if type is Radial + let highlightLength: KeyframeGroup? + + /// Highlight Angle. Only if type is Radial + let highlightAngle: KeyframeGroup? + + /// The number of color points in the gradient + let numberOfColors: Int + + /// The Colors of the gradient. + let colors: KeyframeGroup<[Double]> + + /// The width of the stroke + let width: KeyframeGroup + + /// Line Cap + let lineCap: LineCap + + /// Line Join + let lineJoin: LineJoin + + /// Miter Limit + let miterLimit: Double + + /// The dash pattern of the stroke + let dashPattern: [DashElement]? + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(opacity, forKey: .opacity) + try container.encode(startPoint, forKey: .startPoint) + try container.encode(endPoint, forKey: .endPoint) + try container.encode(gradientType, forKey: .gradientType) + try container.encodeIfPresent(highlightLength, forKey: .highlightLength) + try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle) + try container.encode(width, forKey: .width) + try container.encode(lineCap, forKey: .lineCap) + try container.encode(lineJoin, forKey: .lineJoin) + try container.encode(miterLimit, forKey: .miterLimit) + var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) + try colorsContainer.encode(numberOfColors, forKey: .numberOfColors) + try colorsContainer.encode(colors, forKey: .colors) + try container.encodeIfPresent(dashPattern, forKey: .dashPattern) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case opacity = "o" + case startPoint = "s" + case endPoint = "e" + case gradientType = "t" + case highlightLength = "h" + case highlightAngle = "a" + case colors = "g" + case width = "w" + case lineCap = "lc" + case lineJoin = "lj" + case miterLimit = "ml" + case dashPattern = "d" + } + + private enum GradientDataKeys: String, CodingKey { + case numberOfColors = "p" + case colors = "k" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift new file mode 100644 index 00000000..0055a005 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift @@ -0,0 +1,37 @@ +// +// GroupItem.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// An item that define an ellipse shape +final class Group: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Group.CodingKeys.self) + items = try container.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .items) + try super.init(from: decoder) + } + + // MARK: Internal + + /// A list of shape items. + let items: [ShapeItem] + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(items, forKey: .items) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case items = "it" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift new file mode 100644 index 00000000..a0b7dbaf --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift @@ -0,0 +1,50 @@ +// +// Merge.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - MergeMode + +enum MergeMode: Int, Codable { + case none + case merge + case add + case subtract + case intersect + case exclude +} + +// MARK: - Merge + +/// An item that define an ellipse shape +final class Merge: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Merge.CodingKeys.self) + mode = try container.decode(MergeMode.self, forKey: .mode) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The mode of the merge path + let mode: MergeMode + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(mode, forKey: .mode) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case mode = "mm" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift new file mode 100644 index 00000000..548c09f5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift @@ -0,0 +1,55 @@ +// +// Rectangle.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// An item that define an ellipse shape +final class Rectangle: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Rectangle.CodingKeys.self) + direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise + position = try container.decode(KeyframeGroup.self, forKey: .position) + size = try container.decode(KeyframeGroup.self, forKey: .size) + cornerRadius = try container.decode(KeyframeGroup.self, forKey: .cornerRadius) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The direction of the rect. + let direction: PathDirection + + /// The position + let position: KeyframeGroup + + /// The size + let size: KeyframeGroup + + /// The Corner radius of the rectangle + let cornerRadius: KeyframeGroup + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(direction, forKey: .direction) + try container.encode(position, forKey: .position) + try container.encode(size, forKey: .size) + try container.encode(cornerRadius, forKey: .cornerRadius) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case direction = "d" + case position = "p" + case size = "s" + case cornerRadius = "r" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift new file mode 100644 index 00000000..a88a1e7f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift @@ -0,0 +1,91 @@ +// +// Repeater.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// An item that define an ellipse shape +final class Repeater: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Repeater.CodingKeys.self) + copies = try container.decodeIfPresent(KeyframeGroup.self, forKey: .copies) ?? KeyframeGroup(Vector1D(0)) + offset = try container.decodeIfPresent(KeyframeGroup.self, forKey: .offset) ?? KeyframeGroup(Vector1D(0)) + let transformContainer = try container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform) + startOpacity = try transformContainer + .decodeIfPresent(KeyframeGroup.self, forKey: .startOpacity) ?? KeyframeGroup(Vector1D(100)) + endOpacity = try transformContainer + .decodeIfPresent(KeyframeGroup.self, forKey: .endOpacity) ?? KeyframeGroup(Vector1D(100)) + rotation = try transformContainer + .decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) + position = try transformContainer + .decodeIfPresent(KeyframeGroup.self, forKey: .position) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) + anchorPoint = try transformContainer + .decodeIfPresent(KeyframeGroup.self, forKey: .anchorPoint) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) + scale = try transformContainer + .decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The number of copies to repeat + let copies: KeyframeGroup + + /// The offset of each copy + let offset: KeyframeGroup + + /// Start Opacity + let startOpacity: KeyframeGroup + + /// End opacity + let endOpacity: KeyframeGroup + + /// The rotation + let rotation: KeyframeGroup + + /// Anchor Point + let anchorPoint: KeyframeGroup + + /// Position + let position: KeyframeGroup + + /// Scale + let scale: KeyframeGroup + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(copies, forKey: .copies) + try container.encode(offset, forKey: .offset) + var transformContainer = container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform) + try transformContainer.encode(startOpacity, forKey: .startOpacity) + try transformContainer.encode(endOpacity, forKey: .endOpacity) + try transformContainer.encode(rotation, forKey: .rotation) + try transformContainer.encode(position, forKey: .position) + try transformContainer.encode(anchorPoint, forKey: .anchorPoint) + try transformContainer.encode(scale, forKey: .scale) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case copies = "c" + case offset = "o" + case transform = "tr" + } + + private enum TransformKeys: String, CodingKey { + case rotation = "r" + case startOpacity = "so" + case endOpacity = "eo" + case anchorPoint = "a" + case position = "p" + case scale = "s" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift new file mode 100644 index 00000000..6e3ae7ae --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift @@ -0,0 +1,42 @@ +// +// VectorShape.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// An item that define an ellipse shape +final class Shape: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Shape.CodingKeys.self) + path = try container.decode(KeyframeGroup.self, forKey: .path) + direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The Path + let path: KeyframeGroup + + let direction: PathDirection? + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(path, forKey: .path) + try container.encodeIfPresent(direction, forKey: .direction) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case path = "ks" + case direction = "d" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift new file mode 100644 index 00000000..7084d499 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift @@ -0,0 +1,106 @@ +// +// ShapeItem.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - ShapeType + ClassFamily + +/// Used for mapping a heterogeneous list to classes for parsing. +extension ShapeType: ClassFamily { + + static var discriminator: Discriminator = .type + + func getType() -> AnyObject.Type { + switch self { + case .ellipse: + return Ellipse.self + case .fill: + return Fill.self + case .gradientFill: + return GradientFill.self + case .group: + return Group.self + case .gradientStroke: + return GradientStroke.self + case .merge: + return Merge.self + case .rectangle: + return Rectangle.self + case .repeater: + return Repeater.self + case .shape: + return Shape.self + case .star: + return Star.self + case .stroke: + return Stroke.self + case .trim: + return Trim.self + case .transform: + return ShapeTransform.self + default: + return ShapeItem.self + } + } +} + +// MARK: - ShapeType + +enum ShapeType: String, Codable { + case ellipse = "el" + case fill = "fl" + case gradientFill = "gf" + case group = "gr" + case gradientStroke = "gs" + case merge = "mm" + case rectangle = "rc" + case repeater = "rp" + case round = "rd" + case shape = "sh" + case star = "sr" + case stroke = "st" + case trim = "tm" + case transform = "tr" + case unknown + + public init(from decoder: Decoder) throws { + self = try ShapeType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown + } +} + +// MARK: - ShapeItem + +/// An item belonging to a Shape Layer +class ShapeItem: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ShapeItem.CodingKeys.self) + name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer" + type = try container.decode(ShapeType.self, forKey: .type) + hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false + } + + // MARK: Internal + + /// The name of the shape + let name: String + + /// The type of shape + let type: ShapeType + + let hidden: Bool + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case name = "nm" + case type = "ty" + case hidden = "hd" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift new file mode 100644 index 00000000..3374644c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift @@ -0,0 +1,76 @@ +// +// TransformItem.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// An item that define an ellipse shape +final class ShapeTransform: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ShapeTransform.CodingKeys.self) + anchor = try container + .decodeIfPresent(KeyframeGroup.self, forKey: .anchor) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) + position = try container + .decodeIfPresent(KeyframeGroup.self, forKey: .position) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) + scale = try container + .decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) + rotation = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) + opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) + skew = try container.decodeIfPresent(KeyframeGroup.self, forKey: .skew) ?? KeyframeGroup(Vector1D(0)) + skewAxis = try container.decodeIfPresent(KeyframeGroup.self, forKey: .skewAxis) ?? KeyframeGroup(Vector1D(0)) + try super.init(from: decoder) + } + + // MARK: Internal + + /// Anchor Point + let anchor: KeyframeGroup + + /// Position + let position: KeyframeGroup + + /// Scale + let scale: KeyframeGroup + + /// Rotation + let rotation: KeyframeGroup + + /// opacity + let opacity: KeyframeGroup + + /// Skew + let skew: KeyframeGroup + + /// Skew Axis + let skewAxis: KeyframeGroup + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(anchor, forKey: .anchor) + try container.encode(position, forKey: .position) + try container.encode(scale, forKey: .scale) + try container.encode(rotation, forKey: .rotation) + try container.encode(opacity, forKey: .opacity) + try container.encode(skew, forKey: .skew) + try container.encode(skewAxis, forKey: .skewAxis) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case anchor = "a" + case position = "p" + case scale = "s" + case rotation = "r" + case opacity = "o" + case skew = "sk" + case skewAxis = "sa" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift new file mode 100644 index 00000000..63dbfd61 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift @@ -0,0 +1,95 @@ +// +// Star.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - StarType + +enum StarType: Int, Codable { + case none + case star + case polygon +} + +// MARK: - Star + +/// An item that define an ellipse shape +final class Star: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Star.CodingKeys.self) + direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise + position = try container.decode(KeyframeGroup.self, forKey: .position) + outerRadius = try container.decode(KeyframeGroup.self, forKey: .outerRadius) + outerRoundness = try container.decode(KeyframeGroup.self, forKey: .outerRoundness) + innerRadius = try container.decodeIfPresent(KeyframeGroup.self, forKey: .innerRadius) + innerRoundness = try container.decodeIfPresent(KeyframeGroup.self, forKey: .innerRoundness) + rotation = try container.decode(KeyframeGroup.self, forKey: .rotation) + points = try container.decode(KeyframeGroup.self, forKey: .points) + starType = try container.decode(StarType.self, forKey: .starType) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The direction of the star. + let direction: PathDirection + + /// The position of the star + let position: KeyframeGroup + + /// The outer radius of the star + let outerRadius: KeyframeGroup + + /// The outer roundness of the star + let outerRoundness: KeyframeGroup + + /// The outer radius of the star + let innerRadius: KeyframeGroup? + + /// The outer roundness of the star + let innerRoundness: KeyframeGroup? + + /// The rotation of the star + let rotation: KeyframeGroup + + /// The number of points on the star + let points: KeyframeGroup + + /// The type of star + let starType: StarType + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(direction, forKey: .direction) + try container.encode(position, forKey: .position) + try container.encode(outerRadius, forKey: .outerRadius) + try container.encode(outerRoundness, forKey: .outerRoundness) + try container.encode(innerRadius, forKey: .innerRadius) + try container.encode(innerRoundness, forKey: .innerRoundness) + try container.encode(rotation, forKey: .rotation) + try container.encode(points, forKey: .points) + try container.encode(starType, forKey: .starType) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case direction = "d" + case position = "p" + case outerRadius = "or" + case outerRoundness = "os" + case innerRadius = "ir" + case innerRoundness = "is" + case rotation = "r" + case points = "pt" + case starType = "sy" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift new file mode 100644 index 00000000..c5c79c42 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift @@ -0,0 +1,73 @@ +// +// Stroke.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +/// An item that define an ellipse shape +final class Stroke: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Stroke.CodingKeys.self) + opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) + color = try container.decode(KeyframeGroup.self, forKey: .color) + width = try container.decode(KeyframeGroup.self, forKey: .width) + lineCap = try container.decodeIfPresent(LineCap.self, forKey: .lineCap) ?? .round + lineJoin = try container.decodeIfPresent(LineJoin.self, forKey: .lineJoin) ?? .round + miterLimit = try container.decodeIfPresent(Double.self, forKey: .miterLimit) ?? 4 + dashPattern = try container.decodeIfPresent([DashElement].self, forKey: .dashPattern) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The opacity of the stroke + let opacity: KeyframeGroup + + /// The Color of the stroke + let color: KeyframeGroup + + /// The width of the stroke + let width: KeyframeGroup + + /// Line Cap + let lineCap: LineCap + + /// Line Join + let lineJoin: LineJoin + + /// Miter Limit + let miterLimit: Double + + /// The dash pattern of the stroke + let dashPattern: [DashElement]? + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(opacity, forKey: .opacity) + try container.encode(color, forKey: .color) + try container.encode(width, forKey: .width) + try container.encode(lineCap, forKey: .lineCap) + try container.encode(lineJoin, forKey: .lineJoin) + try container.encode(miterLimit, forKey: .miterLimit) + try container.encodeIfPresent(dashPattern, forKey: .dashPattern) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case opacity = "o" + case color = "c" + case width = "w" + case lineCap = "lc" + case lineJoin = "lj" + case miterLimit = "ml" + case dashPattern = "d" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift new file mode 100644 index 00000000..bbd9a7bb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift @@ -0,0 +1,63 @@ +// +// Trim.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import Foundation + +// MARK: - TrimType + +enum TrimType: Int, Codable { + case simultaneously = 1 + case individually = 2 +} + +// MARK: - Trim + +/// An item that define an ellipse shape +final class Trim: ShapeItem { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Trim.CodingKeys.self) + start = try container.decode(KeyframeGroup.self, forKey: .start) + end = try container.decode(KeyframeGroup.self, forKey: .end) + offset = try container.decode(KeyframeGroup.self, forKey: .offset) + trimType = try container.decode(TrimType.self, forKey: .trimType) + try super.init(from: decoder) + } + + // MARK: Internal + + /// The start of the trim + let start: KeyframeGroup + + /// The end of the trim + let end: KeyframeGroup + + /// The offset of the trim + let offset: KeyframeGroup + + let trimType: TrimType + + override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(start, forKey: .start) + try container.encode(end, forKey: .end) + try container.encode(offset, forKey: .offset) + try container.encode(trimType, forKey: .trimType) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case start = "s" + case end = "e" + case offset = "o" + case trimType = "m" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift new file mode 100644 index 00000000..2b5b60f9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift @@ -0,0 +1,42 @@ +// +// Font.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +// MARK: - Font + +final class Font: Codable { + + // MARK: Internal + + let name: String + let familyName: String + let style: String + let ascent: Double + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case name = "fName" + case familyName = "fFamily" + case style = "fStyle" + case ascent = "ascent" + } + +} + +// MARK: - FontList + +/// A list of fonts +final class FontList: Codable { + + enum CodingKeys: String, CodingKey { + case fonts = "list" + } + + let fonts: [Font] +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift new file mode 100644 index 00000000..cf71fcc8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift @@ -0,0 +1,80 @@ +// +// Glyph.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +/// A model that holds a vector character +final class Glyph: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Glyph.CodingKeys.self) + character = try container.decode(String.self, forKey: .character) + fontSize = try container.decode(Double.self, forKey: .fontSize) + fontFamily = try container.decode(String.self, forKey: .fontFamily) + fontStyle = try container.decode(String.self, forKey: .fontStyle) + width = try container.decode(Double.self, forKey: .width) + if + container.contains(.shapeWrapper), + let shapeContainer = try? container.nestedContainer(keyedBy: ShapeKey.self, forKey: .shapeWrapper), + shapeContainer.contains(.shapes) + { + shapes = try shapeContainer.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .shapes) + } else { + shapes = [] + } + } + + // MARK: Internal + + /// The character + let character: String + + /// The font size of the character + let fontSize: Double + + /// The font family of the character + let fontFamily: String + + /// The Style of the character + let fontStyle: String + + /// The Width of the character + let width: Double + + /// The Shape Data of the Character + let shapes: [ShapeItem] + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(character, forKey: .character) + try container.encode(fontSize, forKey: .fontSize) + try container.encode(fontFamily, forKey: .fontFamily) + try container.encode(fontStyle, forKey: .fontStyle) + try container.encode(width, forKey: .width) + + var shapeContainer = container.nestedContainer(keyedBy: ShapeKey.self, forKey: .shapeWrapper) + try shapeContainer.encode(shapes, forKey: .shapes) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case character = "ch" + case fontSize = "size" + case fontFamily = "fFamily" + case fontStyle = "style" + case width = "w" + case shapeWrapper = "data" + } + + private enum ShapeKey: String, CodingKey { + case shapes + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift new file mode 100644 index 00000000..34801683 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift @@ -0,0 +1,105 @@ +// +// TextAnimator.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +final class TextAnimator: Codable { + + // MARK: Lifecycle + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: TextAnimator.CodingKeys.self) + name = try container.decodeIfPresent(String.self, forKey: .name) ?? "" + let animatorContainer = try container.nestedContainer(keyedBy: TextAnimatorKeys.self, forKey: .textAnimator) + fillColor = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .fillColor) + strokeColor = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .strokeColor) + strokeWidth = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .strokeWidth) + tracking = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .tracking) + anchor = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .anchor) + position = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .position) + scale = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .scale) + skew = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .skew) + skewAxis = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .skewAxis) + rotation = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) + opacity = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) + + } + + // MARK: Internal + + let name: String + + /// Anchor + let anchor: KeyframeGroup? + + /// Position + let position: KeyframeGroup? + + /// Scale + let scale: KeyframeGroup? + + /// Skew + let skew: KeyframeGroup? + + /// Skew Axis + let skewAxis: KeyframeGroup? + + /// Rotation + let rotation: KeyframeGroup? + + /// Opacity + let opacity: KeyframeGroup? + + /// Stroke Color + let strokeColor: KeyframeGroup? + + /// Fill Color + let fillColor: KeyframeGroup? + + /// Stroke Width + let strokeWidth: KeyframeGroup? + + /// Tracking + let tracking: KeyframeGroup? + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + var animatorContainer = container.nestedContainer(keyedBy: TextAnimatorKeys.self, forKey: .textAnimator) + try animatorContainer.encodeIfPresent(fillColor, forKey: .fillColor) + try animatorContainer.encodeIfPresent(strokeColor, forKey: .strokeColor) + try animatorContainer.encodeIfPresent(strokeWidth, forKey: .strokeWidth) + try animatorContainer.encodeIfPresent(tracking, forKey: .tracking) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { +// case textSelector = "s" TODO + case textAnimator = "a" + case name = "nm" + } + + private enum TextSelectorKeys: String, CodingKey { + case start = "s" + case end = "e" + case offset = "o" + } + + private enum TextAnimatorKeys: String, CodingKey { + case fillColor = "fc" + case strokeColor = "sc" + case strokeWidth = "sw" + case tracking = "t" + case anchor = "a" + case position = "p" + case scale = "s" + case skew = "sk" + case skewAxis = "sa" + case rotation = "r" + case opacity = "o" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift new file mode 100644 index 00000000..a983e206 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift @@ -0,0 +1,78 @@ +// +// TextDocument.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/9/19. +// + +import Foundation + +// MARK: - TextJustification + +enum TextJustification: Int, Codable { + case left + case right + case center +} + +// MARK: - TextDocument + +final class TextDocument: Codable { + + // MARK: Internal + + /// The Text + let text: String + + /// The Font size + let fontSize: Double + + /// The Font Family + let fontFamily: String + + /// Justification + let justification: TextJustification + + /// Tracking + let tracking: Int + + /// Line Height + let lineHeight: Double + + /// Baseline + let baseline: Double? + + /// Fill Color data + let fillColorData: Color? + + /// Scroke Color data + let strokeColorData: Color? + + /// Stroke Width + let strokeWidth: Double? + + /// Stroke Over Fill + let strokeOverFill: Bool? + + let textFramePosition: Vector3D? + + let textFrameSize: Vector3D? + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case text = "t" + case fontSize = "s" + case fontFamily = "f" + case justification = "j" + case tracking = "tr" + case lineHeight = "lh" + case baseline = "ls" + case fillColorData = "fc" + case strokeColorData = "sc" + case strokeWidth = "sw" + case strokeOverFill = "of" + case textFramePosition = "ps" + case textFrameSize = "sz" + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift new file mode 100644 index 00000000..2df59082 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift @@ -0,0 +1,97 @@ +// +// ItemsExtension.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/18/19. +// + +import Foundation + +// MARK: - NodeTree + +final class NodeTree { + var rootNode: AnimatorNode? = nil + var transform: ShapeTransform? = nil + var renderContainers: [ShapeContainerLayer] = [] + var paths: [PathOutputNode] = [] + var childrenNodes: [AnimatorNode] = [] +} + +extension Array where Element == ShapeItem { + func initializeNodeTree() -> NodeTree { + + let nodeTree = NodeTree() + + for item in self { + guard item.hidden == false, item.type != .unknown else { continue } + if let fill = item as? Fill { + let node = FillNode(parentNode: nodeTree.rootNode, fill: fill) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let stroke = item as? Stroke { + let node = StrokeNode(parentNode: nodeTree.rootNode, stroke: stroke) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let gradientFill = item as? GradientFill { + let node = GradientFillNode(parentNode: nodeTree.rootNode, gradientFill: gradientFill) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let gradientStroke = item as? GradientStroke { + let node = GradientStrokeNode(parentNode: nodeTree.rootNode, gradientStroke: gradientStroke) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let ellipse = item as? Ellipse { + let node = EllipseNode(parentNode: nodeTree.rootNode, ellipse: ellipse) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let rect = item as? Rectangle { + let node = RectangleNode(parentNode: nodeTree.rootNode, rectangle: rect) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let star = item as? Star { + switch star.starType { + case .none: + continue + case .polygon: + let node = PolygonNode(parentNode: nodeTree.rootNode, star: star) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + case .star: + let node = StarNode(parentNode: nodeTree.rootNode, star: star) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } + } else if let shape = item as? Shape { + let node = ShapeNode(parentNode: nodeTree.rootNode, shape: shape) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let trim = item as? Trim { + let node = TrimPathNode(parentNode: nodeTree.rootNode, trim: trim, upstreamPaths: nodeTree.paths) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + } else if let xform = item as? ShapeTransform { + nodeTree.transform = xform + continue + } else if let group = item as? Group { + + let tree = group.items.initializeNodeTree() + let node = GroupNode(name: group.name, parentNode: nodeTree.rootNode, tree: tree) + nodeTree.rootNode = node + nodeTree.childrenNodes.append(node) + /// Now add all child paths to current tree + nodeTree.paths.append(contentsOf: tree.paths) + nodeTree.renderContainers.append(node.container) + } + + if let pathNode = nodeTree.rootNode as? PathNode { + //// Add path container to the node tree + nodeTree.paths.append(pathNode.pathOutput) + } + + if let renderNode = nodeTree.rootNode as? RenderNode { + nodeTree.renderContainers.append(ShapeRenderLayer(renderer: renderNode.renderer)) + } + } + return nodeTree + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift new file mode 100644 index 00000000..14e00178 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift @@ -0,0 +1,53 @@ +// +// NodeProperty.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +/// A node property that holds a reference to a T ValueProvider and a T ValueContainer. +class NodeProperty: AnyNodeProperty { + + // MARK: Lifecycle + + init(provider: AnyValueProvider) { + valueProvider = provider + typedContainer = ValueContainer(provider.value(frame: 0) as! T) + typedContainer.setNeedsUpdate() + } + + // MARK: Internal + + var valueProvider: AnyValueProvider + + var valueType: Any.Type { T.self } + + var value: T { + typedContainer.outputValue + } + + var valueContainer: AnyValueContainer { + typedContainer + } + + func needsUpdate(frame: CGFloat) -> Bool { + valueContainer.needsUpdate || valueProvider.hasUpdate(frame: frame) + } + + func setProvider(provider: AnyValueProvider) { + guard provider.valueType == valueType else { return } + valueProvider = provider + valueContainer.setNeedsUpdate() + } + + func update(frame: CGFloat) { + typedContainer.setValue(valueProvider.value(frame: frame), forFrame: frame) + } + + // MARK: Fileprivate + + fileprivate var typedContainer: ValueContainer +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift new file mode 100644 index 00000000..4f2d5adb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift @@ -0,0 +1,47 @@ +// +// AnyNodeProperty.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +// MARK: - AnyNodeProperty + +/// A property of a node. The node property holds a provider and a container +protocol AnyNodeProperty { + + /// Returns true if the property needs to recompute its stored value + func needsUpdate(frame: CGFloat) -> Bool + + /// Updates the property for the frame + func update(frame: CGFloat) + + /// The stored value container for the property + var valueContainer: AnyValueContainer { get } + + /// The value provider for the property + var valueProvider: AnyValueProvider { get } + + /// The Type of the value provider + var valueType: Any.Type { get } + + /// Sets the value provider for the property. + func setProvider(provider: AnyValueProvider) +} + +extension AnyNodeProperty { + + /// Returns the most recently computed value for the keypath, returns nil if property wasn't found + func getValueOfType() -> T? { + valueContainer.value as? T + } + + /// Returns the most recently computed value for the keypath, returns nil if property wasn't found + func getValue() -> Any? { + valueContainer.value + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift new file mode 100644 index 00000000..769d51cb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift @@ -0,0 +1,26 @@ +// +// AnyValueContainer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +/// The container for the value of a property. +protocol AnyValueContainer: AnyObject { + + /// The stored value of the container + var value: Any { get } + + /// Notifies the provider that it should update its container + func setNeedsUpdate() + + /// When true the container needs to have its value updated by its provider + var needsUpdate: Bool { get } + + /// The frame time of the last provided update + var lastUpdateFrame: CGFloat { get } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift new file mode 100644 index 00000000..b46f524f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift @@ -0,0 +1,24 @@ +// +// KeypathSettable.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +import QuartzCore + +/// Protocol that provides keypath search functionality. Returns all node properties associated with a keypath. +protocol KeypathSearchable { + + /// The name of the Keypath + var keypathName: String { get } + + /// A list of properties belonging to the keypath. + var keypathProperties: [String: AnyNodeProperty] { get } + + /// Children Keypaths + var childKeypaths: [KeypathSearchable] { get } + + var keypathLayer: CALayer? { get } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift new file mode 100644 index 00000000..07b101f7 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift @@ -0,0 +1,44 @@ +// +// NodePropertyMap.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/21/19. +// + +import Foundation +import QuartzCore + +// MARK: - NodePropertyMap + +protocol NodePropertyMap { + var properties: [AnyNodeProperty] { get } +} + +extension NodePropertyMap { + + var childKeypaths: [KeypathSearchable] { + [] + } + + var keypathLayer: CALayer? { + nil + } + + /// Checks if the node's local contents need to be rebuilt. + func needsLocalUpdate(frame: CGFloat) -> Bool { + for property in properties { + if property.needsUpdate(frame: frame) { + return true + } + } + return false + } + + /// Rebuilds only the local nodes that have an update for the frame + func updateNodeProperties(frame: CGFloat) { + properties.forEach { property in + property.update(frame: frame) + } + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift new file mode 100644 index 00000000..bac72ab7 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift @@ -0,0 +1,47 @@ +// +// ValueContainer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +/// A container for a node value that is Typed to T. +class ValueContainer: AnyValueContainer { + + // MARK: Lifecycle + + init(_ value: T) { + outputValue = value + } + + // MARK: Internal + + private(set) var lastUpdateFrame = CGFloat.infinity + + fileprivate(set) var needsUpdate: Bool = true + + var value: Any { + outputValue as Any + } + + var outputValue: T { + didSet { + needsUpdate = false + } + } + + func setValue(_ value: Any, forFrame: CGFloat) { + if let typedValue = value as? T { + needsUpdate = false + lastUpdateFrame = forFrame + outputValue = typedValue + } + } + + func setNeedsUpdate() { + needsUpdate = true + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift new file mode 100644 index 00000000..1790cc2b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift @@ -0,0 +1,38 @@ +// +// KeyframeGroupInterpolator.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/22/19. +// + +import CoreGraphics +import Foundation + +/// A value provider that produces an array of values from an array of Keyframe Interpolators +final class GroupInterpolator: AnyValueProvider where ValueType: Interpolatable { + + // MARK: Lifecycle + + /// Initialize with an array of array of keyframes. + init(keyframeGroups: ContiguousArray>>) { + keyframeInterpolators = ContiguousArray(keyframeGroups.map({ KeyframeInterpolator(keyframes: $0) })) + } + + // MARK: Internal + + let keyframeInterpolators: ContiguousArray> + + var valueType: Any.Type { + [ValueType].self + } + + func hasUpdate(frame: CGFloat) -> Bool { + let updated = keyframeInterpolators.first(where: { $0.hasUpdate(frame: frame) }) + return updated != nil + } + + func value(frame: CGFloat) -> Any { + let output = keyframeInterpolators.map({ $0.value(frame: frame) as! ValueType }) + return output + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift new file mode 100644 index 00000000..5b974fbb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift @@ -0,0 +1,256 @@ +// +// KeyframeInterpolator.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/15/19. +// + +import CoreGraphics +import Foundation + +// MARK: - KeyframeInterpolator + +/// A value provider that produces a value at Time from a group of keyframes +final class KeyframeInterpolator: AnyValueProvider where ValueType: Interpolatable { + + // MARK: Lifecycle + + init(keyframes: ContiguousArray>) { + self.keyframes = keyframes + } + + // MARK: Internal + + let keyframes: ContiguousArray> + + var valueType: Any.Type { + ValueType.self + } + + /** + Returns true to trigger a frame update for this interpolator. + + An interpolator will be asked if it needs to update every frame. + If the interpolator needs updating it will be asked to compute its value for + the given frame. + + Cases a keyframe should not be updated: + - If time is in span and leading keyframe is hold + - If time is after the last keyframe. + - If time is before the first keyframe + + Cases for updating a keyframe: + - If time is in the span, and is not a hold + - If time is outside of the span, and there are more keyframes + - If a value delegate is set + - If leading and trailing are both nil. + */ + func hasUpdate(frame: CGFloat) -> Bool { + if lastUpdatedFrame == nil { + return true + } + + if + let leading = leadingKeyframe, + trailingKeyframe == nil, + leading.time < frame + { + /// Frame is after bounds of keyframes + return false + } + if + let trailing = trailingKeyframe, + leadingKeyframe == nil, + frame < trailing.time + { + /// Frame is before bounds of keyframes + return false + } + if + let leading = leadingKeyframe, + let trailing = trailingKeyframe, + leading.isHold, + leading.time < frame, + frame < trailing.time + { + return false + } + return true + } + + @discardableResult + func value(frame: CGFloat) -> Any { + // First set the keyframe span for the frame. + updateSpanIndices(frame: frame) + lastUpdatedFrame = frame + // If only one keyframe return its value + let progress: CGFloat + let value: ValueType + + if + let leading = leadingKeyframe, + let trailing = trailingKeyframe + { + /// We have leading and trailing keyframe. + progress = leading.interpolatedProgress(trailing, keyTime: frame) + value = leading.interpolate(trailing, progress: progress) + } else if let leading = leadingKeyframe { + progress = 0 + value = leading.value + } else if let trailing = trailingKeyframe { + progress = 1 + value = trailing.value + } else { + /// Satisfy the compiler. + progress = 0 + value = keyframes[0].value + } + return value + } + + // MARK: Fileprivate + + fileprivate var lastUpdatedFrame: CGFloat? + + fileprivate var leadingIndex: Int? = nil + fileprivate var trailingIndex: Int? = nil + fileprivate var leadingKeyframe: Keyframe? = nil + fileprivate var trailingKeyframe: Keyframe? = nil + + /// Finds the appropriate Leading and Trailing keyframe index for the given time. + fileprivate func updateSpanIndices(frame: CGFloat) { + guard keyframes.count > 0 else { + leadingIndex = nil + trailingIndex = nil + leadingKeyframe = nil + trailingKeyframe = nil + return + } + + /** + This function searches through the array to find the span of two keyframes + that contain the current time. + + We could use Array.first(where:) but that would search through the entire array + each frame. + Instead we track the last used index and search either forwards or + backwards from there. This reduces the iterations and complexity from + + O(n), where n is the length of the sequence to + O(n), where n is the number of items after or before the last used index. + + */ + + if keyframes.count == 1 { + /// Only one keyframe. Set it as first and move on. + leadingIndex = 0 + trailingIndex = nil + leadingKeyframe = keyframes[0] + trailingKeyframe = nil + return + } + + /// Sets the initial keyframes. This is often only needed for the first check. + if + leadingIndex == nil && + trailingIndex == nil + { + if frame < keyframes[0].time { + /// Time is before the first keyframe. Set it as the trailing. + trailingIndex = 0 + } else { + /// Time is after the first keyframe. Set the keyframe and the trailing. + leadingIndex = 0 + trailingIndex = 1 + } + } + + if + let currentTrailing = trailingIndex, + keyframes[currentTrailing].time <= frame + { + /// Time is after the current span. Iterate forward. + var newLeading = currentTrailing + var keyframeFound: Bool = false + while !keyframeFound { + + leadingIndex = newLeading + trailingIndex = keyframes.validIndex(newLeading + 1) + + guard let trailing = trailingIndex else { + /// We have reached the end of our keyframes. Time is after the last keyframe. + keyframeFound = true + continue + } + if frame < keyframes[trailing].time { + /// Keyframe in current span. + keyframeFound = true + continue + } + /// Advance the array. + newLeading = trailing + } + + } else if + let currentLeading = leadingIndex, + frame < keyframes[currentLeading].time + { + + /// Time is before the current span. Iterate backwards + var newTrailing = currentLeading + + var keyframeFound: Bool = false + while !keyframeFound { + + leadingIndex = keyframes.validIndex(newTrailing - 1) + trailingIndex = newTrailing + + guard let leading = leadingIndex else { + /// We have reached the end of our keyframes. Time is after the last keyframe. + keyframeFound = true + continue + } + if keyframes[leading].time <= frame { + /// Keyframe in current span. + keyframeFound = true + continue + } + /// Step back + newTrailing = leading + } + } + if let keyFrame = leadingIndex { + leadingKeyframe = keyframes[keyFrame] + } else { + leadingKeyframe = nil + } + + if let keyFrame = trailingIndex { + trailingKeyframe = keyframes[keyFrame] + } else { + trailingKeyframe = nil + } + } +} + +extension Array { + + fileprivate func validIndex(_ index: Int) -> Int? { + if 0 <= index, index < endIndex { + return index + } + return nil + } + +} + +extension ContiguousArray { + + fileprivate func validIndex(_ index: Int) -> Int? { + if 0 <= index, index < endIndex { + return index + } + return nil + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift new file mode 100644 index 00000000..45447492 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift @@ -0,0 +1,44 @@ +// +// SingleValueProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import Foundation +import QuartzCore + +/// Returns a value for every frame. +final class SingleValueProvider: AnyValueProvider { + + // MARK: Lifecycle + + init(_ value: ValueType) { + self.value = value + } + + // MARK: Internal + + var value: ValueType { + didSet { + hasUpdate = true + } + } + + var valueType: Any.Type { + ValueType.self + } + + func hasUpdate(frame _: CGFloat) -> Bool { + hasUpdate + } + + func value(frame _: CGFloat) -> Any { + hasUpdate = false + return value + } + + // MARK: Private + + private var hasUpdate: Bool = true +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift new file mode 100644 index 00000000..d6d2c580 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift @@ -0,0 +1,281 @@ +// +// TrimPathNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/23/19. +// + +import Foundation +import QuartzCore + +// MARK: - TrimPathProperties + +final class TrimPathProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(trim: Trim) { + keypathName = trim.name + start = NodeProperty(provider: KeyframeInterpolator(keyframes: trim.start.keyframes)) + end = NodeProperty(provider: KeyframeInterpolator(keyframes: trim.end.keyframes)) + offset = NodeProperty(provider: KeyframeInterpolator(keyframes: trim.offset.keyframes)) + type = trim.trimType + keypathProperties = [ + "Start" : start, + "End" : end, + "Offset" : offset, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + let keypathName: String + + let start: NodeProperty + let end: NodeProperty + let offset: NodeProperty + let type: TrimType +} + +// MARK: - TrimPathNode + +final class TrimPathNode: AnimatorNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, trim: Trim, upstreamPaths: [PathOutputNode]) { + outputNode = PassThroughOutputNode(parent: parentNode?.outputNode) + self.parentNode = parentNode + properties = TrimPathProperties(trim: trim) + self.upstreamPaths = upstreamPaths + } + + // MARK: Internal + + let properties: TrimPathProperties + + let parentNode: AnimatorNode? + let outputNode: NodeOutput + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + var isEnabled: Bool = true + + // MARK: Animator Node + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + func forceUpstreamOutputUpdates() -> Bool { + hasLocalUpdates || hasUpstreamUpdates + } + + func rebuildOutputs(frame: CGFloat) { + /// Make sure there is a trim. + let startValue = properties.start.value.cgFloatValue * 0.01 + let endValue = properties.end.value.cgFloatValue * 0.01 + let start = min(startValue, endValue) + let end = max(startValue, endValue) + + let offset = properties.offset.value.cgFloatValue.truncatingRemainder(dividingBy: 360) / 360 + + /// No need to trim, it's a full path + if start == 0, end == 1 { + return + } + + /// All paths are empty. + if start == end { + for pathContainer in upstreamPaths { + pathContainer.removePaths(updateFrame: frame) + } + return + } + + if properties.type == .simultaneously { + /// Just trim each path + for pathContainer in upstreamPaths { + let pathObjects = pathContainer.removePaths(updateFrame: frame) + for path in pathObjects { + // We are treating each compount path as an individual path. Its subpaths are treated as a whole. + pathContainer.appendPath( + path.trim(fromPosition: start, toPosition: end, offset: offset, trimSimultaneously: false), + updateFrame: frame) + } + } + return + } + + /// Individual path trimming. + + /// Brace yourself for the below code. + + /// Normalize lengths with offset. + var startPosition = (start + offset).truncatingRemainder(dividingBy: 1) + var endPosition = (end + offset).truncatingRemainder(dividingBy: 1) + + if startPosition < 0 { + startPosition = 1 + startPosition + } + + if endPosition < 0 { + endPosition = 1 + endPosition + } + if startPosition == 1 { + startPosition = 0 + } + if endPosition == 0 { + endPosition = 1 + } + + /// First get the total length of all paths. + var totalLength: CGFloat = 0 + upstreamPaths.forEach({ totalLength = totalLength + $0.totalLength }) + + /// Now determine the start and end cut lengths + let startLength = startPosition * totalLength + let endLength = endPosition * totalLength + var pathStart: CGFloat = 0 + + /// Now loop through all path containers + for pathContainer in upstreamPaths { + + let pathEnd = pathStart + pathContainer.totalLength + + if + !startLength.isInRange(pathStart, pathEnd) && + endLength.isInRange(pathStart, pathEnd) + { + // pathStart|=======E----------------------|pathEnd + // Cut path components, removing after end. + + let pathCutLength = endLength - pathStart + let subpaths = pathContainer.removePaths(updateFrame: frame) + var subpathStart: CGFloat = 0 + for path in subpaths { + let subpathEnd = subpathStart + path.length + if pathCutLength < subpathEnd { + /// This is the subpath that needs to be cut. + let cutLength = pathCutLength - subpathStart + let newPath = path.trim(fromPosition: 0, toPosition: cutLength / path.length, offset: 0, trimSimultaneously: false) + pathContainer.appendPath(newPath, updateFrame: frame) + break + } else { + /// Add to container and move on + pathContainer.appendPath(path, updateFrame: frame) + } + if pathCutLength == subpathEnd { + /// Right on the end. The next subpath is not included. Break. + break + } + subpathStart = subpathEnd + } + + } else if + !endLength.isInRange(pathStart, pathEnd) && + startLength.isInRange(pathStart, pathEnd) + { + // pathStart|-------S======================|pathEnd + // + + // Cut path components, removing before beginning. + let pathCutLength = startLength - pathStart + // Clear paths from container + let subpaths = pathContainer.removePaths(updateFrame: frame) + var subpathStart: CGFloat = 0 + for path in subpaths { + let subpathEnd = subpathStart + path.length + + if subpathStart < pathCutLength, pathCutLength < subpathEnd { + /// This is the subpath that needs to be cut. + let cutLength = pathCutLength - subpathStart + let newPath = path.trim(fromPosition: cutLength / path.length, toPosition: 1, offset: 0, trimSimultaneously: false) + pathContainer.appendPath(newPath, updateFrame: frame) + } else if pathCutLength <= subpathStart { + pathContainer.appendPath(path, updateFrame: frame) + } + subpathStart = subpathEnd + } + } else if + endLength.isInRange(pathStart, pathEnd) && + startLength.isInRange(pathStart, pathEnd) + { + // pathStart|-------S============E---------|endLength + // pathStart|=====E----------------S=======|endLength + // trim from path beginning to endLength. + + // Cut path components, removing before beginnings. + let startCutLength = startLength - pathStart + let endCutLength = endLength - pathStart + // Clear paths from container + let subpaths = pathContainer.removePaths(updateFrame: frame) + var subpathStart: CGFloat = 0 + for path in subpaths { + + let subpathEnd = subpathStart + path.length + + if + !startCutLength.isInRange(subpathStart, subpathEnd) && + !endCutLength.isInRange(subpathStart, subpathEnd) + { + // The whole path is included. Add + // S|==============================|E + pathContainer.appendPath(path, updateFrame: frame) + + } else if + startCutLength.isInRange(subpathStart, subpathEnd) && + !endCutLength.isInRange(subpathStart, subpathEnd) + { + /// The start of the path needs to be trimmed + // |-------S======================|E + let cutLength = startCutLength - subpathStart + let newPath = path.trim(fromPosition: cutLength / path.length, toPosition: 1, offset: 0, trimSimultaneously: false) + pathContainer.appendPath(newPath, updateFrame: frame) + } else if + !startCutLength.isInRange(subpathStart, subpathEnd) && + endCutLength.isInRange(subpathStart, subpathEnd) + { + // S|=======E----------------------| + let cutLength = endCutLength - subpathStart + let newPath = path.trim(fromPosition: 0, toPosition: cutLength / path.length, offset: 0, trimSimultaneously: false) + pathContainer.appendPath(newPath, updateFrame: frame) + break + } else if + startCutLength.isInRange(subpathStart, subpathEnd) && + endCutLength.isInRange(subpathStart, subpathEnd) + { + // |-------S============E---------| + let cutFromLength = startCutLength - subpathStart + let cutToLength = endCutLength - subpathStart + let newPath = path.trim( + fromPosition: cutFromLength / path.length, + toPosition: cutToLength / path.length, + offset: 0, + trimSimultaneously: false) + pathContainer.appendPath(newPath, updateFrame: frame) + break + } + + subpathStart = subpathEnd + } + } else if + (endLength <= pathStart && pathEnd <= startLength) || + (startLength <= pathStart && endLength <= pathStart) || + (pathEnd <= startLength && pathEnd <= endLength) + { + /// The Path needs to be cleared + pathContainer.removePaths(updateFrame: frame) + } + + pathStart = pathEnd + } + + } + + // MARK: Fileprivate + + fileprivate let upstreamPaths: [PathOutputNode] +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift new file mode 100644 index 00000000..c44ef41f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift @@ -0,0 +1,76 @@ +// +// TransformNodeOutput.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +class GroupOutputNode: NodeOutput { + + // MARK: Lifecycle + + init(parent: NodeOutput?, rootNode: NodeOutput?) { + self.parent = parent + self.rootNode = rootNode + } + + // MARK: Internal + + let parent: NodeOutput? + let rootNode: NodeOutput? + var isEnabled: Bool = true + + private(set) var outputPath: CGPath? = nil + private(set) var transform: CATransform3D = CATransform3DIdentity + + func setTransform(_ xform: CATransform3D, forFrame _: CGFloat) { + transform = xform + outputPath = nil + } + + func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { + guard isEnabled else { + let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false + outputPath = parent?.outputPath + return upstreamUpdates + } + + let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false + if upstreamUpdates { + outputPath = nil + } + let rootUpdates = rootNode?.hasOutputUpdates(forFrame) ?? false + if rootUpdates { + outputPath = nil + } + + var localUpdates: Bool = false + if outputPath == nil { + localUpdates = true + + let newPath = CGMutablePath() + if let parentNode = parent, let parentPath = parentNode.outputPath { + /// First add parent path. + newPath.addPath(parentPath) + } + var xform = CATransform3DGetAffineTransform(transform) + if + let rootNode = rootNode, + let rootPath = rootNode.outputPath, + let xformedPath = rootPath.copy(using: &xform) + { + /// Now add root path. Note root path is transformed. + newPath.addPath(xformedPath) + } + + outputPath = newPath + } + + return upstreamUpdates || localUpdates + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift new file mode 100644 index 00000000..332a2d96 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift @@ -0,0 +1,47 @@ +// +// PassThroughOutputNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +class PassThroughOutputNode: NodeOutput { + + // MARK: Lifecycle + + init(parent: NodeOutput?) { + self.parent = parent + } + + // MARK: Internal + + let parent: NodeOutput? + + var hasUpdate: Bool = false + var isEnabled: Bool = true + + var outputPath: CGPath? { + if let parent = parent { + return parent.outputPath + } + return nil + } + + func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { + /// Changes to this node do not affect downstream nodes. + let parentUpdate = parent?.hasOutputUpdates(forFrame) ?? false + /// Changes to upstream nodes do, however, affect this nodes state. + hasUpdate = hasUpdate || parentUpdate + return parentUpdate + } + + func hasRenderUpdates(_ forFrame: CGFloat) -> Bool { + /// Return true if there are upstream updates or if this node has updates + let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false + hasUpdate = hasUpdate || upstreamUpdates + return hasUpdate + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift new file mode 100644 index 00000000..5ddb0058 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift @@ -0,0 +1,90 @@ +// +// PathNodeOutput.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +/// A node that has an output of a BezierPath +class PathOutputNode: NodeOutput { + + // MARK: Lifecycle + + init(parent: NodeOutput?) { + self.parent = parent + } + + // MARK: Internal + + let parent: NodeOutput? + + fileprivate(set) var outputPath: CGPath? = nil + + var lastUpdateFrame: CGFloat? = nil + var lastPathBuildFrame: CGFloat? = nil + var isEnabled: Bool = true + fileprivate(set) var totalLength: CGFloat = 0 + fileprivate(set) var pathObjects: [CompoundBezierPath] = [] + + func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { + guard isEnabled else { + let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false + outputPath = parent?.outputPath + return upstreamUpdates + } + + /// Ask if parent was updated + let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false + + /// If parent was updated and the path hasn't been built for this frame, clear the path. + if upstreamUpdates && lastPathBuildFrame != forFrame { + outputPath = nil + } + + if outputPath == nil { + /// If the path is clear, build the new path. + lastPathBuildFrame = forFrame + let newPath = CGMutablePath() + if let parentNode = parent, let parentPath = parentNode.outputPath { + newPath.addPath(parentPath) + } + for path in pathObjects { + for subPath in path.paths { + newPath.addPath(subPath.cgPath()) + } + } + outputPath = newPath + } + + /// Return true if there were upstream updates or if this node was updated. + return upstreamUpdates || (lastUpdateFrame == forFrame) + } + + @discardableResult + func removePaths(updateFrame: CGFloat?) -> [CompoundBezierPath] { + lastUpdateFrame = updateFrame + let returnPaths = pathObjects + outputPath = nil + totalLength = 0 + pathObjects = [] + return returnPaths + } + + func setPath(_ path: BezierPath, updateFrame: CGFloat) { + lastUpdateFrame = updateFrame + outputPath = nil + totalLength = path.length + pathObjects = [CompoundBezierPath(path: path)] + } + + func appendPath(_ path: CompoundBezierPath, updateFrame: CGFloat) { + lastUpdateFrame = updateFrame + outputPath = nil + totalLength = totalLength + path.length + pathObjects.append(path) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift new file mode 100644 index 00000000..0ddcafaa --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift @@ -0,0 +1,74 @@ +// +// FillRenderer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +extension FillRule { + var cgFillRule: CGPathFillRule { + switch self { + case .evenOdd: + return .evenOdd + default: + return .winding + } + } + + var caFillRule: CAShapeLayerFillRule { + switch self { + case .evenOdd: + return CAShapeLayerFillRule.evenOdd + default: + return CAShapeLayerFillRule.nonZero + } + } +} + +// MARK: - FillRenderer + +/// A rendered for a Path Fill +final class FillRenderer: PassThroughOutputNode, Renderable { + + let shouldRenderInContext: Bool = false + + var color: CGColor? { + didSet { + hasUpdate = true + } + } + + var opacity: CGFloat = 0 { + didSet { + hasUpdate = true + } + } + + var fillRule: FillRule = .none { + didSet { + hasUpdate = true + } + } + + func updateShapeLayer(layer: CAShapeLayer) { + layer.fillColor = color + layer.opacity = Float(opacity) + layer.fillRule = fillRule.caFillRule + hasUpdate = false + } + + func render(_ inContext: CGContext) { + guard inContext.path != nil && inContext.path!.isEmpty == false else { + return + } + guard let color = color else { return } + hasUpdate = false + inContext.setAlpha(opacity * 0.01) + inContext.setFillColor(color) + inContext.fillPath(using: fillRule.cgFillRule) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift new file mode 100644 index 00000000..f1f317cb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift @@ -0,0 +1,149 @@ +// +// GradientFillRenderer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import Foundation +import QuartzCore + +/// A rendered for a Path Fill +final class GradientFillRenderer: PassThroughOutputNode, Renderable { + + var shouldRenderInContext: Bool = true + + var start: CGPoint = .zero { + didSet { + hasUpdate = true + } + } + + var numberOfColors: Int = 0 { + didSet { + hasUpdate = true + } + } + + var colors: [CGFloat] = [] { + didSet { + hasUpdate = true + } + } + + var end: CGPoint = .zero { + didSet { + hasUpdate = true + } + } + + var opacity: CGFloat = 0 { + didSet { + hasUpdate = true + } + } + + var type: GradientType = .none { + didSet { + hasUpdate = true + } + } + + func updateShapeLayer(layer _: CAShapeLayer) { + // Not applicable + } + + func render(_ inContext: CGContext) { + guard inContext.path != nil && inContext.path!.isEmpty == false else { + return + } + hasUpdate = false + var alphaColors = [CGColor]() + var alphaLocations = [CGFloat]() + + var gradientColors = [CGColor]() + var colorLocations = [CGFloat]() + let colorSpace = CGColorSpaceCreateDeviceRGB() + let maskColorSpace = CGColorSpaceCreateDeviceGray() + for i in 0.. ix, let color = CGColor( + colorSpace: colorSpace, + components: [colors[ix + 1], colors[ix + 2], colors[ix + 3], 1]) + { + gradientColors.append(color) + colorLocations.append(colors[ix]) + } + } + + var drawMask = false + for i in stride(from: numberOfColors * 4, to: colors.endIndex, by: 2) { + let alpha = colors[i + 1] + if alpha < 1 { + drawMask = true + } + if let color = CGColor(colorSpace: maskColorSpace, components: [alpha, 1]) { + alphaLocations.append(colors[i]) + alphaColors.append(color) + } + } + + inContext.setAlpha(opacity) + inContext.clip() + + /// First draw a mask is necessary. + if drawMask { + guard + let maskGradient = CGGradient( + colorsSpace: maskColorSpace, + colors: alphaColors as CFArray, + locations: alphaLocations), + let maskContext = CGContext( + data: nil, + width: inContext.width, + height: inContext.height, + bitsPerComponent: 8, + bytesPerRow: inContext.width, + space: maskColorSpace, + bitmapInfo: 0) else { return } + let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(maskContext.height)) + maskContext.concatenate(flipVertical) + maskContext.concatenate(inContext.ctm) + if type == .linear { + maskContext.drawLinearGradient( + maskGradient, + start: start, + end: end, + options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) + } else { + maskContext.drawRadialGradient( + maskGradient, + startCenter: start, + startRadius: 0, + endCenter: start, + endRadius: start.distanceTo(end), + options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) + } + /// Clips the gradient + if let alphaMask = maskContext.makeImage() { + inContext.clip(to: inContext.boundingBoxOfClipPath, mask: alphaMask) + } + } + + /// Now draw the gradient + guard let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors as CFArray, locations: colorLocations) + else { return } + if type == .linear { + inContext.drawLinearGradient(gradient, start: start, end: end, options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) + } else { + inContext.drawRadialGradient( + gradient, + startCenter: start, + startRadius: 0, + endCenter: start, + endRadius: start.distanceTo(end), + options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift new file mode 100644 index 00000000..9e1a0ccb --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift @@ -0,0 +1,62 @@ +// +// GradientStrokeRenderer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import Foundation +import QuartzCore + +// MARK: - Renderer + +final class GradientStrokeRenderer: PassThroughOutputNode, Renderable { + + // MARK: Lifecycle + + override init(parent: NodeOutput?) { + strokeRender = StrokeRenderer(parent: nil) + gradientRender = GradientFillRenderer(parent: nil) + strokeRender.color = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 1, 1, 1]) + super.init(parent: parent) + } + + // MARK: Internal + + var shouldRenderInContext: Bool = true + + let strokeRender: StrokeRenderer + let gradientRender: GradientFillRenderer + + override func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { + let updates = super.hasOutputUpdates(forFrame) + return updates || strokeRender.hasUpdate || gradientRender.hasUpdate + } + + func updateShapeLayer(layer _: CAShapeLayer) { + /// Not Applicable + } + + func render(_ inContext: CGContext) { + guard inContext.path != nil && inContext.path!.isEmpty == false else { + return + } + + strokeRender.hasUpdate = false + hasUpdate = false + gradientRender.hasUpdate = false + + strokeRender.setupForStroke(inContext) + + inContext.replacePathWithStrokedPath() + + /// Now draw the gradient. + gradientRender.render(inContext) + + } + + func renderBoundsFor(_ boundingBox: CGRect) -> CGRect { + strokeRender.renderBoundsFor(boundingBox) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift new file mode 100644 index 00000000..b283cb67 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift @@ -0,0 +1,162 @@ +// +// StrokeRenderer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import Foundation +import QuartzCore + +extension LineJoin { + var cgLineJoin: CGLineJoin { + switch self { + case .bevel: + return .bevel + case .none: + return .miter + case .miter: + return .miter + case .round: + return .round + } + } + + var caLineJoin: CAShapeLayerLineJoin { + switch self { + case .none: + return CAShapeLayerLineJoin.miter + case .miter: + return CAShapeLayerLineJoin.miter + case .round: + return CAShapeLayerLineJoin.round + case .bevel: + return CAShapeLayerLineJoin.bevel + } + } +} + +extension LineCap { + var cgLineCap: CGLineCap { + switch self { + case .none: + return .butt + case .butt: + return .butt + case .round: + return .round + case .square: + return .square + } + } + + var caLineCap: CAShapeLayerLineCap { + switch self { + case .none: + return CAShapeLayerLineCap.butt + case .butt: + return CAShapeLayerLineCap.butt + case .round: + return CAShapeLayerLineCap.round + case .square: + return CAShapeLayerLineCap.square + } + } +} + +// MARK: - StrokeRenderer + +/// A rendered that renders a stroke on a path. +final class StrokeRenderer: PassThroughOutputNode, Renderable { + + var shouldRenderInContext: Bool = false + + var color: CGColor? { + didSet { + hasUpdate = true + } + } + + var opacity: CGFloat = 0 { + didSet { + hasUpdate = true + } + } + + var width: CGFloat = 0 { + didSet { + hasUpdate = true + } + } + + var miterLimit: CGFloat = 0 { + didSet { + hasUpdate = true + } + } + + var lineCap: LineCap = .none { + didSet { + hasUpdate = true + } + } + + var lineJoin: LineJoin = .none { + didSet { + hasUpdate = true + } + } + + var dashPhase: CGFloat? { + didSet { + hasUpdate = true + } + } + + var dashLengths: [CGFloat]? { + didSet { + hasUpdate = true + } + } + + func renderBoundsFor(_ boundingBox: CGRect) -> CGRect { + boundingBox.insetBy(dx: -width, dy: -width) + } + + func setupForStroke(_ inContext: CGContext) { + inContext.setLineWidth(width) + inContext.setMiterLimit(miterLimit) + inContext.setLineCap(lineCap.cgLineCap) + inContext.setLineJoin(lineJoin.cgLineJoin) + if let dashPhase = dashPhase, let lengths = dashLengths { + inContext.setLineDash(phase: dashPhase, lengths: lengths) + } else { + inContext.setLineDash(phase: 0, lengths: []) + } + } + + func render(_ inContext: CGContext) { + guard inContext.path != nil && inContext.path!.isEmpty == false else { + return + } + guard let color = color else { return } + hasUpdate = false + setupForStroke(inContext) + inContext.setAlpha(opacity) + inContext.setStrokeColor(color) + inContext.strokePath() + } + + func updateShapeLayer(layer: CAShapeLayer) { + layer.strokeColor = color + layer.opacity = Float(opacity) + layer.lineWidth = width + layer.lineJoin = lineJoin.caLineJoin + layer.lineCap = lineCap.caLineCap + layer.lineDashPhase = dashPhase ?? 0 + layer.fillColor = nil + if let dashPattern = dashLengths { + layer.lineDashPattern = dashPattern.map({ NSNumber(value: Double($0)) }) + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift new file mode 100644 index 00000000..64b2226b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift @@ -0,0 +1,126 @@ +// +// EllipseNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/17/19. +// + +import Foundation +import QuartzCore + +// MARK: - EllipseNodeProperties + +final class EllipseNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(ellipse: Ellipse) { + keypathName = ellipse.name + direction = ellipse.direction + position = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.position.keyframes)) + size = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.size.keyframes)) + keypathProperties = [ + "Position" : position, + "Size" : size, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + let direction: PathDirection + let position: NodeProperty + let size: NodeProperty + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] +} + +// MARK: - EllipseNode + +final class EllipseNode: AnimatorNode, PathNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, ellipse: Ellipse) { + pathOutput = PathOutputNode(parent: parentNode?.outputNode) + properties = EllipseNodeProperties(ellipse: ellipse) + self.parentNode = parentNode + } + + // MARK: Internal + + static let ControlPointConstant: CGFloat = 0.55228 + + let pathOutput: PathOutputNode + + let properties: EllipseNodeProperties + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + // MARK: Animator Node + + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + var isEnabled: Bool = true { + didSet{ + pathOutput.isEnabled = isEnabled + } + } + + func rebuildOutputs(frame: CGFloat) { + let ellipseSize = properties.size.value.sizeValue + let center = properties.position.value.pointValue + + // Unfortunately we HAVE to manually build out the ellipse. + // Every Apple method constructs an ellipse from the 3 o-clock position + // After effects constructs from the Noon position. + // After effects does clockwise, but also has a flag for reversed. + + var half = ellipseSize * 0.5 + if properties.direction == .counterClockwise { + half.width = half.width * -1 + } + + let q1 = CGPoint(x: center.x, y: center.y - half.height) + let q2 = CGPoint(x: center.x + half.width, y: center.y) + let q3 = CGPoint(x: center.x, y: center.y + half.height) + let q4 = CGPoint(x: center.x - half.width, y: center.y) + + let cp = half * EllipseNode.ControlPointConstant + + var path = BezierPath(startPoint: CurveVertex( + point: q1, + inTangentRelative: CGPoint(x: -cp.width, y: 0), + outTangentRelative: CGPoint(x: cp.width, y: 0))) + path.addVertex(CurveVertex( + point: q2, + inTangentRelative: CGPoint(x: 0, y: -cp.height), + outTangentRelative: CGPoint(x: 0, y: cp.height))) + + path.addVertex(CurveVertex( + point: q3, + inTangentRelative: CGPoint(x: cp.width, y: 0), + outTangentRelative: CGPoint(x: -cp.width, y: 0))) + + path.addVertex(CurveVertex( + point: q4, + inTangentRelative: CGPoint(x: 0, y: cp.height), + outTangentRelative: CGPoint(x: 0, y: -cp.height))) + + path.addVertex(CurveVertex( + point: q1, + inTangentRelative: CGPoint(x: -cp.width, y: 0), + outTangentRelative: CGPoint(x: cp.width, y: 0))) + path.close() + pathOutput.setPath(path, updateFrame: frame) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift new file mode 100644 index 00000000..dc2d94cd --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift @@ -0,0 +1,152 @@ +// +// PolygonNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/21/19. +// + +import Foundation +import QuartzCore + +// MARK: - PolygonNodeProperties + +final class PolygonNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(star: Star) { + keypathName = star.name + direction = star.direction + position = NodeProperty(provider: KeyframeInterpolator(keyframes: star.position.keyframes)) + outerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRadius.keyframes)) + outerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRoundness.keyframes)) + rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes)) + points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes)) + keypathProperties = [ + "Position" : position, + "Outer Radius" : outerRadius, + "Outer Roundedness" : outerRoundedness, + "Rotation" : rotation, + "Points" : points, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + var childKeypaths: [KeypathSearchable] = [] + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + + let direction: PathDirection + let position: NodeProperty + let outerRadius: NodeProperty + let outerRoundedness: NodeProperty + let rotation: NodeProperty + let points: NodeProperty +} + +// MARK: - PolygonNode + +final class PolygonNode: AnimatorNode, PathNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, star: Star) { + pathOutput = PathOutputNode(parent: parentNode?.outputNode) + properties = PolygonNodeProperties(star: star) + self.parentNode = parentNode + } + + // MARK: Internal + + /// Magic number needed for constructing path. + static let PolygonConstant: CGFloat = 0.25 + + let properties: PolygonNodeProperties + + let pathOutput: PathOutputNode + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + // MARK: Animator Node + + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + var isEnabled: Bool = true { + didSet{ + pathOutput.isEnabled = isEnabled + } + } + + func rebuildOutputs(frame: CGFloat) { + let outerRadius = properties.outerRadius.value.cgFloatValue + let outerRoundedness = properties.outerRoundedness.value.cgFloatValue * 0.01 + let numberOfPoints = properties.points.value.cgFloatValue + let rotation = properties.rotation.value.cgFloatValue + let position = properties.position.value.pointValue + + var currentAngle = (rotation - 90).toRadians() + let anglePerPoint = ((2 * CGFloat.pi) / numberOfPoints) + + var point = CGPoint( + x: outerRadius * cos(currentAngle), + y: outerRadius * sin(currentAngle)) + var vertices = [CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero)] + + var previousPoint = point + currentAngle += anglePerPoint; + for _ in 0.. + let size: NodeProperty + let cornerRadius: NodeProperty + +} + +// MARK: - RectangleNode + +final class RectangleNode: AnimatorNode, PathNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, rectangle: Rectangle) { + properties = RectNodeProperties(rectangle: rectangle) + pathOutput = PathOutputNode(parent: parentNode?.outputNode) + self.parentNode = parentNode + } + + // MARK: Internal + + let properties: RectNodeProperties + + let pathOutput: PathOutputNode + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + // MARK: Animator Node + + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + var isEnabled: Bool = true { + didSet{ + pathOutput.isEnabled = isEnabled + } + } + + func rebuildOutputs(frame: CGFloat) { + + let size = properties.size.value.sizeValue * 0.5 + let radius = min(min(properties.cornerRadius.value.cgFloatValue, size.width) , size.height) + let position = properties.position.value.pointValue + var bezierPath = BezierPath() + let points: [CurveVertex] + + if radius <= 0 { + /// No Corners + points = [ + /// Lead In + CurveVertex( + point: CGPoint(x: size.width, y: -size.height), + inTangentRelative: .zero, + outTangentRelative: .zero) + .translated(position), + /// Corner 1 + CurveVertex( + point: CGPoint(x: size.width, y: size.height), + inTangentRelative: .zero, + outTangentRelative: .zero) + .translated(position), + /// Corner 2 + CurveVertex( + point: CGPoint(x: -size.width, y: size.height), + inTangentRelative: .zero, + outTangentRelative: .zero) + .translated(position), + /// Corner 3 + CurveVertex( + point: CGPoint(x: -size.width, y: -size.height), + inTangentRelative: .zero, + outTangentRelative: .zero) + .translated(position), + /// Corner 4 + CurveVertex( + point: CGPoint(x: size.width, y: -size.height), + inTangentRelative: .zero, + outTangentRelative: .zero) + .translated(position), + ] + } else { + let controlPoint = radius * EllipseNode.ControlPointConstant + points = [ + /// Lead In + CurveVertex( + CGPoint(x: radius, y: 0), + CGPoint(x: radius, y: 0), + CGPoint(x: radius, y: 0)) + .translated(CGPoint(x: -radius, y: radius)) + .translated(CGPoint(x: size.width, y: -size.height)) + .translated(position), + /// Corner 1 + CurveVertex( + CGPoint(x: radius, y: 0), // In tangent + CGPoint(x: radius, y: 0), // Point + CGPoint(x: radius, y: controlPoint)) + .translated(CGPoint(x: -radius, y: -radius)) + .translated(CGPoint(x: size.width, y: size.height)) + .translated(position), + CurveVertex( + CGPoint(x: controlPoint, y: radius), // In tangent + CGPoint(x: 0, y: radius), // Point + CGPoint(x: 0, y: radius)) // Out Tangent + .translated(CGPoint(x: -radius, y: -radius)) + .translated(CGPoint(x: size.width, y: size.height)) + .translated(position), + /// Corner 2 + CurveVertex( + CGPoint(x: 0, y: radius), // In tangent + CGPoint(x: 0, y: radius), // Point + CGPoint(x: -controlPoint, y: radius))// Out tangent + .translated(CGPoint(x: radius, y: -radius)) + .translated(CGPoint(x: -size.width, y: size.height)) + .translated(position), + CurveVertex( + CGPoint(x: -radius, y: controlPoint), // In tangent + CGPoint(x: -radius, y: 0), // Point + CGPoint(x: -radius, y: 0)) // Out tangent + .translated(CGPoint(x: radius, y: -radius)) + .translated(CGPoint(x: -size.width, y: size.height)) + .translated(position), + /// Corner 3 + CurveVertex( + CGPoint(x: -radius, y: 0), // In tangent + CGPoint(x: -radius, y: 0), // Point + CGPoint(x: -radius, y: -controlPoint)) // Out tangent + .translated(CGPoint(x: radius, y: radius)) + .translated(CGPoint(x: -size.width, y: -size.height)) + .translated(position), + CurveVertex( + CGPoint(x: -controlPoint, y: -radius), // In tangent + CGPoint(x: 0, y: -radius), // Point + CGPoint(x: 0, y: -radius)) // Out tangent + .translated(CGPoint(x: radius, y: radius)) + .translated(CGPoint(x: -size.width, y: -size.height)) + .translated(position), + /// Corner 4 + CurveVertex( + CGPoint(x: 0, y: -radius), // In tangent + CGPoint(x: 0, y: -radius), // Point + CGPoint(x: controlPoint, y: -radius)) // Out tangent + .translated(CGPoint(x: -radius, y: radius)) + .translated(CGPoint(x: size.width, y: -size.height)) + .translated(position), + CurveVertex( + CGPoint(x: radius, y: -controlPoint), // In tangent + CGPoint(x: radius, y: 0), // Point + CGPoint(x: radius, y: 0)) // Out tangent + .translated(CGPoint(x: -radius, y: radius)) + .translated(CGPoint(x: size.width, y: -size.height)) + .translated(position), + ] + } + let reversed = properties.direction == .counterClockwise + let pathPoints = reversed ? points.reversed() : points + for point in pathPoints { + bezierPath.addVertex(reversed ? point.reversed() : point) + } + bezierPath.close() + pathOutput.setPath(bezierPath, updateFrame: frame) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift new file mode 100644 index 00000000..f807bf88 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift @@ -0,0 +1,74 @@ +// +// PathNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/16/19. +// + +import CoreGraphics +import Foundation + +// MARK: - ShapeNodeProperties + +final class ShapeNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(shape: Shape) { + keypathName = shape.name + path = NodeProperty(provider: KeyframeInterpolator(keyframes: shape.path.keyframes)) + keypathProperties = [ + "Path" : path, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + let path: NodeProperty + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + +} + +// MARK: - ShapeNode + +final class ShapeNode: AnimatorNode, PathNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, shape: Shape) { + pathOutput = PathOutputNode(parent: parentNode?.outputNode) + properties = ShapeNodeProperties(shape: shape) + self.parentNode = parentNode + } + + // MARK: Internal + + let properties: ShapeNodeProperties + + let pathOutput: PathOutputNode + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + // MARK: Animator Node + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + var isEnabled: Bool = true { + didSet{ + pathOutput.isEnabled = isEnabled + } + } + + func rebuildOutputs(frame: CGFloat) { + pathOutput.setPath(properties.path.value, updateFrame: frame) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift new file mode 100644 index 00000000..61dd394d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift @@ -0,0 +1,201 @@ +// +// StarNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/21/19. +// + +import Foundation +import QuartzCore + +// MARK: - StarNodeProperties + +final class StarNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(star: Star) { + keypathName = star.name + direction = star.direction + position = NodeProperty(provider: KeyframeInterpolator(keyframes: star.position.keyframes)) + outerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRadius.keyframes)) + outerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRoundness.keyframes)) + if let innerRadiusKeyframes = star.innerRadius?.keyframes { + innerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: innerRadiusKeyframes)) + } else { + innerRadius = NodeProperty(provider: SingleValueProvider(Vector1D(0))) + } + if let innderRoundedness = star.innerRoundness?.keyframes { + innerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: innderRoundedness)) + } else { + innerRoundedness = NodeProperty(provider: SingleValueProvider(Vector1D(0))) + } + rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes)) + points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes)) + keypathProperties = [ + "Position" : position, + "Outer Radius" : outerRadius, + "Outer Roundedness" : outerRoundedness, + "Inner Radius" : innerRadius, + "Inner Roundedness" : innerRoundedness, + "Rotation" : rotation, + "Points" : points, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + + let direction: PathDirection + let position: NodeProperty + let outerRadius: NodeProperty + let outerRoundedness: NodeProperty + let innerRadius: NodeProperty + let innerRoundedness: NodeProperty + let rotation: NodeProperty + let points: NodeProperty +} + +// MARK: - StarNode + +final class StarNode: AnimatorNode, PathNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, star: Star) { + pathOutput = PathOutputNode(parent: parentNode?.outputNode) + properties = StarNodeProperties(star: star) + self.parentNode = parentNode + } + + // MARK: Internal + + /// Magic number needed for building path data + static let PolystarConstant: CGFloat = 0.47829 + + let properties: StarNodeProperties + + let pathOutput: PathOutputNode + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + // MARK: Animator Node + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + var isEnabled: Bool = true { + didSet{ + pathOutput.isEnabled = isEnabled + } + } + + func rebuildOutputs(frame: CGFloat) { + let outerRadius = properties.outerRadius.value.cgFloatValue + let innerRadius = properties.innerRadius.value.cgFloatValue + let outerRoundedness = properties.outerRoundedness.value.cgFloatValue * 0.01 + let innerRoundedness = properties.innerRoundedness.value.cgFloatValue * 0.01 + let numberOfPoints = properties.points.value.cgFloatValue + let rotation = properties.rotation.value.cgFloatValue + let position = properties.position.value.pointValue + + var currentAngle = (rotation - 90).toRadians() + let anglePerPoint = (2 * CGFloat.pi) / numberOfPoints + let halfAnglePerPoint = anglePerPoint / 2.0 + let partialPointAmount = numberOfPoints - floor(numberOfPoints) + + var point: CGPoint = .zero + + var partialPointRadius: CGFloat = 0 + if partialPointAmount != 0 { + currentAngle += halfAnglePerPoint * (1 - partialPointAmount) + partialPointRadius = innerRadius + partialPointAmount * (outerRadius - innerRadius) + point.x = (partialPointRadius * cos(currentAngle)) + point.y = (partialPointRadius * sin(currentAngle)) + currentAngle += anglePerPoint * partialPointAmount / 2 + } else { + point.x = (outerRadius * cos(currentAngle)) + point.y = (outerRadius * sin(currentAngle)) + currentAngle += halfAnglePerPoint + } + + var vertices = [CurveVertex]() + vertices.append(CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero)) + + var previousPoint = point + var longSegment = false + let numPoints = Int(ceil(numberOfPoints) * 2) + for i in 0.. + let position: NodeProperty + let scale: NodeProperty + let rotation: NodeProperty + let opacity: NodeProperty + let skew: NodeProperty + let skewAxis: NodeProperty + + var caTransform: CATransform3D { + CATransform3D.makeTransform( + anchor: anchor.value.pointValue, + position: position.value.pointValue, + scale: scale.value.sizeValue, + rotation: rotation.value.cgFloatValue, + skew: skew.value.cgFloatValue, + skewAxis: skewAxis.value.cgFloatValue) + } +} + +// MARK: - GroupNode + +final class GroupNode: AnimatorNode { + + // MARK: Lifecycle + + // MARK: Initializer + init(name: String, parentNode: AnimatorNode?, tree: NodeTree) { + self.parentNode = parentNode + keypathName = name + rootNode = tree.rootNode + properties = GroupNodeProperties(transform: tree.transform) + groupOutput = GroupOutputNode(parent: parentNode?.outputNode, rootNode: rootNode?.outputNode) + var childKeypaths: [KeypathSearchable] = tree.childrenNodes + childKeypaths.append(properties) + self.childKeypaths = childKeypaths + + for childContainer in tree.renderContainers { + container.insertRenderLayer(childContainer) + } + } + + // MARK: Internal + + // MARK: Properties + let groupOutput: GroupOutputNode + + let properties: GroupNodeProperties + + let rootNode: AnimatorNode? + + var container = ShapeContainerLayer() + + // MARK: Keypath Searchable + + let keypathName: String + + let childKeypaths: [KeypathSearchable] + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + var keypathLayer: CALayer? { + container + } + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + properties + } + + var outputNode: NodeOutput { + groupOutput + } + + var isEnabled: Bool = true { + didSet { + container.isHidden = !isEnabled + } + } + + func performAdditionalLocalUpdates(frame: CGFloat, forceLocalUpdate: Bool) -> Bool { + rootNode?.updateContents(frame, forceLocalUpdate: forceLocalUpdate) ?? false + } + + func performAdditionalOutputUpdates(_ frame: CGFloat, forceOutputUpdate: Bool) { + rootNode?.updateOutputs(frame, forceOutputUpdate: forceOutputUpdate) + } + + func rebuildOutputs(frame: CGFloat) { + container.opacity = Float(properties.opacity.value.cgFloatValue) * 0.01 + container.transform = properties.caTransform + groupOutput.setTransform(container.transform, forFrame: frame) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift new file mode 100644 index 00000000..e75b04c4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift @@ -0,0 +1,90 @@ +// +// FillNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/17/19. +// + +import CoreGraphics +import Foundation + +// MARK: - FillNodeProperties + +final class FillNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(fill: Fill) { + keypathName = fill.name + color = NodeProperty(provider: KeyframeInterpolator(keyframes: fill.color.keyframes)) + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: fill.opacity.keyframes)) + type = fill.fillRule + keypathProperties = [ + "Opacity" : opacity, + "Color" : color, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + let opacity: NodeProperty + let color: NodeProperty + let type: FillRule + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + +} + +// MARK: - FillNode + +final class FillNode: AnimatorNode, RenderNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, fill: Fill) { + fillRender = FillRenderer(parent: parentNode?.outputNode) + fillProperties = FillNodeProperties(fill: fill) + self.parentNode = parentNode + } + + // MARK: Internal + + let fillRender: FillRenderer + + let fillProperties: FillNodeProperties + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + var renderer: NodeOutput & Renderable { + fillRender + } + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + fillProperties + } + + var isEnabled: Bool = true { + didSet { + fillRender.isEnabled = isEnabled + } + } + + func localUpdatesPermeateDownstream() -> Bool { + false + } + + func rebuildOutputs(frame _: CGFloat) { + fillRender.color = fillProperties.color.value.cgColorValue + fillRender.opacity = fillProperties.opacity.value.cgFloatValue * 0.01 + fillRender.fillRule = fillProperties.type + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift new file mode 100644 index 00000000..570d8ca8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift @@ -0,0 +1,102 @@ +// +// GradientFillNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/22/19. +// + +import Foundation +import QuartzCore + +// MARK: - GradientFillProperties + +final class GradientFillProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(gradientfill: GradientFill) { + keypathName = gradientfill.name + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.opacity.keyframes)) + startPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.startPoint.keyframes)) + endPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.endPoint.keyframes)) + colors = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.colors.keyframes)) + gradientType = gradientfill.gradientType + numberOfColors = gradientfill.numberOfColors + keypathProperties = [ + "Opacity" : opacity, + "Start Point" : startPoint, + "End Point" : endPoint, + "Colors" : colors, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + let opacity: NodeProperty + let startPoint: NodeProperty + let endPoint: NodeProperty + let colors: NodeProperty<[Double]> + + let gradientType: GradientType + let numberOfColors: Int + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + +} + +// MARK: - GradientFillNode + +final class GradientFillNode: AnimatorNode, RenderNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, gradientFill: GradientFill) { + fillRender = GradientFillRenderer(parent: parentNode?.outputNode) + fillProperties = GradientFillProperties(gradientfill: gradientFill) + self.parentNode = parentNode + } + + // MARK: Internal + + let fillRender: GradientFillRenderer + + let fillProperties: GradientFillProperties + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + var renderer: NodeOutput & Renderable { + fillRender + } + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + fillProperties + } + + var isEnabled: Bool = true { + didSet { + fillRender.isEnabled = isEnabled + } + } + + func localUpdatesPermeateDownstream() -> Bool { + false + } + + func rebuildOutputs(frame _: CGFloat) { + fillRender.start = fillProperties.startPoint.value.pointValue + fillRender.end = fillProperties.endPoint.value.pointValue + fillRender.opacity = fillProperties.opacity.value.cgFloatValue * 0.01 + fillRender.colors = fillProperties.colors.value.map { CGFloat($0) } + fillRender.type = fillProperties.gradientType + fillRender.numberOfColors = fillProperties.numberOfColors + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift new file mode 100644 index 00000000..8d607d25 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift @@ -0,0 +1,151 @@ +// +// GradientStrokeNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/23/19. +// + +import CoreGraphics +import Foundation + +// MARK: - GradientStrokeProperties + +final class GradientStrokeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(gradientStroke: GradientStroke) { + keypathName = gradientStroke.name + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.opacity.keyframes)) + startPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.startPoint.keyframes)) + endPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.endPoint.keyframes)) + colors = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.colors.keyframes)) + gradientType = gradientStroke.gradientType + numberOfColors = gradientStroke.numberOfColors + width = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.width.keyframes)) + miterLimit = CGFloat(gradientStroke.miterLimit) + lineCap = gradientStroke.lineCap + lineJoin = gradientStroke.lineJoin + + if let dashes = gradientStroke.dashPattern { + var dashPatterns = ContiguousArray>>() + var dashPhase = ContiguousArray>() + for dash in dashes { + if dash.type == .offset { + dashPhase = dash.value.keyframes + } else { + dashPatterns.append(dash.value.keyframes) + } + } + dashPattern = NodeProperty(provider: GroupInterpolator(keyframeGroups: dashPatterns)) + self.dashPhase = NodeProperty(provider: KeyframeInterpolator(keyframes: dashPhase)) + } else { + dashPattern = NodeProperty(provider: SingleValueProvider([Vector1D]())) + dashPhase = NodeProperty(provider: SingleValueProvider(Vector1D(0))) + } + keypathProperties = [ + "Opacity" : opacity, + "Start Point" : startPoint, + "End Point" : endPoint, + "Colors" : colors, + "Stroke Width" : width, + "Dashes" : dashPattern, + "Dash Phase" : dashPhase, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + var keypathName: String + + let opacity: NodeProperty + let startPoint: NodeProperty + let endPoint: NodeProperty + let colors: NodeProperty<[Double]> + let width: NodeProperty + + let dashPattern: NodeProperty<[Vector1D]> + let dashPhase: NodeProperty + + let lineCap: LineCap + let lineJoin: LineJoin + let miterLimit: CGFloat + let gradientType: GradientType + let numberOfColors: Int + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + +} + +// MARK: - GradientStrokeNode + +final class GradientStrokeNode: AnimatorNode, RenderNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, gradientStroke: GradientStroke) { + strokeRender = GradientStrokeRenderer(parent: parentNode?.outputNode) + strokeProperties = GradientStrokeProperties(gradientStroke: gradientStroke) + self.parentNode = parentNode + } + + // MARK: Internal + + let strokeRender: GradientStrokeRenderer + + let strokeProperties: GradientStrokeProperties + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + var renderer: NodeOutput & Renderable { + strokeRender + } + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + strokeProperties + } + + var isEnabled: Bool = true { + didSet { + strokeRender.isEnabled = isEnabled + } + } + + func localUpdatesPermeateDownstream() -> Bool { + false + } + + func rebuildOutputs(frame _: CGFloat) { + /// Update gradient properties + strokeRender.gradientRender.start = strokeProperties.startPoint.value.pointValue + strokeRender.gradientRender.end = strokeProperties.endPoint.value.pointValue + strokeRender.gradientRender.opacity = strokeProperties.opacity.value.cgFloatValue + strokeRender.gradientRender.colors = strokeProperties.colors.value.map { CGFloat($0) } + strokeRender.gradientRender.type = strokeProperties.gradientType + strokeRender.gradientRender.numberOfColors = strokeProperties.numberOfColors + + /// Now update stroke properties + strokeRender.strokeRender.opacity = strokeProperties.opacity.value.cgFloatValue + strokeRender.strokeRender.width = strokeProperties.width.value.cgFloatValue + strokeRender.strokeRender.miterLimit = strokeProperties.miterLimit + strokeRender.strokeRender.lineCap = strokeProperties.lineCap + strokeRender.strokeRender.lineJoin = strokeProperties.lineJoin + + /// Get dash lengths + let dashLengths = strokeProperties.dashPattern.value.map { $0.cgFloatValue } + if dashLengths.count > 0 { + strokeRender.strokeRender.dashPhase = strokeProperties.dashPhase.value.cgFloatValue + strokeRender.strokeRender.dashLengths = dashLengths + } else { + strokeRender.strokeRender.dashLengths = nil + strokeRender.strokeRender.dashPhase = nil + } + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift new file mode 100644 index 00000000..ae7fcc12 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift @@ -0,0 +1,138 @@ +// +// StrokeNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/22/19. +// + +import Foundation +import QuartzCore + +// MARK: - StrokeNodeProperties + +final class StrokeNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(stroke: Stroke) { + keypathName = stroke.name + color = NodeProperty(provider: KeyframeInterpolator(keyframes: stroke.color.keyframes)) + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: stroke.opacity.keyframes)) + width = NodeProperty(provider: KeyframeInterpolator(keyframes: stroke.width.keyframes)) + miterLimit = CGFloat(stroke.miterLimit) + lineCap = stroke.lineCap + lineJoin = stroke.lineJoin + + if let dashes = stroke.dashPattern { + var dashPatterns = ContiguousArray>>() + var dashPhase = ContiguousArray>() + for dash in dashes { + if dash.type == .offset { + dashPhase = dash.value.keyframes + } else { + dashPatterns.append(dash.value.keyframes) + } + } + dashPattern = NodeProperty(provider: GroupInterpolator(keyframeGroups: dashPatterns)) + if dashPhase.count == 0 { + self.dashPhase = NodeProperty(provider: SingleValueProvider(Vector1D(0))) + } else { + self.dashPhase = NodeProperty(provider: KeyframeInterpolator(keyframes: dashPhase)) + } + } else { + dashPattern = NodeProperty(provider: SingleValueProvider([Vector1D]())) + dashPhase = NodeProperty(provider: SingleValueProvider(Vector1D(0))) + } + keypathProperties = [ + "Opacity" : opacity, + "Color" : color, + "Stroke Width" : width, + "Dashes" : dashPattern, + "Dash Phase" : dashPhase, + ] + properties = Array(keypathProperties.values) + } + + // MARK: Internal + + let keypathName: String + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + + let opacity: NodeProperty + let color: NodeProperty + let width: NodeProperty + + let dashPattern: NodeProperty<[Vector1D]> + let dashPhase: NodeProperty + + let lineCap: LineCap + let lineJoin: LineJoin + let miterLimit: CGFloat + +} + +// MARK: - StrokeNode + +/// Node that manages stroking a path +final class StrokeNode: AnimatorNode, RenderNode { + + // MARK: Lifecycle + + init(parentNode: AnimatorNode?, stroke: Stroke) { + strokeRender = StrokeRenderer(parent: parentNode?.outputNode) + strokeProperties = StrokeNodeProperties(stroke: stroke) + self.parentNode = parentNode + } + + // MARK: Internal + + let strokeRender: StrokeRenderer + + let strokeProperties: StrokeNodeProperties + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + + var renderer: NodeOutput & Renderable { + strokeRender + } + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + strokeProperties + } + + var isEnabled: Bool = true { + didSet { + strokeRender.isEnabled = isEnabled + } + } + + func localUpdatesPermeateDownstream() -> Bool { + false + } + + func rebuildOutputs(frame _: CGFloat) { + strokeRender.color = strokeProperties.color.value.cgColorValue + strokeRender.opacity = strokeProperties.opacity.value.cgFloatValue * 0.01 + strokeRender.width = strokeProperties.width.value.cgFloatValue + strokeRender.miterLimit = strokeProperties.miterLimit + strokeRender.lineCap = strokeProperties.lineCap + strokeRender.lineJoin = strokeProperties.lineJoin + + /// Get dash lengths + let dashLengths = strokeProperties.dashPattern.value.map { $0.cgFloatValue } + if dashLengths.count > 0 { + strokeRender.dashPhase = strokeProperties.dashPhase.value.cgFloatValue + strokeRender.dashLengths = dashLengths + } else { + strokeRender.dashLengths = nil + strokeRender.dashPhase = nil + } + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift new file mode 100644 index 00000000..6156a8b0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift @@ -0,0 +1,270 @@ +// +// TextAnimatorNode.swift +// lottie-ios-iOS +// +// Created by Brandon Withrow on 2/19/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +// MARK: - TextAnimatorNodeProperties + +final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable { + + // MARK: Lifecycle + + init(textAnimator: TextAnimator) { + keypathName = textAnimator.name + var properties = [String : AnyNodeProperty]() + + if let keyframeGroup = textAnimator.anchor { + anchor = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Anchor"] = anchor + } else { + anchor = nil + } + + if let keyframeGroup = textAnimator.position { + position = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Position"] = position + } else { + position = nil + } + + if let keyframeGroup = textAnimator.scale { + scale = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Scale"] = scale + } else { + scale = nil + } + + if let keyframeGroup = textAnimator.skew { + skew = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Skew"] = skew + } else { + skew = nil + } + + if let keyframeGroup = textAnimator.skewAxis { + skewAxis = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Skew Axis"] = skewAxis + } else { + skewAxis = nil + } + + if let keyframeGroup = textAnimator.rotation { + rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Rotation"] = rotation + } else { + rotation = nil + } + + if let keyframeGroup = textAnimator.opacity { + opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Opacity"] = opacity + } else { + opacity = nil + } + + if let keyframeGroup = textAnimator.strokeColor { + strokeColor = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Stroke Color"] = strokeColor + } else { + strokeColor = nil + } + + if let keyframeGroup = textAnimator.fillColor { + fillColor = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Fill Color"] = fillColor + } else { + fillColor = nil + } + + if let keyframeGroup = textAnimator.strokeWidth { + strokeWidth = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Stroke Width"] = strokeWidth + } else { + strokeWidth = nil + } + + if let keyframeGroup = textAnimator.tracking { + tracking = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) + properties["Tracking"] = tracking + } else { + tracking = nil + } + + keypathProperties = properties + + self.properties = Array(keypathProperties.values) + } + + // MARK: Internal + + let keypathName: String + + let anchor: NodeProperty? + let position: NodeProperty? + let scale: NodeProperty? + let skew: NodeProperty? + let skewAxis: NodeProperty? + let rotation: NodeProperty? + let opacity: NodeProperty? + let strokeColor: NodeProperty? + let fillColor: NodeProperty? + let strokeWidth: NodeProperty? + let tracking: NodeProperty? + + let keypathProperties: [String: AnyNodeProperty] + let properties: [AnyNodeProperty] + + var caTransform: CATransform3D { + CATransform3D.makeTransform( + anchor: anchor?.value.pointValue ?? .zero, + position: position?.value.pointValue ?? .zero, + scale: scale?.value.sizeValue ?? CGSize(width: 100, height: 100), + rotation: rotation?.value.cgFloatValue ?? 0, + skew: skew?.value.cgFloatValue, + skewAxis: skewAxis?.value.cgFloatValue) + } +} + +// MARK: - TextOutputNode + +final class TextOutputNode: NodeOutput { + + // MARK: Lifecycle + + init(parent: TextOutputNode?) { + parentTextNode = parent + } + + // MARK: Internal + + var parentTextNode: TextOutputNode? + var isEnabled: Bool = true + + var outputPath: CGPath? + + var parent: NodeOutput? { + parentTextNode + } + + var xform: CATransform3D { + get { + _xform ?? parentTextNode?.xform ?? CATransform3DIdentity + } + set { + _xform = newValue + } + } + + var opacity: CGFloat { + get { + _opacity ?? parentTextNode?.opacity ?? 1 + } + set { + _opacity = newValue + } + } + + var strokeColor: CGColor? { + get { + _strokeColor ?? parentTextNode?.strokeColor + } + set { + _strokeColor = newValue + } + } + + var fillColor: CGColor? { + get { + _fillColor ?? parentTextNode?.fillColor + } + set { + _fillColor = newValue + } + } + + var tracking: CGFloat { + get { + _tracking ?? parentTextNode?.tracking ?? 0 + } + set { + _tracking = newValue + } + } + + var strokeWidth: CGFloat { + get { + _strokeWidth ?? parentTextNode?.strokeWidth ?? 0 + } + set { + _strokeWidth = newValue + } + } + + func hasOutputUpdates(_: CGFloat) -> Bool { + // TODO Fix This + true + } + + // MARK: Fileprivate + + fileprivate var _xform: CATransform3D? + fileprivate var _opacity: CGFloat? + fileprivate var _strokeColor: CGColor? + fileprivate var _fillColor: CGColor? + fileprivate var _tracking: CGFloat? + fileprivate var _strokeWidth: CGFloat? +} + +// MARK: - TextAnimatorNode + +class TextAnimatorNode: AnimatorNode { + + // MARK: Lifecycle + + init(parentNode: TextAnimatorNode?, textAnimator: TextAnimator) { + textOutputNode = TextOutputNode(parent: parentNode?.textOutputNode) + textAnimatorProperties = TextAnimatorNodeProperties(textAnimator: textAnimator) + self.parentNode = parentNode + } + + // MARK: Internal + + let textOutputNode: TextOutputNode + + let textAnimatorProperties: TextAnimatorNodeProperties + + let parentNode: AnimatorNode? + var hasLocalUpdates: Bool = false + var hasUpstreamUpdates: Bool = false + var lastUpdateFrame: CGFloat? = nil + var isEnabled: Bool = true + + var outputNode: NodeOutput { + textOutputNode + } + + // MARK: Animator Node Protocol + + var propertyMap: NodePropertyMap & KeypathSearchable { + textAnimatorProperties + } + + func localUpdatesPermeateDownstream() -> Bool { + true + } + + func rebuildOutputs(frame _: CGFloat) { + textOutputNode.xform = textAnimatorProperties.caTransform + textOutputNode.opacity = (textAnimatorProperties.opacity?.value.cgFloatValue ?? 100) * 0.01 + textOutputNode.strokeColor = textAnimatorProperties.strokeColor?.value.cgColorValue + textOutputNode.fillColor = textAnimatorProperties.fillColor?.value.cgColorValue + textOutputNode.tracking = textAnimatorProperties.tracking?.value.cgFloatValue ?? 1 + textOutputNode.strokeWidth = textAnimatorProperties.strokeWidth?.value.cgFloatValue ?? 0 + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift new file mode 100644 index 00000000..d9b15a59 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift @@ -0,0 +1,204 @@ +// +// AnimatorNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/15/19. +// + +import Foundation +import QuartzCore + +// MARK: - NodeOutput + +/** + Defines the basic outputs of an animator node. + + */ +protocol NodeOutput { + + /// The parent node. + var parent: NodeOutput? { get } + + /// Returns true if there are any updates upstream. OutputPath must be built before returning. + func hasOutputUpdates(_ forFrame: CGFloat) -> Bool + + var outputPath: CGPath? { get } + + var isEnabled: Bool { get set } +} + +// MARK: - AnimatorNode + +/** + The Animator Node is the base node in the render system tree. + + It defines a single node that has an output path and option input node. + At animation time the root animation node is asked to update its contents for + the current frame. + The node reaches up its chain of nodes until the first node that does not need + updating is found. Then each node updates its contents down the render pipeline. + Each node adds its local path to its input path and passes it forward. + + An animator node holds a group of interpolators. These interpolators determine + if the node needs an update for the current frame. + + */ +protocol AnimatorNode: AnyObject, KeypathSearchable { + + /** + The available properties of the Node. + + These properties are automatically updated each frame. + These properties are also settable and gettable through the dynamic + property system. + + */ + var propertyMap: NodePropertyMap & KeypathSearchable { get } + + /// The upstream input node + var parentNode: AnimatorNode? { get } + + /// The output of the node. + var outputNode: NodeOutput { get } + + /// Update the outputs of the node. Called if local contents were update or if outputsNeedUpdate returns true. + func rebuildOutputs(frame: CGFloat) + + /// Setters for marking current node state. + var isEnabled: Bool { get set } + var hasLocalUpdates: Bool { get set } + var hasUpstreamUpdates: Bool { get set } + var lastUpdateFrame: CGFloat? { get set } + + // MARK: Optional + + /// Marks if updates to this node affect nodes downstream. + func localUpdatesPermeateDownstream() -> Bool + func forceUpstreamOutputUpdates() -> Bool + + /// Called at the end of this nodes update cycle. Always called. Optional. + func performAdditionalLocalUpdates(frame: CGFloat, forceLocalUpdate: Bool) -> Bool + func performAdditionalOutputUpdates(_ frame: CGFloat, forceOutputUpdate: Bool) + + /// The default simply returns `hasLocalUpdates` + func shouldRebuildOutputs(frame: CGFloat) -> Bool +} + +/// Basic Node Logic +extension AnimatorNode { + + func shouldRebuildOutputs(frame _: CGFloat) -> Bool { + hasLocalUpdates + } + + func localUpdatesPermeateDownstream() -> Bool { + /// Optional override + true + } + + func forceUpstreamOutputUpdates() -> Bool { + /// Optional + false + } + + func performAdditionalLocalUpdates(frame _: CGFloat, forceLocalUpdate: Bool) -> Bool { + /// Optional + forceLocalUpdate + } + + func performAdditionalOutputUpdates(_: CGFloat, forceOutputUpdate _: Bool) { + /// Optional + } + + @discardableResult + func updateOutputs(_ frame: CGFloat, forceOutputUpdate: Bool) -> Bool { + guard isEnabled else { + // Disabled node, pass through. + lastUpdateFrame = frame + return parentNode?.updateOutputs(frame, forceOutputUpdate: forceOutputUpdate) ?? false + } + + if forceOutputUpdate == false && lastUpdateFrame != nil && lastUpdateFrame! == frame { + /// This node has already updated for this frame. Go ahead and return the results. + return hasUpstreamUpdates || hasLocalUpdates + } + + /// Ask if this node should force output updates upstream. + let forceUpstreamUpdates = forceOutputUpdate || forceUpstreamOutputUpdates() + + /// Perform upstream output updates. Optionally mark upstream updates if any. + hasUpstreamUpdates = ( + parentNode? + .updateOutputs(frame, forceOutputUpdate: forceUpstreamUpdates) ?? false || hasUpstreamUpdates) + + /// Perform additional local output updates + performAdditionalOutputUpdates(frame, forceOutputUpdate: forceUpstreamUpdates) + + /// If there are local updates, or if updates have been force, rebuild outputs + if forceUpstreamUpdates || shouldRebuildOutputs(frame: frame) { + lastUpdateFrame = frame + rebuildOutputs(frame: frame) + } + return hasUpstreamUpdates || hasLocalUpdates + } + + /// Rebuilds the content of this node, and upstream nodes if necessary. + @discardableResult + func updateContents(_ frame: CGFloat, forceLocalUpdate: Bool) -> Bool { + guard isEnabled else { + // Disabled node, pass through. + return parentNode?.updateContents(frame, forceLocalUpdate: forceLocalUpdate) ?? false + } + + if forceLocalUpdate == false && lastUpdateFrame != nil && lastUpdateFrame! == frame { + /// This node has already updated for this frame. Go ahead and return the results. + return localUpdatesPermeateDownstream() ? hasUpstreamUpdates || hasLocalUpdates : hasUpstreamUpdates + } + + /// Are there local updates? If so mark the node. + hasLocalUpdates = forceLocalUpdate ? forceLocalUpdate : propertyMap.needsLocalUpdate(frame: frame) + + /// Were there upstream updates? If so mark the node + hasUpstreamUpdates = parentNode?.updateContents(frame, forceLocalUpdate: forceLocalUpdate) ?? false + + /// Perform property updates if necessary. + if hasLocalUpdates { + /// Rebuild local properties + propertyMap.updateNodeProperties(frame: frame) + } + + /// Ask the node to perform any other updates it might have. + hasUpstreamUpdates = performAdditionalLocalUpdates(frame: frame, forceLocalUpdate: forceLocalUpdate) || hasUpstreamUpdates + + /// If the node can update nodes downstream, notify them, otherwise pass on any upstream updates downstream. + return localUpdatesPermeateDownstream() ? hasUpstreamUpdates || hasLocalUpdates : hasUpstreamUpdates + } + + func updateTree(_ frame: CGFloat, forceUpdates: Bool = false) { + updateContents(frame, forceLocalUpdate: forceUpdates) + updateOutputs(frame, forceOutputUpdate: forceUpdates) + } + +} + +extension AnimatorNode { + /// Default implementation for Keypath searchable. + /// Forward all calls to the propertyMap. + + var keypathName: String { + propertyMap.keypathName + } + + var keypathProperties: [String: AnyNodeProperty] { + propertyMap.keypathProperties + } + + var childKeypaths: [KeypathSearchable] { + propertyMap.childKeypaths + } + + var keypathLayer: CALayer? { + nil + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift new file mode 100644 index 00000000..7eaa6cf4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift @@ -0,0 +1,22 @@ +// +// PathNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/17/19. +// + +import Foundation + +// MARK: - PathNode + +protocol PathNode { + var pathOutput: PathOutputNode { get } +} + +extension PathNode where Self: AnimatorNode { + + var outputNode: NodeOutput { + pathOutput + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift new file mode 100644 index 00000000..ca4e6eaf --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift @@ -0,0 +1,61 @@ +// +// RenderNode.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/17/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +// MARK: - RenderNode + +/// A protocol that defines a node that holds render instructions +protocol RenderNode { + var renderer: Renderable & NodeOutput { get } +} + +// MARK: - Renderable + +/// A protocol that defines anything with render instructions +protocol Renderable { + + /// The last frame in which this node was updated. + var hasUpdate: Bool { get } + + func hasRenderUpdates(_ forFrame: CGFloat) -> Bool + + /** + Determines if the renderer requires a custom context for drawing. + If yes the shape layer will perform a custom drawing pass. + If no the shape layer will be a standard CAShapeLayer + */ + var shouldRenderInContext: Bool { get } + + /// Passes in the CAShapeLayer to update + func updateShapeLayer(layer: CAShapeLayer) + + /// Asks the renderer what the renderable bounds is for the given box. + func renderBoundsFor(_ boundingBox: CGRect) -> CGRect + + /// Renders the shape in a custom context + func render(_ inContext: CGContext) +} + +extension RenderNode where Self: AnimatorNode { + + var outputNode: NodeOutput { + renderer + } + +} + +extension Renderable { + + func renderBoundsFor(_ boundingBox: CGRect) -> CGRect { + /// Optional + boundingBox + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift new file mode 100644 index 00000000..146d2b1a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift @@ -0,0 +1,77 @@ +// +// ShapeContainerLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import Foundation +import QuartzCore + +/** + The base layer that holds Shapes and Shape Renderers + */ +class ShapeContainerLayer: CALayer { + + // MARK: Lifecycle + + override init() { + super.init() + actions = [ + "position" : NSNull(), + "bounds" : NSNull(), + "anchorPoint" : NSNull(), + "transform" : NSNull(), + "opacity" : NSNull(), + "hidden" : NSNull(), + ] + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(layer: Any) { + guard let layer = layer as? ShapeContainerLayer else { + fatalError("init(layer:) wrong class.") + } + super.init(layer: layer) + } + + // MARK: Internal + + private(set) var renderLayers: [ShapeContainerLayer] = [] + + var renderScale: CGFloat = 1 { + didSet { + updateRenderScale() + } + } + + func insertRenderLayer(_ layer: ShapeContainerLayer) { + renderLayers.append(layer) + insertSublayer(layer, at: 0) + } + + func markRenderUpdates(forFrame: CGFloat) { + if hasRenderUpdate(forFrame: forFrame) { + rebuildContents(forFrame: forFrame) + } + guard isHidden == false else { return } + renderLayers.forEach { $0.markRenderUpdates(forFrame: forFrame) } + } + + func hasRenderUpdate(forFrame _: CGFloat) -> Bool { + false + } + + func rebuildContents(forFrame _: CGFloat) { + /// Override + } + + func updateRenderScale() { + contentsScale = renderScale + renderLayers.forEach( { $0.renderScale = renderScale } ) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift new file mode 100644 index 00000000..19ffed34 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift @@ -0,0 +1,99 @@ +// +// RenderLayer.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/18/19. +// + +import Foundation +import QuartzCore + +/** + The layer responsible for rendering shape objects + */ +final class ShapeRenderLayer: ShapeContainerLayer { + + // MARK: Lifecycle + + init(renderer: Renderable & NodeOutput) { + self.renderer = renderer + super.init() + anchorPoint = .zero + actions = [ + "position" : NSNull(), + "bounds" : NSNull(), + "anchorPoint" : NSNull(), + "path" : NSNull(), + "transform" : NSNull(), + "opacity" : NSNull(), + "hidden" : NSNull(), + ] + shapeLayer.actions = [ + "position" : NSNull(), + "bounds" : NSNull(), + "anchorPoint" : NSNull(), + "path" : NSNull(), + "fillColor" : NSNull(), + "strokeColor" : NSNull(), + "lineWidth" : NSNull(), + "miterLimit" : NSNull(), + "lineDashPhase" : NSNull(), + "hidden" : NSNull(), + ] + addSublayer(shapeLayer) + } + + override init(layer: Any) { + guard let layer = layer as? ShapeRenderLayer else { + fatalError("init(layer:) wrong class.") + } + renderer = layer.renderer + super.init(layer: layer) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Internal + + fileprivate(set) var renderer: Renderable & NodeOutput + + let shapeLayer = CAShapeLayer() + + override func hasRenderUpdate(forFrame: CGFloat) -> Bool { + isHidden = !renderer.isEnabled + guard isHidden == false else { return false } + return renderer.hasRenderUpdates(forFrame) + } + + override func rebuildContents(forFrame _: CGFloat) { + + if renderer.shouldRenderInContext { + if let newPath = renderer.outputPath { + bounds = renderer.renderBoundsFor(newPath.boundingBox) + } else { + bounds = .zero + } + position = bounds.origin + setNeedsDisplay() + } else { + shapeLayer.path = renderer.outputPath + renderer.updateShapeLayer(layer: shapeLayer) + } + } + + override func draw(in ctx: CGContext) { + if let path = renderer.outputPath { + if !path.isEmpty { + ctx.addPath(path) + } + } + renderer.render(ctx) + } + + override func updateRenderScale() { + super.updateRenderScale() + shapeLayer.contentsScale = renderScale + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift new file mode 100644 index 00000000..3e8a5114 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift @@ -0,0 +1,25 @@ +// +// AnimatorNodeDebugging.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/18/19. +// + +import Foundation + +extension AnimatorNode { + + func printNodeTree() { + parentNode?.printNodeTree() + print(String(describing: type(of: self))) + + if let group = self as? GroupNode { + print("* |Children") + group.rootNode?.printNodeTree() + print("*") + } else { + print("|") + } + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift new file mode 100644 index 00000000..25ace05d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift @@ -0,0 +1,226 @@ +// +// LayerDebugging.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/24/19. +// + +import Foundation +import QuartzCore + +// MARK: - LayerDebugStyle + +struct LayerDebugStyle { + let anchorColor: CGColor + let boundsColor: CGColor + let anchorWidth: CGFloat + let boundsWidth: CGFloat +} + +// MARK: - LayerDebugging + +protocol LayerDebugging { + var debugStyle: LayerDebugStyle { get } +} + +// MARK: - CustomLayerDebugging + +protocol CustomLayerDebugging { + func layerForDebugging() -> CALayer +} + +// MARK: - DebugLayer + +class DebugLayer: CALayer { + init(style: LayerDebugStyle) { + super.init() + zPosition = 1000 + bounds = CGRect(x: 0, y: 0, width: style.anchorWidth, height: style.anchorWidth) + backgroundColor = style.anchorColor + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension CALayer { + + public func logLayerTree(withIndent: Int = 0) { + var string = "" + for _ in 0...withIndent { + string = string + " " + } + string = string + "|_" + String(describing: self) + print(string) + if let sublayers = sublayers { + for sublayer in sublayers { + sublayer.logLayerTree(withIndent: withIndent + 1) + } + } + } + +} + +// MARK: - CompositionLayer + CustomLayerDebugging + +extension CompositionLayer: CustomLayerDebugging { + func layerForDebugging() -> CALayer { + contentsLayer + } +} + +extension CALayer { + + func setDebuggingState(visible: Bool) { + + var sublayers = self.sublayers + if let cust = self as? CustomLayerDebugging { + sublayers = cust.layerForDebugging().sublayers + } + + if let sublayers = sublayers { + for i in 0.. LayerDebugStyle { + let colorSpace = CGColorSpaceCreateDeviceRGB() + + let anchorColor = CGColor(colorSpace: colorSpace, components: [1, 0, 0, 1])! + let boundsColor = CGColor(colorSpace: colorSpace, components: [1, 1, 0, 1])! + return LayerDebugStyle( + anchorColor: anchorColor, + boundsColor: boundsColor, + anchorWidth: 10, + boundsWidth: 2) + } + + static func topLayerStyle() -> LayerDebugStyle { + let colorSpace = CGColorSpaceCreateDeviceRGB() + let anchorColor = CGColor(colorSpace: colorSpace, components: [1, 0.5, 0, 0])! + let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! + + return LayerDebugStyle( + anchorColor: anchorColor, + boundsColor: boundsColor, + anchorWidth: 10, + boundsWidth: 2) + } + + static func nullLayerStyle() -> LayerDebugStyle { + let colorSpace = CGColorSpaceCreateDeviceRGB() + let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 0, 1, 0])! + let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! + + return LayerDebugStyle( + anchorColor: anchorColor, + boundsColor: boundsColor, + anchorWidth: 10, + boundsWidth: 2) + } + + static func shapeLayerStyle() -> LayerDebugStyle { + let colorSpace = CGColorSpaceCreateDeviceRGB() + let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 0])! + let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! + + return LayerDebugStyle( + anchorColor: anchorColor, + boundsColor: boundsColor, + anchorWidth: 10, + boundsWidth: 2) + } + + static func shapeRenderLayerStyle() -> LayerDebugStyle { + let colorSpace = CGColorSpaceCreateDeviceRGB() + let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 1, 1, 0])! + let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! + + return LayerDebugStyle( + anchorColor: anchorColor, + boundsColor: boundsColor, + anchorWidth: 10, + boundsWidth: 2) + } +} + +extension Array where Element == LayerModel { + + var parents: [Int] { + var array = [Int]() + for layer in self { + if let parent = layer.parent { + array.append(parent) + } else { + array.append(-1) + } + } + return array + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift new file mode 100644 index 00000000..32a09e73 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift @@ -0,0 +1,268 @@ +// +// KeypathSearchableExtension.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +import QuartzCore + +extension KeypathSearchable { + + func animatorNodes(for keyPath: AnimationKeypath) -> [AnimatorNode]? { + // Make sure there is a current key path. + guard let currentKey = keyPath.currentKey else { return nil } + + // Now try popping the keypath for wildcard / child search + guard let nextKeypath = keyPath.popKey(keypathName) else { + // We may be on the final keypath. Check for match. + if + let node = self as? AnimatorNode, + currentKey.equalsKeypath(keypathName) + { + // This is the final keypath and matches self. Return.s + return [node] + } + /// Nope. Stop Search + return nil + } + + var results: [AnimatorNode] = [] + + if + let node = self as? AnimatorNode, + nextKeypath.currentKey == nil + { + // Keypath matched self and was the final keypath. + results.append(node) + } + + for childNode in childKeypaths { + // Check if the child has any nodes matching the next keypath. + if let foundNodes = childNode.animatorNodes(for: nextKeypath) { + results.append(contentsOf: foundNodes) + } + + // In this case the current key is fuzzy, and both child and self match the next keyname. Keep digging! + if + currentKey.keyPathType == .fuzzyWildcard, + let nextKeypath = keyPath.nextKeypath, + nextKeypath.equalsKeypath(childNode.keypathName), + let foundNodes = childNode.animatorNodes(for: keyPath) + { + results.append(contentsOf: foundNodes) + } + } + + guard results.count > 0 else { + return nil + } + + return results + } + + func nodeProperties(for keyPath: AnimationKeypath) -> [AnyNodeProperty]? { + guard let nextKeypath = keyPath.popKey(keypathName) else { + /// Nope. Stop Search + return nil + } + + /// Keypath matches in some way. Continue the search. + var results: [AnyNodeProperty] = [] + + /// Check if we have a property keypath yet + if + let propertyKey = nextKeypath.propertyKey, + let property = keypathProperties[propertyKey] + { + /// We found a property! + results.append(property) + } + + if nextKeypath.nextKeypath != nil { + /// Now check child keypaths. + for child in childKeypaths { + if let childProperties = child.nodeProperties(for: nextKeypath) { + results.append(contentsOf: childProperties) + } + } + } + + guard results.count > 0 else { + return nil + } + + return results + } + + func layer(for keyPath: AnimationKeypath) -> CALayer? { + if keyPath.nextKeypath == nil, let layerKey = keyPath.currentKey, layerKey.equalsKeypath(keypathName) { + /// We found our layer! + return keypathLayer + } + guard let nextKeypath = keyPath.popKey(keypathName) else { + /// Nope. Stop Search + return nil + } + + if nextKeypath.nextKeypath != nil { + /// Now check child keypaths. + for child in childKeypaths { + if let layer = child.layer(for: keyPath) { + return layer + } + } + } + return nil + } + + func logKeypaths(for keyPath: AnimationKeypath?) { + let newKeypath: AnimationKeypath + if let previousKeypath = keyPath { + newKeypath = previousKeypath.appendingKey(keypathName) + } else { + newKeypath = AnimationKeypath(keys: [keypathName]) + } + print(newKeypath.fullPath) + for key in keypathProperties.keys { + print(newKeypath.appendingKey(key).fullPath) + } + for child in childKeypaths { + child.logKeypaths(for: newKeypath) + } + } +} + +extension AnimationKeypath { + var currentKey: String? { + keys.first + } + + var nextKeypath: String? { + guard keys.count > 1 else { + return nil + } + return keys[1] + } + + var propertyKey: String? { + if nextKeypath == nil { + /// There are no more keypaths. This is a property key. + return currentKey + } + if keys.count == 2, currentKey?.keyPathType == .fuzzyWildcard { + /// The next keypath is the last and the current is a fuzzy key. + return nextKeypath + } + return nil + } + + var fullPath: String { + keys.joined(separator: ".") + } + + // Pops the top keypath from the stack if the keyname matches. + func popKey(_ keyname: String) -> AnimationKeypath? { + guard + let currentKey = currentKey, + currentKey.equalsKeypath(keyname), + keys.count > 1 else + { + // Current key either doesnt match or we are on the last key. + return nil + } + + // Pop the keypath from the stack and return the new stack. + let newKeys: [String] + + if currentKey.keyPathType == .fuzzyWildcard { + /// Dont remove if current key is a fuzzy wildcard, and if the next keypath doesnt equal keypathname + if + let nextKeypath = nextKeypath, + nextKeypath.equalsKeypath(keyname) + { + /// Remove next two keypaths. This keypath breaks the wildcard. + var oldKeys = keys + oldKeys.remove(at: 0) + oldKeys.remove(at: 0) + newKeys = oldKeys + } else { + newKeys = keys + } + } else { + var oldKeys = keys + oldKeys.remove(at: 0) + newKeys = oldKeys + } + + return AnimationKeypath(keys: newKeys) + } + + func appendingKey(_ key: String) -> AnimationKeypath { + var newKeys = keys + newKeys.append(key) + return AnimationKeypath(keys: newKeys) + } +} + +extension String { + var keyPathType: KeyType { + switch self { + case "*": + return .wildcard + case "**": + return .fuzzyWildcard + default: + return .specific + } + } + + func equalsKeypath(_ keyname: String) -> Bool { + if keyPathType == .wildcard || keyPathType == .fuzzyWildcard { + return true + } + if self == keyname { + return true + } + if let index = firstIndex(of: "*") { + // Wildcard search. + let prefix = String(self.prefix(upTo: index)) + let suffix = String(self.suffix(from: self.index(after: index))) + + if prefix.count > 0 { + // Match prefix. + if keyname.count < prefix.count { + return false + } + let testPrefix = String(keyname.prefix(upTo: keyname.index(keyname.startIndex, offsetBy: prefix.count))) + if testPrefix != prefix { + // Prefix doesnt match + return false + } + } + if suffix.count > 0 { + // Match suffix. + if keyname.count < suffix.count { + // Suffix doesnt match + return false + } + let index = keyname.index(keyname.endIndex, offsetBy: -suffix.count) + let testSuffix = String(keyname.suffix(from: index)) + if testSuffix != suffix { + return false + } + } + return true + } + return false + } +} + +// MARK: - KeyType + +enum KeyType { + case specific + case wildcard + case fuzzyWildcard +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift new file mode 100644 index 00000000..015a10ce --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift @@ -0,0 +1,152 @@ +// +// CGFloatExtensions.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/14/19. +// + +import Foundation +import QuartzCore + +extension CGFloat { + + // MARK: Internal + + var squared: CGFloat { + self * self + } + + var cubed: CGFloat { + self * self * self + } + + var cubicRoot: CGFloat { + CGFloat(pow(Double(self), 1.0 / 3.0)) + } + + func isInRangeOrEqual(_ from: CGFloat, _ to: CGFloat) -> Bool { + from <= self && self <= to + } + + func isInRange(_ from: CGFloat, _ to: CGFloat) -> Bool { + from < self && self < to + } + + func cubicBezierInterpolate(_ P0: CGPoint, _ P1: CGPoint, _ P2: CGPoint, _ P3: CGPoint) -> CGFloat { + var t: CGFloat + if self == P0.x { + // Handle corner cases explicitly to prevent rounding errors + t = 0 + } else if self == P3.x { + t = 1 + } else { + // Calculate t + let a = -P0.x + 3 * P1.x - 3 * P2.x + P3.x; + let b = 3 * P0.x - 6 * P1.x + 3 * P2.x; + let c = -3 * P0.x + 3 * P1.x; + let d = P0.x - self; + let tTemp = CGFloat.SolveCubic(a, b, c, d); + if tTemp == -1 { + return -1; + } + t = tTemp + } + + // Calculate y from t + return (1 - t).cubed * P0.y + 3 * t * (1 - t).squared * P1.y + 3 * t.squared * (1 - t) * P2.y + t.cubed * P3.y; + } + + func cubicBezier(_ t: CGFloat, _ c1: CGFloat, _ c2: CGFloat, _ end: CGFloat) -> CGFloat { + let t_ = (1.0 - t) + let tt_ = t_ * t_ + let ttt_ = t_ * t_ * t_ + let tt = t * t + let ttt = t * t * t + + return self * ttt_ + + 3.0 * c1 * tt_ * t + + 3.0 * c2 * t_ * tt + + end * ttt; + } + + // MARK: Fileprivate + + fileprivate static func SolveQuadratic(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat) -> CGFloat { + var result = (-b + sqrt(b.squared - 4 * a * c)) / (2 * a); + guard !result.isInRangeOrEqual(0, 1) else { + return result + } + + result = (-b - sqrt(b.squared - 4 * a * c)) / (2 * a); + guard !result.isInRangeOrEqual(0, 1) else { + return result + } + + return -1; + } + + fileprivate static func SolveCubic(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat, _ d: CGFloat) -> CGFloat { + if a == 0 { + return SolveQuadratic(b, c, d) + } + if d == 0 { + return 0 + } + let a = a + var b = b + var c = c + var d = d + b /= a + c /= a + d /= a + var q = (3.0 * c - b.squared) / 9.0 + let r = (-27.0 * d + b * (9.0 * c - 2.0 * b.squared)) / 54.0 + let disc = q.cubed + r.squared + let term1 = b / 3.0 + + if disc > 0 { + var s = r + sqrt(disc) + s = (s < 0) ? -((-s).cubicRoot) : s.cubicRoot + var t = r - sqrt(disc) + t = (t < 0) ? -((-t).cubicRoot) : t.cubicRoot + + let result = -term1 + s + t; + if result.isInRangeOrEqual(0, 1) { + return result + } + } else if disc == 0 { + let r13 = (r < 0) ? -((-r).cubicRoot) : r.cubicRoot; + + var result = -term1 + 2.0 * r13; + if result.isInRangeOrEqual(0, 1) { + return result + } + + result = -(r13 + term1); + if result.isInRangeOrEqual(0, 1) { + return result + } + + } else { + q = -q; + var dum1 = q * q * q; + dum1 = acos(r / sqrt(dum1)); + let r13 = 2.0 * sqrt(q); + + var result = -term1 + r13 * cos(dum1 / 3.0); + if result.isInRangeOrEqual(0, 1) { + return result + } + result = -term1 + r13 * cos((dum1 + 2.0 * .pi) / 3.0); + if result.isInRangeOrEqual(0, 1) { + return result + } + result = -term1 + r13 * cos((dum1 + 4.0 * .pi) / 3.0); + if result.isInRangeOrEqual(0, 1) { + return result + } + } + + return -1; + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift new file mode 100644 index 00000000..8dea758e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift @@ -0,0 +1,579 @@ +// +// MathKit.swift +// UIToolBox +// +// Created by Brandon Withrow on 10/10/18. +// +// From https://github.com/buba447/UIToolBox + +import CoreGraphics +import Foundation + +extension Int { + var cgFloat: CGFloat { + CGFloat(self) + } +} + +extension Double { + var cgFloat: CGFloat { + CGFloat(self) + } +} + +// MARK: - CGFloat + Interpolatable + +extension CGFloat: Interpolatable { + + /** + Interpolates the receiver to the given number by Amount. + - Parameter toNumber: The number to interpolate to. + - Parameter amount: The amount to interpolate from 0-1 + + ``` + let number = 5 + let interpolated = number.interpolateTo(10, amount: 0.5) + print(interpolated) + // Result: 7.5 + ``` + + 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. + */ + func interpolateTo(_ to: CGFloat, amount: CGFloat) -> CGFloat { + self + ((to - self) * CGFloat(amount)) + } + + func interpolateTo(_ to: CGFloat, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> CGFloat { + interpolateTo(to, amount: amount) + } + + func remap(fromLow: CGFloat, fromHigh: CGFloat, toLow: CGFloat, toHigh: CGFloat) -> CGFloat { + guard (fromHigh - fromLow) != 0 else { + // Would produce NAN + return 0 + } + return toLow + (self - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + } + + /** + Returns a value that is clamped between the two numbers + + 1. The order of arguments does not matter. + */ + func clamp(_ a: CGFloat, _ b: CGFloat) -> CGFloat { + CGFloat(Double(self).clamp(Double(a), Double(b))) + } + + /** + Returns the difference between the receiver and the given number. + - Parameter absolute: If *true* (Default) the returned value will always be positive. + */ + func diff(_ a: CGFloat, absolute: Bool = true) -> CGFloat { + absolute ? abs(a - self) : a - self + } + + func toRadians() -> CGFloat { self * .pi / 180 } + func toDegrees() -> CGFloat { self * 180 / .pi } + +} + +// MARK: - Double + Interpolatable + +extension Double: Interpolatable { + + /** + Interpolates the receiver to the given number by Amount. + - Parameter toNumber: The number to interpolate to. + - Parameter amount: The amount to interpolate from 0-1 + + ``` + let number = 5 + let interpolated = number.interpolateTo(10, amount: 0.5) + print(interpolated) + // Result: 7.5 + ``` + + 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. + */ + func interpolateTo(_ to: Double, amount: CGFloat) -> Double { + self + ((to - self) * Double(amount)) + } + + func interpolateTo(_ to: Double, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> Double { + interpolateTo(to, amount: amount) + } + + func remap(fromLow: Double, fromHigh: Double, toLow: Double, toHigh: Double) -> Double { + toLow + (self - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + } + + /** + Returns a value that is clamped between the two numbers + + 1. The order of arguments does not matter. + */ + func clamp(_ a: Double, _ b: Double) -> Double { + let minValue = a <= b ? a : b + let maxValue = a <= b ? b : a + return max(min(self, maxValue), minValue) + } + +} + +extension CGRect { + + // MARK: Lifecycle + + /// Initializes a new CGRect with a center point and size. + init(center: CGPoint, size: CGSize) { + self.init( + x: center.x - (size.width * 0.5), + y: center.y - (size.height * 0.5), + width: size.width, + height: size.height) + } + + // MARK: Internal + + /// Returns the total area of the rect. + var area: CGFloat { + width * height + } + + /// The center point of the rect. Settable. + var center: CGPoint { + get { + CGPoint(x: midX, y: midY) + } + set { + origin = CGPoint( + x: newValue.x - (size.width * 0.5), + y: newValue.y - (size.height * 0.5)) + } + } + + /// The top left point of the rect. Settable. + var topLeft: CGPoint { + get { + CGPoint(x: minX, y: minY) + } + set { + origin = CGPoint( + x: newValue.x, + y: newValue.y) + } + } + + /// The bottom left point of the rect. Settable. + var bottomLeft: CGPoint { + get { + CGPoint(x: minX, y: maxY) + } + set { + origin = CGPoint( + x: newValue.x, + y: newValue.y - size.height) + } + } + + /// The top right point of the rect. Settable. + var topRight: CGPoint { + get { + CGPoint(x: maxX, y: minY) + } + set { + origin = CGPoint( + x: newValue.x - size.width, + y: newValue.y) + } + } + + /// The bottom right point of the rect. Settable. + var bottomRight: CGPoint { + get { + CGPoint(x: maxX, y: maxY) + } + set { + origin = CGPoint( + x: newValue.x - size.width, + y: newValue.y - size.height) + } + } + + /** + Interpolates the receiver to the given rect by Amount. + - Parameter to: The rect to interpolate to. + - Parameter amount: The amount to interpolate from 0-1 + + ``` + let rect = CGRect(x:0, y:0, width: 50, height: 50) + let interpolated = rect.interpolateTo(CGRect(x:100, y:100, width: 100, height: 100), amount: 0.5) + print(interpolated) + // Result: (x: 50, y: 50, width: 75, height: 75) + ``` + + 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. + */ + func interpolateTo(_ to: CGRect, amount: CGFloat) -> CGRect { + CGRect( + x: origin.x.interpolateTo(to.origin.x, amount: amount), + y: origin.y.interpolateTo(to.origin.y, amount: amount), + width: width.interpolateTo(to.width, amount: amount), + height: height.interpolateTo(to.height, amount: amount)) + } + +} + +extension CGSize { + + /// Operator convenience to add sizes with + + static func +(left: CGSize, right: CGSize) -> CGSize { + left.add(right) + } + + /// Operator convenience to subtract sizes with - + static func -(left: CGSize, right: CGSize) -> CGSize { + left.subtract(right) + } + + /// Operator convenience to multiply sizes with * + static func *(left: CGSize, right: CGFloat) -> CGSize { + CGSize(width: left.width * right, height: left.height * right) + } + + /** + Interpolates the receiver to the given size by Amount. + - Parameter to: The size to interpolate to. + - Parameter amount: The amount to interpolate from 0-1 + + ``` + let size = CGSize(width: 50, height: 50) + let interpolated = rect.interpolateTo(CGSize(width: 100, height: 100), amount: 0.5) + print(interpolated) + // Result: (width: 75, height: 75) + ``` + + 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. + */ + func interpolateTo(_ to: CGSize, amount: CGFloat) -> CGSize { + CGSize( + width: width.interpolateTo(to.width, amount: amount), + height: height.interpolateTo(to.height, amount: amount)) + } + + /// Returns the scale float that will fit the receive inside of the given size. + func scaleThatFits(_ size: CGSize) -> CGFloat { + CGFloat.minimum(width / size.width, height / size.height) + } + + /// Adds receiver size to give size. + func add(_ size: CGSize) -> CGSize { + CGSize(width: width + size.width, height: height + size.height) + } + + /// Subtracts given size from receiver size. + func subtract(_ size: CGSize) -> CGSize { + CGSize(width: width - size.width, height: height - size.height) + } + + /// Multiplies receiver size by the given size. + func multiply(_ size: CGSize) -> CGSize { + CGSize(width: width * size.width, height: height * size.height) + } +} + +// MARK: - CGLine + +/// A struct that defines a line segment with two CGPoints +struct CGLine { + + // MARK: Lifecycle + + /// Initializes a line segment with start and end points + init(start: CGPoint, end: CGPoint) { + self.start = start + self.end = end + } + + // MARK: Internal + + /// The Start of the line segment. + var start: CGPoint + /// The End of the line segment. + var end: CGPoint + + /// The length of the line segment. + var length: CGFloat { + end.distanceTo(start) + } + + /// Returns a line segment that is normalized to a length of 1 + func normalize() -> CGLine { + let len = length + guard len > 0 else { + return self + } + let relativeEnd = end - start + let relativeVector = CGPoint(x: relativeEnd.x / len, y: relativeEnd.y / len) + let absoluteVector = relativeVector + start + return CGLine(start: start, end: absoluteVector) + } + + /// Trims a line segment to the given length + func trimmedToLength(_ toLength: CGFloat) -> CGLine { + let len = length + guard len > 0 else { + return self + } + let relativeEnd = end - start + let relativeVector = CGPoint(x: relativeEnd.x / len, y: relativeEnd.y / len) + let sizedVector = CGPoint(x: relativeVector.x * toLength, y: relativeVector.y * toLength) + let absoluteVector = sizedVector + start + return CGLine(start: start, end: absoluteVector) + } + + /// Flips a line vertically and horizontally from the start point. + func flipped() -> CGLine { + let relativeEnd = end - start + let flippedEnd = CGPoint(x: relativeEnd.x * -1, y: relativeEnd.y * -1) + return CGLine(start: start, end: flippedEnd + start) + } + + /// Move the line to the new start point. + func transpose(_ toPoint: CGPoint) -> CGLine { + let diff = toPoint - start + let newEnd = end + diff + return CGLine(start: toPoint, end: newEnd) + } + +} + +infix operator +| +infix operator +- + +// MARK: - CGPoint + Interpolatable + +extension CGPoint: Interpolatable { + + /// Returns the length between the receiver and *CGPoint.zero* + var vectorLength: CGFloat { + distanceTo(.zero) + } + + var isZero: Bool { + x == 0 && y == 0 + } + + /// Operator convenience to divide points with / + static func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint { + CGPoint(x: lhs.x / CGFloat(rhs), y: lhs.y / CGFloat(rhs)) + } + + /// Operator convenience to multiply points with * + static func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint { + CGPoint(x: lhs.x * CGFloat(rhs), y: lhs.y * CGFloat(rhs)) + } + + /// Operator convenience to add points with + + static func +(left: CGPoint, right: CGPoint) -> CGPoint { + left.add(right) + } + + /// Operator convenience to subtract points with - + static func -(left: CGPoint, right: CGPoint) -> CGPoint { + left.subtract(right) + } + + static func +|(left: CGPoint, right: CGFloat) -> CGPoint { + CGPoint(x: left.x, y: left.y + right) + } + + static func +-(left: CGPoint, right: CGFloat) -> CGPoint { + CGPoint(x: left.x + right, y: left.y) + } + + /// Returns the distance between the receiver and the given point. + func distanceTo(_ a: CGPoint) -> CGFloat { + let xDist = a.x - x + let yDist = a.y - y + return CGFloat(sqrt((xDist * xDist) + (yDist * yDist))) + } + + func rounded(decimal: CGFloat) -> CGPoint { + CGPoint(x: round(decimal * x) / decimal, y: round(decimal * y) / decimal) + } + + /** + Interpolates the receiver to the given Point by Amount. + - Parameter to: The Point to interpolate to. + - Parameter amount: The amount to interpolate from 0-1 + + ``` + let point = CGPoint(width: 50, height: 50) + let interpolated = rect.interpolateTo(CGPoint(width: 100, height: 100), amount: 0.5) + print(interpolated) + // Result: (x: 75, y: 75) + ``` + + 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. + */ + + func interpolate(_ to: CGPoint, amount: CGFloat) -> CGPoint { + CGPoint( + x: x.interpolateTo(to.x, amount: amount), + y: y.interpolateTo(to.y, amount: amount)) + } + + func interpolate( + _ to: CGPoint, + outTangent: CGPoint, + inTangent: CGPoint, + amount: CGFloat, + maxIterations: Int = 3, + samples: Int = 20, + accuracy: CGFloat = 1) + -> CGPoint + { + if amount == 0 { + return self + } + if amount == 1 { + return to + } + + if + colinear(outTangent, inTangent) == true, + outTangent.colinear(inTangent, to) == true + { + return interpolate(to, amount: amount) + } + + let step = 1 / CGFloat(samples) + + var points: [(point: CGPoint, distance: CGFloat)] = [(point: self, distance: 0)] + var totalLength: CGFloat = 0 + + var previousPoint = self + var previousAmount = CGFloat(0) + + var closestPoint: Int = 0 + + while previousAmount < 1 { + + previousAmount = previousAmount + step + + if previousAmount < amount { + closestPoint = closestPoint + 1 + } + + let newPoint = pointOnPath(to, outTangent: outTangent, inTangent: inTangent, amount: previousAmount) + let distance = previousPoint.distanceTo(newPoint) + totalLength = totalLength + distance + points.append((point: newPoint, distance: totalLength)) + previousPoint = newPoint + } + + let accurateDistance = amount * totalLength + var point = points[closestPoint] + + var foundPoint: Bool = false + + var pointAmount = CGFloat(closestPoint) * step + var nextPointAmount: CGFloat = pointAmount + step + + var refineIterations = 0 + while foundPoint == false { + refineIterations = refineIterations + 1 + /// First see if the next point is still less than the projected length. + let nextPoint = points[closestPoint + 1] + if nextPoint.distance < accurateDistance { + point = nextPoint + closestPoint = closestPoint + 1 + pointAmount = CGFloat(closestPoint) * step + nextPointAmount = pointAmount + step + if closestPoint == points.count { + foundPoint = true + } + continue + } + if accurateDistance < point.distance { + closestPoint = closestPoint - 1 + if closestPoint < 0 { + foundPoint = true + continue + } + point = points[closestPoint] + pointAmount = CGFloat(closestPoint) * step + nextPointAmount = pointAmount + step + continue + } + + /// Now we are certain the point is the closest point under the distance + let pointDiff = nextPoint.distance - point.distance + let proposedPointAmount = ((accurateDistance - point.distance) / pointDiff) + .remap(fromLow: 0, fromHigh: 1, toLow: pointAmount, toHigh: nextPointAmount) + + let newPoint = pointOnPath(to, outTangent: outTangent, inTangent: inTangent, amount: proposedPointAmount) + let newDistance = point.distance + point.point.distanceTo(newPoint) + pointAmount = proposedPointAmount + point = (point: newPoint, distance: newDistance) + if + accurateDistance - newDistance <= accuracy || + newDistance - accurateDistance <= accuracy + { + foundPoint = true + } + + if refineIterations == maxIterations { + foundPoint = true + } + } + return point.point + } + + func pointOnPath(_ to: CGPoint, outTangent: CGPoint, inTangent: CGPoint, amount: CGFloat) -> CGPoint { + let a = interpolate(outTangent, amount: amount) + let b = outTangent.interpolate(inTangent, amount: amount) + let c = inTangent.interpolate(to, amount: amount) + let d = a.interpolate(b, amount: amount) + let e = b.interpolate(c, amount: amount) + let f = d.interpolate(e, amount: amount) + return f + } + + func colinear(_ a: CGPoint, _ b: CGPoint) -> Bool { + let area = x * (a.y - b.y) + a.x * (b.y - y) + b.x * (y - a.y); + let accuracy: CGFloat = 0.05 + if area < accuracy && area > -accuracy { + return true + } + return false + } + + func interpolateTo(_ to: CGPoint, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> CGPoint { + guard + let outTan = spatialOutTangent, + let inTan = spatialInTangent else + { + return interpolate(to, amount: amount) + } + let cp1 = self + outTan + let cp2 = to + inTan + + return interpolate(to, outTangent: cp1, inTangent: cp2, amount: amount) + } + + /// Subtracts the given point from the receiving point. + func subtract(_ point: CGPoint) -> CGPoint { + CGPoint( + x: x - point.x, + y: y - point.y) + } + + /// Adds the given point from the receiving point. + func add(_ point: CGPoint) -> CGPoint { + CGPoint( + x: x + point.x, + y: y + point.y) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift new file mode 100644 index 00000000..800d4d68 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift @@ -0,0 +1,33 @@ +// +// StringExtensions.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import CoreGraphics +import Foundation + +extension String { + + func hexColorComponents() -> (red: CGFloat, green: CGFloat, blue: CGFloat) { + + var cString: String = trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + + if cString.hasPrefix("#") { + cString.remove(at: cString.startIndex) + } + + if (cString.count) != 6 { + return (red: 0, green: 0, blue: 0) + } + + var rgbValue: UInt64 = 0 + Scanner(string: cString).scanHexInt64(&rgbValue) + + return ( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift new file mode 100644 index 00000000..cb99d13b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift @@ -0,0 +1,78 @@ +// +// AnimationContext.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/1/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +/// A completion block for animations. `true` is passed in if the animation completed playing. +public typealias LottieCompletionBlock = (Bool) -> Void + +// MARK: - AnimationContext + +struct AnimationContext { + + init( + playFrom: CGFloat, + playTo: CGFloat, + closure: LottieCompletionBlock?) + { + self.playTo = playTo + self.playFrom = playFrom + self.closure = AnimationCompletionDelegate(completionBlock: closure) + } + + var playFrom: CGFloat + var playTo: CGFloat + var closure: AnimationCompletionDelegate + +} + +// MARK: - AnimationContextState + +enum AnimationContextState { + case playing + case cancelled + case complete +} + +// MARK: - AnimationCompletionDelegate + +class AnimationCompletionDelegate: NSObject, CAAnimationDelegate { + + // MARK: Lifecycle + + init(completionBlock: LottieCompletionBlock?) { + self.completionBlock = completionBlock + super.init() + } + + // MARK: Public + + public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { + guard ignoreDelegate == false else { return } + animationState = flag ? .complete : .cancelled + if let animationLayer = animationLayer, let key = animationKey { + animationLayer.removeAnimation(forKey: key) + if flag { + animationLayer.currentFrame = (anim as! CABasicAnimation).toValue as! CGFloat + } + } + if let completionBlock = completionBlock { + completionBlock(flag) + } + } + + // MARK: Internal + + var animationLayer: AnimationContainer? + var animationKey: String? + var ignoreDelegate: Bool = false + var animationState: AnimationContextState = .playing + + let completionBlock: LottieCompletionBlock? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift new file mode 100644 index 00000000..1929b99c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift @@ -0,0 +1,19 @@ +// +// Interpolatable.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/14/19. +// + +import CoreGraphics +import Foundation + +protocol Interpolatable { + + func interpolateTo( + _ to: Self, + amount: CGFloat, + spatialOutTangent: CGPoint?, + spatialInTangent: CGPoint?) -> Self + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift new file mode 100644 index 00000000..fc48c329 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift @@ -0,0 +1,217 @@ +// +// InterpolatableExtensions.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/14/19. +// + +import CoreGraphics +import Foundation + +// MARK: - Vector1D + Interpolatable + +extension Vector1D: Interpolatable { + func interpolateTo(_ to: Vector1D, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> Vector1D { + value.interpolateTo(to.value, amount: amount).vectorValue + } +} + +// MARK: - Vector2D + Interpolatable + +extension Vector2D: Interpolatable { + func interpolateTo(_ to: Vector2D, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> Vector2D { + pointValue.interpolateTo( + to.pointValue, + amount: CGFloat(amount), + spatialOutTangent: spatialOutTangent, + spatialInTangent: spatialInTangent).vector2dValue + } + +} + +// MARK: - Vector3D + Interpolatable + +extension Vector3D: Interpolatable { + func interpolateTo(_ to: Vector3D, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> Vector3D { + if spatialInTangent != nil || spatialOutTangent != nil { + // TODO Support third dimension spatial interpolation + let point = pointValue.interpolateTo( + to.pointValue, + amount: amount, + spatialOutTangent: spatialOutTangent, + spatialInTangent: spatialInTangent) + return Vector3D( + x: point.x, + y: point.y, + z: CGFloat(z.interpolateTo(to.z, amount: amount))) + } + return Vector3D( + x: x.interpolateTo(to.x, amount: amount), + y: y.interpolateTo(to.y, amount: amount), + z: z.interpolateTo(to.z, amount: amount)) + } +} + +// MARK: - Color + Interpolatable + +extension Color: Interpolatable { + + // MARK: Lifecycle + + /// Initialize a new color with Hue Saturation and Value + init(h: Double, s: Double, v: Double, a: Double) { + + let i = floor(h * 6) + let f = h * 6 - i + let p = v * (1 - s); + let q = v * (1 - f * s) + let t = v * (1 - (1 - f) * s) + + switch i.truncatingRemainder(dividingBy: 6) { + case 0: + r = v + g = t + b = p + case 1: + r = q + g = v + b = p + case 2: + r = p + g = v + b = t + case 3: + r = p + g = q + b = v + case 4: + r = t + g = p + b = v + case 5: + r = v + g = p + b = q + default: + r = 0 + g = 0 + b = 0 + } + self.a = a + } + + init(y: Double, u: Double, v: Double, a: Double) { + // From https://www.fourcc.org/fccyvrgb.php + r = y + 1.403 * v + g = y - 0.344 * u + b = y + 1.770 * u + self.a = a + } + + // MARK: Internal + + /// Hue Saturation Value of the color. + var hsva: (h: Double, s: Double, v: Double, a: Double) { + let maxValue = max(r, g, b) + let minValue = min(r, g, b) + + var h: Double, s: Double, v: Double = maxValue + + let d = maxValue - minValue + s = maxValue == 0 ? 0 : d / maxValue; + + if maxValue == minValue { + h = 0; // achromatic + } else { + switch maxValue { + case r: h = (g - b) / d + (g < b ? 6 : 0) + case g: h = (b - r) / d + 2 + case b: h = (r - g) / d + 4 + default: h = maxValue + } + h = h / 6 + } + return (h: h, s: s, v: v, a: a) + } + + var yuv: (y: Double, u: Double, v: Double, a: Double) { + /// From https://www.fourcc.org/fccyvrgb.php + let y = 0.299 * r + 0.587 * g + 0.114 * b + let u = -0.14713 * r - 0.28886 * g + 0.436 * b + let v = 0.615 * r - 0.51499 * g - 0.10001 * b + return (y: y, u: u, v: v, a: a) + } + + func interpolateTo(_ to: Color, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> Color { + Color( + r: r.interpolateTo(to.r, amount: amount), + g: g.interpolateTo(to.g, amount: amount), + b: b.interpolateTo(to.b, amount: amount), + a: a.interpolateTo(to.a, amount: amount)) + } +} + +// MARK: - CurveVertex + Interpolatable + +extension CurveVertex: Interpolatable { + func interpolateTo( + _ to: CurveVertex, + amount: CGFloat, + spatialOutTangent _: CGPoint?, + spatialInTangent _: CGPoint?) + -> CurveVertex + { + CurveVertex( + point: point.interpolate(to.point, amount: amount), + inTangent: inTangent.interpolate(to.inTangent, amount: amount), + outTangent: outTangent.interpolate(to.outTangent, amount: amount)) + } +} + +// MARK: - BezierPath + Interpolatable + +extension BezierPath: Interpolatable { + func interpolateTo(_ to: BezierPath, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> BezierPath { + var newPath = BezierPath() + for i in 0.. TextDocument + { + if amount == 1 { + return to + } + return self + } +} + +// MARK: - Array + Interpolatable + +extension Array: Interpolatable where Element == Double { + func interpolateTo(_ to: [Element], amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> [Element] { + var returnArray = [Double]() + for i in 0.. CGFloat { + let startTime = time + let endTime = to.time + if keyTime <= startTime { + return 0 + } + if endTime <= keyTime { + return 1 + } + + if isHold { + return 0 + } + + let outTanPoint = outTangent?.pointValue ?? .zero + let inTanPoint = to.inTangent?.pointValue ?? CGPoint(x: 1, y: 1) + var progress: CGFloat = keyTime.remap(fromLow: startTime, fromHigh: endTime, toLow: 0, toHigh: 1) + if !outTanPoint.isZero || !inTanPoint.equalTo(CGPoint(x: 1, y: 1)) { + /// Cubic interpolation + progress = progress.cubicBezierInterpolate(.zero, outTanPoint, inTanPoint, CGPoint(x: 1, y: 1)) + } + return progress + } + + /// Interpolates the keyframes' by a progress from 0-1 + func interpolate(_ to: Keyframe, progress: CGFloat) -> T { + value.interpolateTo( + to.value, + amount: progress, + spatialOutTangent: spatialOutTangent?.pointValue, + spatialInTangent: to.spatialInTangent?.pointValue) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift new file mode 100644 index 00000000..5e5d743a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift @@ -0,0 +1,423 @@ +// +// Shape.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/8/19. +// + +import CoreGraphics +import Foundation + +// MARK: - BezierPath + +/// A container that holds instructions for creating a single, unbroken Bezier Path. +struct BezierPath { + + // MARK: Lifecycle + + /// Initializes a new Bezier Path. + init(startPoint: CurveVertex) { + elements = [PathElement(vertex: startPoint)] + length = 0 + closed = false + } + + init() { + elements = [] + length = 0 + closed = false + } + + // MARK: Internal + + /// The elements of the path + fileprivate(set) var elements: [PathElement] + + /// If the path is closed or not. + fileprivate(set) var closed: Bool + + /// The total length of the path. + fileprivate(set) var length: CGFloat + + mutating func moveToStartPoint(_ vertex: CurveVertex) { + elements = [PathElement(vertex: vertex)] + length = 0 + } + + mutating func addVertex(_ vertex: CurveVertex) { + guard let previous = elements.last else { + addElement(PathElement(vertex: vertex)) + return + } + addElement(previous.pathElementTo(vertex)) + } + + mutating func addCurve(toPoint: CGPoint, outTangent: CGPoint, inTangent: CGPoint) { + guard let previous = elements.last else { return } + let newVertex = CurveVertex(inTangent, toPoint, toPoint) + updateVertex( + CurveVertex(previous.vertex.inTangent, previous.vertex.point, outTangent), + atIndex: elements.endIndex - 1, + remeasure: false) + addVertex(newVertex) + } + + mutating func addLine(toPoint: CGPoint) { + guard let previous = elements.last else { return } + let newVertex = CurveVertex(point: toPoint, inTangentRelative: .zero, outTangentRelative: .zero) + updateVertex( + CurveVertex(previous.vertex.inTangent, previous.vertex.point, previous.vertex.point), + atIndex: elements.endIndex - 1, + remeasure: false) + addVertex(newVertex) + } + + mutating func close() { + closed = true + } + + mutating func addElement(_ pathElement: PathElement) { + elements.append(pathElement) + length = length + pathElement.length + } + + mutating func updateVertex(_ vertex: CurveVertex, atIndex: Int, remeasure: Bool) { + if remeasure { + var newElement: PathElement + if atIndex > 0 { + let previousElement = elements[atIndex - 1] + newElement = previousElement.pathElementTo(vertex) + } else { + newElement = PathElement(vertex: vertex) + } + elements[atIndex] = newElement + + if atIndex + 1 < elements.count{ + let nextElement = elements[atIndex + 1] + elements[atIndex + 1] = newElement.pathElementTo(nextElement.vertex) + } + + } else { + let oldElement = elements[atIndex] + elements[atIndex] = oldElement.updateVertex(newVertex: vertex) + } + } + + /** + Trims a path fromLength toLength with an offset. + + Length and offset are defined in the length coordinate space. + If any argument is outside the range of this path, then it will be looped over the path from finish to start. + + Cutting the curve when fromLength is less than toLength + x x x x + ~~~~~~~~~~~~~~~ooooooooooooooooooooooooooooooooooooooooooooooooo------------------- + |Offset |fromLength toLength| | + + Cutting the curve when from Length is greater than toLength + x x x x x + oooooooooooooooooo--------------------~~~~~~~~~~~~~~~~ooooooooooooooooooooooooooooo + | toLength| |Offset |fromLength | + + */ + func trim(fromLength: CGFloat, toLength: CGFloat, offsetLength: CGFloat) -> [BezierPath] { + guard elements.count > 1 else { + return [] + } + + if fromLength == toLength { + return [] + } + + /// Normalize lengths to the curve length. + var start = (fromLength + offsetLength).truncatingRemainder(dividingBy: length) + var end = (toLength + offsetLength).truncatingRemainder(dividingBy: length) + + if start < 0 { + start = length + start + } + + if end < 0 { + end = length + end + } + + if start == length { + start = 0 + } + if end == 0 { + end = length + } + + if + start == 0 && end == length || + start == end || + start == length && end == 0 + { + /// The trim encompasses the entire path. Return. + return [self] + } + + if start > end { + // Start is greater than end. Two paths are returned. + return trimPathAtLengths(positions: [(start: 0, end: end), (start: start, end: length)]) + } + + return trimPathAtLengths(positions: [(start: start, end: end)]) + } + + // MARK: Fileprivate + + fileprivate func trimPathAtLengths(positions: [(start: CGFloat, end: CGFloat)]) -> [BezierPath] { + guard positions.count > 0 else { + return [] + } + var remainingPositions = positions + + var trim = remainingPositions.remove(at: 0) + + var paths = [BezierPath]() + + var runningLength: CGFloat = 0 + var finishedTrimming: Bool = false + var pathElements = elements + + var currentPath = BezierPath() + var i: Int = 0 + + while !finishedTrimming { + if pathElements.count <= i { + /// Do this for rounding errors + paths.append(currentPath) + finishedTrimming = true + continue + } + /// Loop through and add elements within start->end range. + /// Get current element + let element = pathElements[i] + + /// Calculate new running length. + let newLength = runningLength + element.length + + if newLength < trim.start { + /// Element is not included in the trim, continue. + runningLength = newLength + i = i + 1 + /// Increment index, we are done with this element. + continue + } + + if newLength == trim.start { + /// Current element IS the start element. + /// For start we want to add a zero length element. + currentPath.moveToStartPoint(element.vertex) + runningLength = newLength + i = i + 1 + /// Increment index, we are done with this element. + continue + } + + if runningLength < trim.start, trim.start < newLength, currentPath.elements.count == 0 { + /// The start of the trim is between this element and the previous, trim. + /// Get previous element. + let previousElement = pathElements[i - 1] + /// Trim it + let trimLength = trim.start - runningLength + let trimResults = element.splitElementAtPosition(fromElement: previousElement, atLength: trimLength) + /// Add the right span start. + currentPath.moveToStartPoint(trimResults.rightSpan.start.vertex) + + pathElements[i] = trimResults.rightSpan.end + pathElements[i - 1] = trimResults.rightSpan.start + runningLength = runningLength + trimResults.leftSpan.end.length + /// Dont increment index or the current length, the end of this path can be within this span. + continue + } + + if trim.start < newLength, newLength < trim.end { + /// Element lies within the trim span. + currentPath.addElement(element) + runningLength = newLength + i = i + 1 + continue + } + + if newLength == trim.end { + /// Element is the end element. + /// The element could have a new length if it's added right after the start node. + currentPath.addElement(element) + /// We are done with this span. + runningLength = newLength + i = i + 1 + /// Allow the path to be finalized. + /// Fall through to finalize path and move to next position + } + + if runningLength < trim.end, trim.end < newLength { + /// New element must be cut for end. + /// Get previous element. + let previousElement = pathElements[i - 1] + /// Trim it + let trimLength = trim.end - runningLength + let trimResults = element.splitElementAtPosition(fromElement: previousElement, atLength: trimLength) + /// Add the left span end. + + currentPath.updateVertex(trimResults.leftSpan.start.vertex, atIndex: currentPath.elements.count - 1, remeasure: false) + currentPath.addElement(trimResults.leftSpan.end) + + pathElements[i] = trimResults.rightSpan.end + pathElements[i - 1] = trimResults.rightSpan.start + runningLength = runningLength + trimResults.leftSpan.end.length + /// Dont increment index or the current length, the start of the next path can be within this span. + /// We are done with this span. + /// Allow the path to be finalized. + /// Fall through to finalize path and move to next position + } + + paths.append(currentPath) + currentPath = BezierPath() + if remainingPositions.count > 0 { + trim = remainingPositions.remove(at: 0) + } else { + finishedTrimming = true + } + } + return paths + } + +} + +// MARK: Codable + +extension BezierPath: Codable { + + // MARK: Lifecycle + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer + + if let keyedContainer = try? decoder.container(keyedBy: BezierPath.CodingKeys.self) { + container = keyedContainer + } else { + var unkeyedContainer = try decoder.unkeyedContainer() + container = try unkeyedContainer.nestedContainer(keyedBy: BezierPath.CodingKeys.self) + } + + closed = try container.decodeIfPresent(Bool.self, forKey: .closed) ?? true + + var vertexContainer = try container.nestedUnkeyedContainer(forKey: .vertices) + var inPointsContainer = try container.nestedUnkeyedContainer(forKey: .inPoints) + var outPointsContainer = try container.nestedUnkeyedContainer(forKey: .outPoints) + + guard vertexContainer.count == inPointsContainer.count, inPointsContainer.count == outPointsContainer.count else { + /// Will throw an error if vertex, inpoints, and outpoints are not the same length. + /// This error is to be expected. + throw DecodingError.dataCorruptedError( + forKey: CodingKeys.vertices, + in: container, + debugDescription: "Vertex data does not match In Tangents and Out Tangents") + } + + guard let count = vertexContainer.count, count > 0 else { + length = 0 + elements = [] + return + } + + var decodedElements = [PathElement]() + + /// Create first point + let firstVertex = CurveVertex( + point: try vertexContainer.decode(CGPoint.self), + inTangentRelative: try inPointsContainer.decode(CGPoint.self), + outTangentRelative: try outPointsContainer.decode(CGPoint.self)) + var previousElement = PathElement(vertex: firstVertex) + decodedElements.append(previousElement) + + var totalLength: CGFloat = 0 + while !vertexContainer.isAtEnd { + /// Get the next vertex data. + let vertex = CurveVertex( + point: try vertexContainer.decode(CGPoint.self), + inTangentRelative: try inPointsContainer.decode(CGPoint.self), + outTangentRelative: try outPointsContainer.decode(CGPoint.self)) + let pathElement = previousElement.pathElementTo(vertex) + decodedElements.append(pathElement) + previousElement = pathElement + totalLength = totalLength + pathElement.length + } + if closed { + let closeElement = previousElement.pathElementTo(firstVertex) + decodedElements.append(closeElement) + totalLength = totalLength + closeElement.length + } + length = totalLength + elements = decodedElements + } + + // MARK: Internal + + /** + The BezierPath container is encoded and decoded from the JSON format + that defines points for a lottie animation. + + { + "c" = Bool + "i" = [[Double]], + "o" = [[Double]], + "v" = [[Double]] + } + + */ + + enum CodingKeys: String, CodingKey { + case closed = "c" + case inPoints = "i" + case outPoints = "o" + case vertices = "v" + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: BezierPath.CodingKeys.self) + try container.encode(closed, forKey: .closed) + + var vertexContainer = container.nestedUnkeyedContainer(forKey: .vertices) + var inPointsContainer = container.nestedUnkeyedContainer(forKey: .inPoints) + var outPointsContainer = container.nestedUnkeyedContainer(forKey: .outPoints) + + /// If closed path, ignore the final element. + let finalIndex = closed ? elements.endIndex - 1 : elements.endIndex + for i in 0.. CGPath { + let cgPath = CGMutablePath() + + var previousElement: PathElement? + for element in elements { + if let previous = previousElement { + if previous.vertex.outTangentRelative.isZero && element.vertex.inTangentRelative.isZero { + cgPath.addLine(to: element.vertex.point) + } else { + cgPath.addCurve(to: element.vertex.point, control1: previous.vertex.outTangent, control2: element.vertex.inTangent) + } + } else { + cgPath.move(to: element.vertex.point) + } + previousElement = element + } + if closed { + cgPath.closeSubpath() + } + return cgPath + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift new file mode 100644 index 00000000..7001252b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift @@ -0,0 +1,82 @@ +// +// Color.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/14/19. +// + +import CoreGraphics +import Foundation + +// MARK: - Color + Codable + +extension Color: Codable { + + // MARK: Lifecycle + + public init(from decoder: Decoder) throws { + var container = try decoder.unkeyedContainer() + + var r1: Double + if !container.isAtEnd { + r1 = try container.decode(Double.self) + } else { + r1 = 0 + } + + var g1: Double + if !container.isAtEnd { + g1 = try container.decode(Double.self) + } else { + g1 = 0 + } + + var b1: Double + if !container.isAtEnd { + b1 = try container.decode(Double.self) + } else { + b1 = 0 + } + + var a1: Double + if !container.isAtEnd { + a1 = try container.decode(Double.self) + } else { + a1 = 1 + } + if r1 > 1, g1 > 1, b1 > 1, a1 > 1 { + r1 = r1 / 255 + g1 = g1 / 255 + b1 = b1 / 255 + a1 = a1 / 255 + } + r = r1 + g = g1 + b = b1 + a = a1 + } + + // MARK: Public + + public func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + try container.encode(r) + try container.encode(g) + try container.encode(b) + try container.encode(a) + } + +} + +extension Color { + + static var clearColor: CGColor { + CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 0])! + } + + var cgColorValue: CGColor { + // TODO: Fix color spaces + let colorspace = CGColorSpaceCreateDeviceRGB() + return CGColor(colorSpace: colorspace, components: [CGFloat(r), CGFloat(g), CGFloat(b), CGFloat(a)]) ?? Color.clearColor + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift new file mode 100644 index 00000000..f425bf30 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift @@ -0,0 +1,169 @@ +// +// CompoundBezierPath.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/14/19. +// + +import CoreGraphics +import Foundation + +/** + A collection of BezierPath objects that can be trimmed and added. + + */ +struct CompoundBezierPath { + + // MARK: Lifecycle + + init() { + paths = [] + length = 0 + } + + init(path: BezierPath) { + paths = [path] + length = path.length + } + + init(paths: [BezierPath], length: CGFloat) { + self.paths = paths + self.length = length + } + + init(paths: [BezierPath]) { + self.paths = paths + var l: CGFloat = 0 + for path in paths { + l = l + path.length + } + length = l + } + + // MARK: Internal + + let paths: [BezierPath] + + let length: CGFloat + + func addPath(path: BezierPath) -> CompoundBezierPath { + var newPaths = paths + newPaths.append(path) + return CompoundBezierPath(paths: newPaths, length: length + path.length) + } + + func combine(_ compoundBezier: CompoundBezierPath) -> CompoundBezierPath { + var newPaths = paths + newPaths.append(contentsOf: compoundBezier.paths) + return CompoundBezierPath(paths: newPaths, length: length + compoundBezier.length) + } + + func trim(fromPosition: CGFloat, toPosition: CGFloat, offset: CGFloat, trimSimultaneously: Bool) -> CompoundBezierPath { + if fromPosition == toPosition { + return CompoundBezierPath() + } + + if trimSimultaneously { + /// Trim each path individually. + var newPaths = [BezierPath]() + for path in paths { + newPaths.append(contentsOf: path.trim( + fromLength: fromPosition * path.length, + toLength: toPosition * path.length, + offsetLength: offset * path.length)) + } + return CompoundBezierPath(paths: newPaths) + } + + /// Normalize lengths to the curve length. + var startPosition = (fromPosition + offset).truncatingRemainder(dividingBy: 1) + var endPosition = (toPosition + offset).truncatingRemainder(dividingBy: 1) + + if startPosition < 0 { + startPosition = 1 + startPosition + } + + if endPosition < 0 { + endPosition = 1 + endPosition + } + + if startPosition == 1 { + startPosition = 0 + } + if endPosition == 0 { + endPosition = 1 + } + + if + startPosition == 0 && endPosition == 1 || + startPosition == endPosition || + startPosition == 1 && endPosition == 0 + { + /// The trim encompasses the entire path. Return. + return self + } + + var positions: [(start: CGFloat, end: CGFloat)] + if endPosition < startPosition { + positions = [ + (start: 0, end: endPosition * length), + (start: startPosition * length, end: length), + ] + } else { + positions = [(start: startPosition * length, end: endPosition * length)] + } + + var compoundPath = CompoundBezierPath() + var trim = positions.remove(at: 0) + var pathStartPosition: CGFloat = 0 + + var finishedTrimming: Bool = false + var i: Int = 0 + + while !finishedTrimming { + if paths.count <= i { + /// Rounding errors + finishedTrimming = true + continue + } + let path = paths[i] + + let pathEndPosition = pathStartPosition + path.length + + if pathEndPosition < trim.start { + /// Path is not included in the trim, continue. + pathStartPosition = pathEndPosition + i = i + 1 + continue + + } else if trim.start <= pathStartPosition, pathEndPosition <= trim.end { + /// Full Path is inside of trim. Add full path. + compoundPath = compoundPath.addPath(path: path) + } else { + if + let trimPath = path.trim( + fromLength: trim.start > pathStartPosition ? (trim.start - pathStartPosition) : 0, + toLength: trim.end < pathEndPosition ? (trim.end - pathStartPosition) : path.length, + offsetLength: 0).first + { + compoundPath = compoundPath.addPath(path: trimPath) + } + } + + if trim.end <= pathEndPosition { + /// We are done with the current trim. + /// Advance trim but remain on the same path in case the next trim overlaps it. + if positions.count > 0 { + trim = positions.remove(at: 0) + } else { + finishedTrimming = true + } + } else { + pathStartPosition = pathEndPosition + i = i + 1 + } + } + return compoundPath + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift new file mode 100644 index 00000000..c9f9fbd9 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift @@ -0,0 +1,192 @@ +// +// CurveVertex.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/11/19. +// + +import CoreGraphics +import Foundation + +/// A single vertex with an in and out tangent +struct CurveVertex { + + // MARK: Lifecycle + + /// Initializes a curve point with absolute values + init(_ inTangent: CGPoint, _ point: CGPoint, _ outTangent: CGPoint) { + self.point = point + self.inTangent = inTangent + self.outTangent = outTangent + } + + /// Initializes a curve point with relative values + init(point: CGPoint, inTangentRelative: CGPoint, outTangentRelative: CGPoint) { + self.point = point + inTangent = point.add(inTangentRelative) + outTangent = point.add(outTangentRelative) + } + + /// Initializes a curve point with absolute values + init(point: CGPoint, inTangent: CGPoint, outTangent: CGPoint) { + self.point = point + self.inTangent = inTangent + self.outTangent = outTangent + } + + // MARK: Internal + + let point: CGPoint + + let inTangent: CGPoint + let outTangent: CGPoint + + var inTangentRelative: CGPoint { + inTangent.subtract(point) + } + + var outTangentRelative: CGPoint { + outTangent.subtract(point) + } + + func reversed() -> CurveVertex { + CurveVertex(point: point, inTangent: outTangent, outTangent: inTangent) + } + + func translated(_ translation: CGPoint) -> CurveVertex { + CurveVertex(point: point + translation, inTangent: inTangent + translation, outTangent: outTangent + translation) + } + + /** + Trims a path defined by two Vertices at a specific position, from 0 to 1 + + The path can be visualized below. + + F is fromVertex. + V is the vertex of the receiver. + P is the position from 0-1. + O is the outTangent of fromVertex. + F====O=========P=======I====V + + After trimming the curve can be visualized below. + + S is the returned Start vertex. + E is the returned End vertex. + T is the trim point. + TI and TO are the new tangents for the trimPoint + NO and NI are the new tangents for the startPoint and endPoints + S==NO=========TI==T==TO=======NI==E + */ + func splitCurve(toVertex: CurveVertex, position: CGFloat) -> + (start: CurveVertex, trimPoint: CurveVertex, end: CurveVertex) + { + + /// If position is less than or equal to 0, trim at start. + if position <= 0 { + return ( + start: CurveVertex(point: point, inTangentRelative: inTangentRelative, outTangentRelative: .zero), + trimPoint: CurveVertex(point: point, inTangentRelative: .zero, outTangentRelative: outTangentRelative), + end: toVertex) + } + + /// If position is greater than or equal to 1, trim at end. + if position >= 1 { + return ( + start: self, + trimPoint: CurveVertex( + point: toVertex.point, + inTangentRelative: toVertex.inTangentRelative, + outTangentRelative: .zero), + end: CurveVertex( + point: toVertex.point, + inTangentRelative: .zero, + outTangentRelative: toVertex.outTangentRelative)) + } + + if outTangentRelative.isZero && toVertex.inTangentRelative.isZero { + /// If both tangents are zero, then span to be trimmed is a straight line. + let trimPoint = point.interpolate(toVertex.point, amount: position) + return ( + start: self, + trimPoint: CurveVertex(point: trimPoint, inTangentRelative: .zero, outTangentRelative: .zero), + end: toVertex) + } + /// Cutting by amount gives incorrect length.... + /// One option is to cut by a stride until it gets close then edge it down. + /// Measuring a percentage of the spans does not equal the same as measuring a percentage of length. + /// This is where the historical trim path bugs come from. + let a = point.interpolate(outTangent, amount: position) + let b = outTangent.interpolate(toVertex.inTangent, amount: position) + let c = toVertex.inTangent.interpolate(toVertex.point, amount: position) + let d = a.interpolate(b, amount: position) + let e = b.interpolate(c, amount: position) + let f = d.interpolate(e, amount: position) + return ( + start: CurveVertex(point: point, inTangent: inTangent, outTangent: a), + trimPoint: CurveVertex(point: f, inTangent: d, outTangent: e), + end: CurveVertex(point: toVertex.point, inTangent: c, outTangent: toVertex.outTangent)) + } + + /** + Trims a curve of a known length to a specific length and returns the points. + + There is not a performant yet accurate way to cut a curve to a specific length. + This calls splitCurve(toVertex: position:) to split the curve and then measures + the length of the new curve. The function then iterates through the samples, + adjusting the position of the cut for a more precise cut. + Usually a single iteration is enough to get within 0.5 points of the desired + length. + + This function should probably live in PathElement, since it deals with curve + lengths. + */ + func trimCurve(toVertex: CurveVertex, atLength: CGFloat, curveLength: CGFloat, maxSamples: Int, accuracy: CGFloat = 1) -> + (start: CurveVertex, trimPoint: CurveVertex, end: CurveVertex) + { + var currentPosition = atLength / curveLength + var results = splitCurve(toVertex: toVertex, position: currentPosition) + + if maxSamples == 0 { + return results + } + + for _ in 1...maxSamples { + let length = results.start.distanceTo(results.trimPoint) + let lengthDiff = atLength - length + /// Check if length is correct. + if lengthDiff < accuracy { + return results + } + let diffPosition = max(min((currentPosition / length) * lengthDiff, currentPosition * 0.5), currentPosition * -0.5) + currentPosition = diffPosition + currentPosition + results = splitCurve(toVertex: toVertex, position: currentPosition) + } + return results + } + + /** + The distance from the receiver to the provided vertex. + + For lines (zeroed tangents) the distance between the two points is measured. + For curves the curve is iterated over by sample count and the points are measured. + This is ~99% accurate at a sample count of 30 + */ + func distanceTo(_ toVertex: CurveVertex, sampleCount: Int = 25) -> CGFloat { + + if outTangentRelative.isZero && toVertex.inTangentRelative.isZero { + /// Return a linear distance. + return point.distanceTo(toVertex.point) + } + + var distance: CGFloat = 0 + + var previousPoint = point + for i in 0.. PathElement { + PathElement(length: vertex.distanceTo(toVertex), vertex: toVertex) + } + + func updateVertex(newVertex: CurveVertex) -> PathElement { + PathElement(length: length, vertex: newVertex) + } + + /// Splits an element span defined by the receiver and fromElement to a position 0-1 + func splitElementAtPosition(fromElement: PathElement, atLength: CGFloat) -> + (leftSpan: (start: PathElement, end: PathElement), rightSpan: (start: PathElement, end: PathElement)) + { + /// Trim the span. Start and trim go into the first, trim and end go into second. + let trimResults = fromElement.vertex.trimCurve(toVertex: vertex, atLength: atLength, curveLength: length, maxSamples: 3) + + /// Create the elements for the break + let spanAStart = PathElement( + length: fromElement.length, + vertex: CurveVertex( + point: fromElement.vertex.point, + inTangent: fromElement.vertex.inTangent, + outTangent: trimResults.start.outTangent)) + /// Recalculating the length here is a waste as the trimCurve function also accurately calculates this length. + let spanAEnd = spanAStart.pathElementTo(trimResults.trimPoint) + + let spanBStart = PathElement(vertex: trimResults.trimPoint) + let spanBEnd = spanBStart.pathElementTo(trimResults.end) + return ( + leftSpan: (start: spanAStart, end: spanAEnd), + rightSpan: (start: spanBStart, end: spanBEnd)) + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift new file mode 100644 index 00000000..6dfd7d72 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift @@ -0,0 +1,283 @@ +// +// Vector.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/7/19. +// + +import CoreGraphics +import Foundation +import QuartzCore + +// MARK: - Vector1D + Codable + +/** + Single value container. Needed because lottie sometimes wraps a Double in an array. + */ +extension Vector1D: Codable { + + // MARK: Lifecycle + + public init(from decoder: Decoder) throws { + /// Try to decode an array of doubles + do { + var container = try decoder.unkeyedContainer() + value = try container.decode(Double.self) + } catch { + value = try decoder.singleValueContainer().decode(Double.self) + } + } + + // MARK: Public + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(value) + } + + // MARK: Internal + + var cgFloatValue: CGFloat { + CGFloat(value) + } + +} + +extension Double { + var vectorValue: Vector1D { + Vector1D(self) + } +} + +// MARK: - Vector2D + +/** + Needed for decoding json {x: y:} to a CGPoint + */ +struct Vector2D: Codable { + + // MARK: Lifecycle + + init(x: Double, y: Double) { + self.x = x + self.y = y + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Vector2D.CodingKeys.self) + + do { + let xValue: [Double] = try container.decode([Double].self, forKey: .x) + x = xValue[0] + } catch { + x = try container.decode(Double.self, forKey: .x) + } + + do { + let yValue: [Double] = try container.decode([Double].self, forKey: .y) + y = yValue[0] + } catch { + y = try container.decode(Double.self, forKey: .y) + } + } + + // MARK: Internal + + var x: Double + var y: Double + + var pointValue: CGPoint { + CGPoint(x: x, y: y) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: Vector2D.CodingKeys.self) + try container.encode(x, forKey: .x) + try container.encode(y, forKey: .y) + } + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case x + case y + } +} + +extension Vector2D { + +} + +extension CGPoint { + var vector2dValue: Vector2D { + Vector2D(x: Double(x), y: Double(y)) + } +} + +// MARK: - Vector3D + Codable + +/** + A three dimensional vector. + These vectors are encoded and decoded from [Double] + */ + +extension Vector3D: Codable { + + // MARK: Lifecycle + + init(x: CGFloat, y: CGFloat, z: CGFloat) { + self.x = Double(x) + self.y = Double(y) + self.z = Double(z) + } + + public init(from decoder: Decoder) throws { + var container = try decoder.unkeyedContainer() + + if !container.isAtEnd { + x = try container.decode(Double.self) + } else { + x = 0 + } + + if !container.isAtEnd { + y = try container.decode(Double.self) + } else { + y = 0 + } + + if !container.isAtEnd { + z = try container.decode(Double.self) + } else { + z = 0 + } + } + + // MARK: Public + + public func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + try container.encode(x) + try container.encode(y) + try container.encode(z) + } + +} + +extension Vector3D { + public var pointValue: CGPoint { + CGPoint(x: x, y: y) + } + + public var sizeValue: CGSize { + CGSize(width: x, height: y) + } +} + +extension CGPoint { + var vector3dValue: Vector3D { + Vector3D(x: x, y: y, z: 0) + } +} + +extension CGSize { + var vector3dValue: Vector3D { + Vector3D(x: width, y: height, z: 1) + } +} + +extension CATransform3D { + + static func makeSkew(skew: CGFloat, skewAxis: CGFloat) -> CATransform3D { + let mCos = cos(skewAxis.toRadians()) + let mSin = sin(skewAxis.toRadians()) + let aTan = tan(skew.toRadians()) + + let transform1 = CATransform3D( + m11: mCos, + m12: mSin, + m13: 0, + m14: 0, + m21: -mSin, + m22: mCos, + m23: 0, + m24: 0, + m31: 0, + m32: 0, + m33: 1, + m34: 0, + m41: 0, + m42: 0, + m43: 0, + m44: 1) + + let transform2 = CATransform3D( + m11: 1, + m12: 0, + m13: 0, + m14: 0, + m21: aTan, + m22: 1, + m23: 0, + m24: 0, + m31: 0, + m32: 0, + m33: 1, + m34: 0, + m41: 0, + m42: 0, + m43: 0, + m44: 1) + + let transform3 = CATransform3D( + m11: mCos, + m12: -mSin, + m13: 0, + m14: 0, + m21: mSin, + m22: mCos, + m23: 0, + m24: 0, + m31: 0, + m32: 0, + m33: 1, + m34: 0, + m41: 0, + m42: 0, + m43: 0, + m44: 1) + return CATransform3DConcat(transform3, CATransform3DConcat(transform2, transform1)) + } + + static func makeTransform( + anchor: CGPoint, + position: CGPoint, + scale: CGSize, + rotation: CGFloat, + skew: CGFloat?, + skewAxis: CGFloat?) + -> CATransform3D + { + if let skew = skew, let skewAxis = skewAxis { + return CATransform3DMakeTranslation(position.x, position.y, 0).rotated(rotation).skewed(skew: -skew, skewAxis: skewAxis) + .scaled(scale * 0.01).translated(anchor * -1) + } + return CATransform3DMakeTranslation(position.x, position.y, 0).rotated(rotation).scaled(scale * 0.01).translated(anchor * -1) + } + + func rotated(_ degrees: CGFloat) -> CATransform3D { + CATransform3DRotate(self, degrees.toRadians(), 0, 0, 1) + } + + func translated(_ translation: CGPoint) -> CATransform3D { + CATransform3DTranslate(self, translation.x, translation.y, 0) + } + + func scaled(_ scale: CGSize) -> CATransform3D { + CATransform3DScale(self, scale.width, scale.height, 1) + } + + func skewed(skew: CGFloat, skewAxis: CGFloat) -> CATransform3D { + CATransform3DConcat(CATransform3D.makeSkew(skew: skew, skewAxis: skewAxis), self) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift new file mode 100644 index 00000000..1e8012be --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift @@ -0,0 +1,206 @@ +// +// AnimationPublic.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/5/19. +// + +import CoreGraphics +import Foundation + +extension Animation { + + /// A closure for an Animation download. The closure is passed `nil` if there was an error. + public typealias DownloadClosure = (Animation?) -> Void + + /// The duration in seconds of the animation. + public var duration: TimeInterval { + Double(endFrame - startFrame) / framerate + } + + /// The natural bounds in points of the animation. + public var bounds: CGRect { + CGRect(x: 0, y: 0, width: width, height: height) + } + + /// The natural size in points of the animation. + public var size: CGSize { + CGSize(width: width, height: height) + } + + // MARK: Animation (Loading) + + /** + Loads an animation model from a bundle by its name. Returns `nil` if an animation is not found. + + - Parameter name: The name of the json file without the json extension. EG "StarAnimation" + - Parameter bundle: The bundle in which the animation is located. Defaults to `Bundle.main` + - Parameter subdirectory: A subdirectory in the bundle in which the animation is located. Optional. + - Parameter animationCache: A cache for holding loaded animations. Optional. + + - Returns: Deserialized `Animation`. Optional. + */ + public static func named( + _ name: String, + bundle: Bundle = Bundle.main, + subdirectory: String? = nil, + animationCache: AnimationCacheProvider? = nil) + -> Animation? + { + /// Create a cache key for the animation. + let cacheKey = bundle.bundlePath + (subdirectory ?? "") + "/" + name + + /// Check cache for animation + if + let animationCache = animationCache, + let animation = animationCache.animation(forKey: cacheKey) + { + /// If found, return the animation. + return animation + } + + do { + /// Decode animation. + guard let json = try bundle.getAnimationData(name, subdirectory: subdirectory) else { + return nil + } + let animation = try JSONDecoder().decode(Animation.self, from: json) + animationCache?.setAnimation(animation, forKey: cacheKey) + return animation + } catch { + /// Decoding error. + print(error) + return nil + } + } + + /** + Loads an animation from a specific filepath. + - Parameter filepath: The absolute filepath of the animation to load. EG "/User/Me/starAnimation.json" + - Parameter animationCache: A cache for holding loaded animations. Optional. + + - Returns: Deserialized `Animation`. Optional. + */ + public static func filepath( + _ filepath: String, + animationCache: AnimationCacheProvider? = nil) + -> Animation? + { + + /// Check cache for animation + if + let animationCache = animationCache, + let animation = animationCache.animation(forKey: filepath) + { + return animation + } + + do { + /// Decode the animation. + let json = try Data(contentsOf: URL(fileURLWithPath: filepath)) + let animation = try JSONDecoder().decode(Animation.self, from: json) + animationCache?.setAnimation(animation, forKey: filepath) + return animation + } catch { + /// Decoding Error. + return nil + } + } + + /** + Loads a Lottie animation asynchronously from the URL. + + - Parameter url: The url to load the animation from. + - Parameter closure: A closure to be called when the animation has loaded. + - Parameter animationCache: A cache for holding loaded animations. + + */ + public static func loadedFrom( + url: URL, + closure: @escaping Animation.DownloadClosure, + animationCache: AnimationCacheProvider?) + { + + if let animationCache = animationCache, let animation = animationCache.animation(forKey: url.absoluteString) { + closure(animation) + } else { + let task = URLSession.shared.dataTask(with: url) { data, _, error in + guard error == nil, let jsonData = data else { + DispatchQueue.main.async { + closure(nil) + } + return + } + do { + let animation = try JSONDecoder().decode(Animation.self, from: jsonData) + DispatchQueue.main.async { + animationCache?.setAnimation(animation, forKey: url.absoluteString) + closure(animation) + } + } catch { + DispatchQueue.main.async { + closure(nil) + } + } + + } + task.resume() + } + } + + // MARK: Animation (Helpers) + + /** + Markers are a way to describe a point in time by a key name. + + Markers are encoded into animation JSON. By using markers a designer can mark + playback points for a developer to use without having to worry about keeping + track of animation frames. If the animation file is updated, the developer + does not need to update playback code. + + Returns the Progress Time for the marker named. Returns nil if no marker found. + */ + public func progressTime(forMarker named: String) -> AnimationProgressTime? { + guard let markers = markerMap, let marker = markers[named] else { + return nil + } + return progressTime(forFrame: marker.frameTime) + } + + /** + Markers are a way to describe a point in time by a key name. + + Markers are encoded into animation JSON. By using markers a designer can mark + playback points for a developer to use without having to worry about keeping + track of animation frames. If the animation file is updated, the developer + does not need to update playback code. + + Returns the Frame Time for the marker named. Returns nil if no marker found. + */ + public func frameTime(forMarker named: String) -> AnimationFrameTime? { + guard let markers = markerMap, let marker = markers[named] else { + return nil + } + return marker.frameTime + } + + /// Converts Frame Time (Seconds * Framerate) into Progress Time (0 to 1). + public func progressTime(forFrame frameTime: AnimationFrameTime) -> AnimationProgressTime { + ((frameTime - startFrame) / (endFrame - startFrame)).clamp(0, 1) + } + + /// Converts Progress Time (0 to 1) into Frame Time (Seconds * Framerate) + public func frameTime(forProgress progressTime: AnimationProgressTime) -> AnimationFrameTime { + ((endFrame - startFrame) * progressTime) + startFrame + } + + /// Converts Frame Time (Seconds * Framerate) into Time (Seconds) + public func time(forFrame frameTime: AnimationFrameTime) -> TimeInterval { + Double(frameTime - startFrame) / framerate + } + + /// Converts Time (Seconds) into Frame Time (Seconds * Framerate) + public func frameTime(forTime time: TimeInterval) -> AnimationFrameTime { + CGFloat(time * framerate) + startFrame + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift new file mode 100644 index 00000000..5a2616c8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift @@ -0,0 +1,1045 @@ +// +// LottieView.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/23/19. +// + +import Foundation +import QuartzCore + +// MARK: - LottieBackgroundBehavior + +/// Describes the behavior of an AnimationView when the app is moved to the background. +public enum LottieBackgroundBehavior { + /// Stop the animation and reset it to the beginning of its current play time. The completion block is called. + case stop + /// Pause the animation in its current state. The completion block is called. + case pause + /// Pause the animation and restart it when the application moves to the foreground. The completion block is stored and called when the animation completes. + case pauseAndRestore + /// Stops the animation and sets it to the end of its current play time. The completion block is called. + case forceFinish +} + +// MARK: - LottieLoopMode + +/// Defines animation loop behavior +public enum LottieLoopMode { + /// Animation is played once then stops. + case playOnce + /// Animation will loop from beginning to end until stopped. + case loop + /// Animation will play forward, then backwards and loop until stopped. + case autoReverse + /// Animation will loop from beginning to end up to defined amount of times. + case `repeat`(Float) + /// Animation will play forward, then backwards a defined amount of times. + case repeatBackwards(Float) +} + +// MARK: Equatable + +extension LottieLoopMode: Equatable { + public static func == (lhs: LottieLoopMode, rhs: LottieLoopMode) -> Bool { + switch (lhs, rhs) { + case (.repeat(let lhsAmount), .repeat(let rhsAmount)), + (.repeatBackwards(let lhsAmount), .repeatBackwards(let rhsAmount)): + return lhsAmount == rhsAmount + case (.playOnce, .playOnce), + (.loop, .loop), + (.autoReverse, .autoReverse): + return true + default: + return false + } + } +} + +// MARK: - AnimationView + +@IBDesignable +final public class AnimationView: LottieView { + + // MARK: Lifecycle + + // MARK: - Public (Initializers) + + /// Initializes a LottieView with an animation. + public init( + animation: Animation?, + imageProvider: AnimationImageProvider? = nil, + textProvider: AnimationTextProvider = DefaultTextProvider(), + fontProvider: AnimationFontProvider = DefaultFontProvider()) + { + self.animation = animation + self.imageProvider = imageProvider ?? BundleImageProvider(bundle: Bundle.main, searchPath: nil) + self.textProvider = textProvider + self.fontProvider = fontProvider + super.init(frame: .zero) + commonInit() + makeAnimationLayer() + if let animation = animation { + frame = animation.bounds + } + } + + public init() { + animation = nil + imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil) + textProvider = DefaultTextProvider() + fontProvider = DefaultFontProvider() + super.init(frame: .zero) + commonInit() + } + + public override init(frame _: CGRect) { + animation = nil + imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil) + textProvider = DefaultTextProvider() + fontProvider = DefaultFontProvider() + super.init(frame: .zero) + commonInit() + } + + required public init?(coder aDecoder: NSCoder) { + imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil) + textProvider = DefaultTextProvider() + fontProvider = DefaultFontProvider() + super.init(coder: aDecoder) + commonInit() + } + + // MARK: Public + + /** + Describes the behavior of an AnimationView when the app is moved to the background. + + The default is `pause` which pauses the animation when the application moves to + the background. The completion block is called with `false` for completed. + */ + public var backgroundBehavior: LottieBackgroundBehavior = .pause + + // MARK: - Public Properties + + /** + Sets the animation backing the animation view. Setting this will clear the + view's contents, completion blocks and current state. The new animation will + be loaded up and set to the beginning of its timeline. + */ + public var animation: Animation? { + didSet { + makeAnimationLayer() + } + } + + /** + Sets the image provider for the animation view. An image provider provides the + animation with its required image data. + + Setting this will cause the animation to reload its image contents. + */ + public var imageProvider: AnimationImageProvider { + didSet { + animationLayer?.imageProvider = imageProvider + reloadImages() + } + } + + /** + Sets the text provider for animation view. A text provider provides the + animation with values for text layers + */ + public var textProvider: AnimationTextProvider { + didSet { + animationLayer?.textProvider = textProvider + } + } + + /** + Sets the text provider for animation view. A text provider provides the + animation with values for text layers + */ + public var fontProvider: AnimationFontProvider { + didSet { + animationLayer?.fontProvider = fontProvider + } + } + + /// Returns `true` if the animation is currently playing. + public var isAnimationPlaying: Bool { + animationLayer?.animation(forKey: activeAnimationName) != nil + } + + /// Returns `true` if the animation will start playing when this view is added to a window. + public var isAnimationQueued: Bool { + animationContext != nil && waitingToPlayAnimation + } + + /// Sets the loop behavior for `play` calls. Defaults to `playOnce` + public var loopMode: LottieLoopMode = .playOnce { + didSet { + updateInFlightAnimation() + } + } + + /** + When `true` the animation view will rasterize its contents when not animating. + Rasterizing will improve performance of static animations. + + Note: this will not produce crisp results at resolutions above the animations natural resolution. + + Defaults to `false` + */ + public var shouldRasterizeWhenIdle: Bool = false { + didSet { + updateRasterizationState() + } + } + + /** + Sets the current animation time with a Progress Time + + Note: Setting this will stop the current animation, if any. + Note 2: If `animation` is nil, setting this will fallback to 0 + */ + public var currentProgress: AnimationProgressTime { + set { + if let animation = animation { + currentFrame = animation.frameTime(forProgress: newValue) + } else { + currentFrame = 0 + } + } + get { + if let animation = animation { + return animation.progressTime(forFrame: currentFrame) + } else { + return 0 + } + } + } + + /** + Sets the current animation time with a time in seconds. + + Note: Setting this will stop the current animation, if any. + Note 2: If `animation` is nil, setting this will fallback to 0 + */ + public var currentTime: TimeInterval { + set { + if let animation = animation { + currentFrame = animation.frameTime(forTime: newValue) + } else { + currentFrame = 0 + } + } + get { + if let animation = animation { + return animation.time(forFrame: currentFrame) + } else { + return 0 + } + } + } + + /** + Sets the current animation time with a frame in the animations framerate. + + Note: Setting this will stop the current animation, if any. + */ + public var currentFrame: AnimationFrameTime { + set { + removeCurrentAnimation() + updateAnimationFrame(newValue) + } + get { + animationLayer?.currentFrame ?? 0 + } + } + + /// Returns the current animation frame while an animation is playing. + public var realtimeAnimationFrame: AnimationFrameTime { + isAnimationPlaying ? animationLayer?.presentation()?.currentFrame ?? currentFrame : currentFrame + } + + /// Returns the current animation frame while an animation is playing. + public var realtimeAnimationProgress: AnimationProgressTime { + if let animation = animation { + return animation.progressTime(forFrame: realtimeAnimationFrame) + } + return 0 + } + + /// Sets the speed of the animation playback. Defaults to 1 + public var animationSpeed: CGFloat = 1 { + didSet { + updateInFlightAnimation() + } + } + + /** + When `true` the animation will play back at the framerate encoded in the + `Animation` model. When `false` the animation will play at the framerate + of the device. + + Defaults to false + */ + public var respectAnimationFrameRate: Bool = false { + didSet { + animationLayer?.respectAnimationFrameRate = respectAnimationFrameRate + } + } + + /** + Controls the cropping of an Animation. Setting this property will crop the animation + to the current views bounds by the viewport frame. The coordinate space is specified + in the animation's coordinate space. + + Animatable. + */ + public var viewportFrame: CGRect? = nil { + didSet { + + /* + This is really ugly, but is needed to trigger a layout pass within an animation block. + Typically this happens automatically, when layout objects are UIView based. + The animation layer is a CALayer which will not implicitly grab the animation + duration of a UIView animation block. + + By setting bounds and then resetting bounds the UIView animation block's + duration and curve are captured and added to the layer. This is used in the + layout block to animate the animationLayer's position and size. + */ + let rect = bounds + self.bounds = CGRect.zero + self.bounds = rect + self.setNeedsLayout() + } + } + + // MARK: - Public (UIView Overrides) + + override public var intrinsicContentSize: CGSize { + if let animation = animation { + return animation.bounds.size + } + return .zero + } + + // MARK: - Public Functions + + /** + Plays the animation from its current state to the end. + + - Parameter completion: An optional completion closure to be called when the animation completes playing. + */ + public func play(completion: LottieCompletionBlock? = nil) { + guard let animation = animation else { + return + } + + /// Build a context for the animation. + let context = AnimationContext( + playFrom: CGFloat(animation.startFrame), + playTo: CGFloat(animation.endFrame), + closure: completion) + removeCurrentAnimation() + addNewAnimationForContext(context) + } + + /** + Plays the animation from a progress (0-1) to a progress (0-1). + + - Parameter fromProgress: The start progress of the animation. If `nil` the animation will start at the current progress. + - Parameter toProgress: The end progress of the animation. + - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used. + - Parameter completion: An optional completion closure to be called when the animation stops. + */ + public func play( + fromProgress: AnimationProgressTime? = nil, + toProgress: AnimationProgressTime, + loopMode: LottieLoopMode? = nil, + completion: LottieCompletionBlock? = nil) + { + guard let animation = animation else { + return + } + + removeCurrentAnimation() + if let loopMode = loopMode { + /// Set the loop mode, if one was supplied + self.loopMode = loopMode + } + let context = AnimationContext( + playFrom: animation.frameTime(forProgress: fromProgress ?? currentProgress), + playTo: animation.frameTime(forProgress: toProgress), + closure: completion) + addNewAnimationForContext(context) + } + + /** + Plays the animation from a start frame to an end frame in the animation's framerate. + + - Parameter fromFrame: The start frame of the animation. If `nil` the animation will start at the current frame. + - Parameter toFrame: The end frame of the animation. + - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used. + - Parameter completion: An optional completion closure to be called when the animation stops. + */ + public func play( + fromFrame: AnimationFrameTime? = nil, + toFrame: AnimationFrameTime, + loopMode: LottieLoopMode? = nil, + completion: LottieCompletionBlock? = nil) + { + removeCurrentAnimation() + if let loopMode = loopMode { + /// Set the loop mode, if one was supplied + self.loopMode = loopMode + } + + let context = AnimationContext( + playFrom: fromFrame ?? currentProgress, + playTo: toFrame, + closure: completion) + addNewAnimationForContext(context) + } + + /** + Plays the animation from a named marker to another marker. + + Markers are point in time that are encoded into the Animation data and assigned + a name. + + NOTE: If markers are not found the play command will exit. + + - Parameter fromMarker: The start marker for the animation playback. If `nil` the + animation will start at the current progress. + - Parameter toMarker: The end marker for the animation playback. + - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used. + - Parameter completion: An optional completion closure to be called when the animation stops. + */ + public func play( + fromMarker: String? = nil, + toMarker: String, + loopMode: LottieLoopMode? = nil, + completion: LottieCompletionBlock? = nil) + { + + guard let animation = animation, let markers = animation.markerMap, let to = markers[toMarker] else { + return + } + + removeCurrentAnimation() + if let loopMode = loopMode { + /// Set the loop mode, if one was supplied + self.loopMode = loopMode + } + + let fromTime: CGFloat + if let fromName = fromMarker, let from = markers[fromName] { + fromTime = CGFloat(from.frameTime) + } else { + fromTime = currentFrame + } + + let context = AnimationContext( + playFrom: fromTime, + playTo: CGFloat(to.frameTime), + closure: completion) + addNewAnimationForContext(context) + } + + /** + Stops the animation and resets the view to its start frame. + + The completion closure will be called with `false` + */ + public func stop() { + removeCurrentAnimation() + currentFrame = 0 + } + + /** + Pauses the animation in its current state. + + The completion closure will be called with `false` + */ + public func pause() { + removeCurrentAnimation() + } + + /// Reloads the images supplied to the animation from the `imageProvider` + public func reloadImages() { + animationLayer?.reloadImages() + } + + /// Forces the AnimationView to redraw its contents. + public func forceDisplayUpdate() { + animationLayer?.forceDisplayUpdate() + } + + // MARK: - Public (Dynamic Properties) + + /** + + Sets a ValueProvider for the specified keypath. The value provider will be set + on all properties that match the keypath. + + Nearly all properties of a Lottie animation can be changed at runtime using a + combination of `Animation Keypaths` and `Value Providers`. + Setting a ValueProvider on a keypath will cause the animation to update its + contents and read the new Value Provider. + + A value provider provides a typed value on a frame by frame basis. + + - Parameter valueProvider: The new value provider for the properties. + - Parameter keypath: The keypath used to search for properties. + + Example: + ``` + /// A keypath that finds the color value for all `Fill 1` nodes. + let fillKeypath = AnimationKeypath(keypath: "**.Fill 1.Color") + /// A Color Value provider that returns a reddish color. + let redValueProvider = ColorValueProvider(Color(r: 1, g: 0.2, b: 0.3, a: 1)) + /// Set the provider on the animationView. + animationView.setValueProvider(redValueProvider, keypath: fillKeypath) + ``` + */ + public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) { + animationLayer?.setValueProvider(valueProvider, keypath: keypath) + } + + /** + Reads the value of a property specified by the Keypath. + Returns nil if no property is found. + + - Parameter for: The keypath used to search for the property. + - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used. + */ + public func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? { + animationLayer?.getValue(for: keypath, atFrame: atFrame) + } + + /// Logs all child keypaths. + public func logHierarchyKeypaths() { + animationLayer?.logHierarchyKeypaths() + } + + // MARK: - Public (Add Subview) + + /** + Searches for the nearest child layer to the first Keypath and adds the subview + to that layer. The subview will move and animate with the child layer. + Furthermore the subview will be in the child layers coordinate space. + + Note: if no layer is found for the keypath, then nothing happens. + + - Parameter subview: The subview to add to the found animation layer. + - Parameter keypath: The keypath used to find the animation layer. + + Example: + ``` + /// A keypath that finds `Layer 1` + let layerKeypath = AnimationKeypath(keypath: "Layer 1") + + /// Wrap the custom view in an `AnimationSubview` + let subview = AnimationSubview() + subview.addSubview(customView) + + /// Set the provider on the animationView. + animationView.addSubview(subview, forLayerAt: layerKeypath) + ``` + */ + public func addSubview(_ subview: AnimationSubview, forLayerAt keypath: AnimationKeypath) { + guard let sublayer = animationLayer?.layer(for: keypath) else { + return + } + setNeedsLayout() + layoutIfNeeded() + forceDisplayUpdate() + addSubview(subview) + if let subViewLayer = subview.viewLayer { + sublayer.addSublayer(subViewLayer) + } + } + + /** + Converts a CGRect from the AnimationView's coordinate space into the + coordinate space of the layer found at Keypath. + + If no layer is found, nil is returned + + - Parameter rect: The CGRect to convert. + - Parameter toLayerAt: The keypath used to find the layer. + */ + public func convert(_ rect: CGRect, toLayerAt keypath: AnimationKeypath?) -> CGRect? { + guard let animationLayer = animationLayer else { return nil } + guard let keypath = keypath else { + return viewLayer?.convert(rect, to: animationLayer) + } + guard let sublayer = animationLayer.layer(for: keypath) else { + return nil + } + setNeedsLayout() + layoutIfNeeded() + forceDisplayUpdate() + return animationLayer.convert(rect, to: sublayer) + } + + /** + Converts a CGPoint from the AnimationView's coordinate space into the + coordinate space of the layer found at Keypath. + + If no layer is found, nil is returned + + - Parameter point: The CGPoint to convert. + - Parameter toLayerAt: The keypath used to find the layer. + */ + public func convert(_ point: CGPoint, toLayerAt keypath: AnimationKeypath?) -> CGPoint? { + guard let animationLayer = animationLayer else { return nil } + guard let keypath = keypath else { + return viewLayer?.convert(point, to: animationLayer) + } + guard let sublayer = animationLayer.layer(for: keypath) else { + return nil + } + setNeedsLayout() + layoutIfNeeded() + forceDisplayUpdate() + return animationLayer.convert(point, to: sublayer) + } + + // MARK: - Public (Animation Contents) + + /** + Sets the enabled state of all animator nodes found with the keypath search. + This can be used to interactively enable / disable parts of the animation. + + - Parameter isEnabled: When true the animator nodes affect the rendering tree. When false the node is removed from the tree. + - Parameter keypath: The keypath used to find the node(s). + */ + public func setNodeIsEnabled(isEnabled: Bool, keypath: AnimationKeypath) { + guard let animationLayer = animationLayer else { return } + let nodes = animationLayer.animatorNodes(for: keypath) + if let nodes = nodes { + for node in nodes { + node.isEnabled = isEnabled + } + forceDisplayUpdate() + } + } + + // MARK: - Public (Markers) + + /** + Markers are a way to describe a point in time by a key name. + + Markers are encoded into animation JSON. By using markers a designer can mark + playback points for a developer to use without having to worry about keeping + track of animation frames. If the animation file is updated, the developer + does not need to update playback code. + + Returns the Progress Time for the marker named. Returns nil if no marker found. + */ + public func progressTime(forMarker named: String) -> AnimationProgressTime? { + guard let animation = animation else { + return nil + } + return animation.progressTime(forMarker: named) + } + + /** + Markers are a way to describe a point in time by a key name. + + Markers are encoded into animation JSON. By using markers a designer can mark + playback points for a developer to use without having to worry about keeping + track of animation frames. If the animation file is updated, the developer + does not need to update playback code. + + Returns the Frame Time for the marker named. Returns nil if no marker found. + */ + public func frameTime(forMarker named: String) -> AnimationFrameTime? { + guard let animation = animation else { + return nil + } + return animation.frameTime(forMarker: named) + } + + // MARK: Internal + + // MARK: - Private (Properties) + + var animationLayer: AnimationContainer? = nil + + /// Set animation name from Interface Builder + @IBInspectable var animationName: String? { + didSet { + self.animation = animationName.flatMap { + Animation.named($0, animationCache: nil) + } + } + } + + override func layoutAnimation() { + guard let animation = animation, let animationLayer = animationLayer else { return } + var position = animation.bounds.center + let xform: CATransform3D + var shouldForceUpdates: Bool = false + + if let viewportFrame = viewportFrame { + shouldForceUpdates = contentMode == .redraw + + let compAspect = viewportFrame.size.width / viewportFrame.size.height + let viewAspect = bounds.size.width / bounds.size.height + let dominantDimension = compAspect > viewAspect ? bounds.size.width : bounds.size.height + let compDimension = compAspect > viewAspect ? viewportFrame.size.width : viewportFrame.size.height + let scale = dominantDimension / compDimension + + let viewportOffset = animation.bounds.center - viewportFrame.center + xform = CATransform3DTranslate(CATransform3DMakeScale(scale, scale, 1), viewportOffset.x, viewportOffset.y, 0) + position = bounds.center + } else { + switch contentMode { + case .scaleToFill: + position = bounds.center + xform = CATransform3DMakeScale( + bounds.size.width / animation.size.width, + bounds.size.height / animation.size.height, + 1); + case .scaleAspectFit: + position = bounds.center + let compAspect = animation.size.width / animation.size.height + let viewAspect = bounds.size.width / bounds.size.height + let dominantDimension = compAspect > viewAspect ? bounds.size.width : bounds.size.height + let compDimension = compAspect > viewAspect ? animation.size.width : animation.size.height + let scale = dominantDimension / compDimension + xform = CATransform3DMakeScale(scale, scale, 1) + case .scaleAspectFill: + position = bounds.center + let compAspect = animation.size.width / animation.size.height + let viewAspect = bounds.size.width / bounds.size.height + let scaleWidth = compAspect < viewAspect + let dominantDimension = scaleWidth ? bounds.size.width : bounds.size.height + let compDimension = scaleWidth ? animation.size.width : animation.size.height + let scale = dominantDimension / compDimension + xform = CATransform3DMakeScale(scale, scale, 1) + case .redraw: + shouldForceUpdates = true + xform = CATransform3DIdentity + case .center: + position = bounds.center + xform = CATransform3DIdentity + case .top: + position.x = bounds.center.x + xform = CATransform3DIdentity + case .bottom: + position.x = bounds.center.x + position.y = bounds.maxY - animation.bounds.midY + xform = CATransform3DIdentity + case .left: + position.y = bounds.center.y + xform = CATransform3DIdentity + case .right: + position.y = bounds.center.y + position.x = bounds.maxX - animation.bounds.midX + xform = CATransform3DIdentity + case .topLeft: + xform = CATransform3DIdentity + case .topRight: + position.x = bounds.maxX - animation.bounds.midX + xform = CATransform3DIdentity + case .bottomLeft: + position.y = bounds.maxY - animation.bounds.midY + xform = CATransform3DIdentity + case .bottomRight: + position.x = bounds.maxX - animation.bounds.midX + position.y = bounds.maxY - animation.bounds.midY + xform = CATransform3DIdentity + + #if os(iOS) || os(tvOS) + @unknown default: + print("unsupported contentMode: \(contentMode.rawValue); please update lottie-ios") + xform = CATransform3DIdentity + #endif + } + } + + /* + UIView Animation does not implicitly set CAAnimation time or timing fuctions. + If layout is changed in an animation we must get the current animation duration + and timing function and then manually create a CAAnimation to match the UIView animation. + If layout is changed without animation, explicitly set animation duration to 0.0 + inside CATransaction to avoid unwanted artifacts. + */ + /// Check if any animation exist on the view's layer, and match it. + if let key = viewLayer?.animationKeys()?.first, let animation = viewLayer?.animation(forKey: key) { + // The layout is happening within an animation block. Grab the animation data. + + let positionKey = "LayoutPositionAnimation" + let transformKey = "LayoutTransformAnimation" + animationLayer.removeAnimation(forKey: positionKey) + animationLayer.removeAnimation(forKey: transformKey) + + let positionAnimation = animation.copy() as? CABasicAnimation ?? CABasicAnimation(keyPath: "position") + positionAnimation.keyPath = "position" + positionAnimation.isAdditive = false + positionAnimation.fromValue = (animationLayer.presentation() ?? animationLayer).position + positionAnimation.toValue = position + positionAnimation.isRemovedOnCompletion = true + + let xformAnimation = animation.copy() as? CABasicAnimation ?? CABasicAnimation(keyPath: "transform") + xformAnimation.keyPath = "transform" + xformAnimation.isAdditive = false + xformAnimation.fromValue = (animationLayer.presentation() ?? animationLayer).transform + xformAnimation.toValue = xform + xformAnimation.isRemovedOnCompletion = true + + animationLayer.position = position + animationLayer.transform = xform + #if os(OSX) + animationLayer.anchorPoint = layer?.anchorPoint ?? CGPoint.zero + #else + animationLayer.anchorPoint = layer.anchorPoint + #endif + animationLayer.add(positionAnimation, forKey: positionKey) + animationLayer.add(xformAnimation, forKey: transformKey) + } else { + CATransaction.begin() + CATransaction.setAnimationDuration(0.0) + CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: .linear)) + animationLayer.position = position + animationLayer.transform = xform + CATransaction.commit() + } + + if shouldForceUpdates { + animationLayer.forceDisplayUpdate() + } + } + + func updateRasterizationState() { + if isAnimationPlaying { + animationLayer?.shouldRasterize = false + } else { + animationLayer?.shouldRasterize = shouldRasterizeWhenIdle + } + } + + // MARK: - Private (Animation Playback) + + /// Updates the animation frame. Does not affect any current animations + func updateAnimationFrame(_ newFrame: CGFloat) { + CATransaction.begin() + CATransaction.setCompletionBlock { + self.animationLayer?.forceDisplayUpdate() + } + CATransaction.setDisableActions(true) + animationLayer?.currentFrame = newFrame + CATransaction.commit() + } + + @objc + override func animationWillMoveToBackground() { + updateAnimationForBackgroundState() + } + + @objc + override func animationWillEnterForeground() { + updateAnimationForForegroundState() + } + + override func animationMovedToWindow() { + /// Don't update any state if the `superview` is `nil` + /// When A viewA owns superViewB, it removes the superViewB from the window. At this point, viewA still owns superViewB and triggers the viewA method: -didmovetowindow + guard superview != nil else { return } + + if window != nil { + updateAnimationForForegroundState() + } else { + updateAnimationForBackgroundState() + } + } + + // MARK: Fileprivate + + fileprivate var animationContext: AnimationContext? + fileprivate var activeAnimationName: String = AnimationView.animationName + fileprivate var animationID: Int = 0 + + fileprivate var waitingToPlayAnimation: Bool = false + + // MARK: - Private (Building Animation View) + + fileprivate func makeAnimationLayer() { + + /// Remove current animation if any + removeCurrentAnimation() + + if let oldAnimation = self.animationLayer { + oldAnimation.removeFromSuperlayer() + } + + invalidateIntrinsicContentSize() + + guard let animation = animation else { + return + } + + let animationLayer = AnimationContainer( + animation: animation, + imageProvider: imageProvider, + textProvider: textProvider, + fontProvider: fontProvider) + animationLayer.renderScale = screenScale + viewLayer?.addSublayer(animationLayer) + self.animationLayer = animationLayer + reloadImages() + animationLayer.setNeedsDisplay() + setNeedsLayout() + currentFrame = CGFloat(animation.startFrame) + } + + fileprivate func updateAnimationForBackgroundState() { + if let currentContext = animationContext { + switch backgroundBehavior { + case .stop: + removeCurrentAnimation() + updateAnimationFrame(currentContext.playFrom) + case .pause: + removeCurrentAnimation() + case .pauseAndRestore: + currentContext.closure.ignoreDelegate = true + removeCurrentAnimation() + /// Keep the stale context around for when the app enters the foreground. + animationContext = currentContext + case .forceFinish: + removeCurrentAnimation() + updateAnimationFrame(currentContext.playTo) + } + } + } + + fileprivate func updateAnimationForForegroundState() { + if let currentContext = animationContext { + if waitingToPlayAnimation { + waitingToPlayAnimation = false + addNewAnimationForContext(currentContext) + } else if backgroundBehavior == .pauseAndRestore { + /// Restore animation from saved state + updateInFlightAnimation() + } + } + } + + /// Stops the current in flight animation and freezes the animation in its current state. + fileprivate func removeCurrentAnimation() { + guard animationContext != nil else { return } + let pauseFrame = realtimeAnimationFrame + animationLayer?.removeAnimation(forKey: activeAnimationName) + updateAnimationFrame(pauseFrame) + animationContext = nil + } + + /// Updates an in flight animation. + fileprivate func updateInFlightAnimation() { + guard let animationContext = animationContext else { return } + + guard animationContext.closure.animationState != .complete else { + // Tried to re-add an already completed animation. Cancel. + self.animationContext = nil + return + } + + /// Tell existing context to ignore its closure + animationContext.closure.ignoreDelegate = true + + /// Make a new context, stealing the completion block from the previous. + let newContext = AnimationContext( + playFrom: animationContext.playFrom, + playTo: animationContext.playTo, + closure: animationContext.closure.completionBlock) + + /// Remove current animation, and freeze the current frame. + let pauseFrame = realtimeAnimationFrame + animationLayer?.removeAnimation(forKey: activeAnimationName) + animationLayer?.currentFrame = pauseFrame + + addNewAnimationForContext(newContext) + } + + /// Adds animation to animation layer and sets the delegate. If animation layer or animation are nil, exits. + fileprivate func addNewAnimationForContext(_ animationContext: AnimationContext) { + guard let animationlayer = animationLayer, let animation = animation else { + return + } + + self.animationContext = animationContext + + guard window != nil else { waitingToPlayAnimation = true; return } + + animationID = animationID + 1 + activeAnimationName = AnimationView.animationName + String(animationID) + + /// At this point there is no animation on animationLayer and its state is set. + + let framerate = animation.framerate + + let playFrom = animationContext.playFrom.clamp(animation.startFrame, animation.endFrame) + let playTo = animationContext.playTo.clamp(animation.startFrame, animation.endFrame) + + let duration = ((max(playFrom, playTo) - min(playFrom, playTo)) / CGFloat(framerate)) + + let playingForward: Bool = + ( + (animationSpeed > 0 && playFrom < playTo) || + (animationSpeed < 0 && playTo < playFrom)) + + var startFrame = currentFrame.clamp(min(playFrom, playTo), max(playFrom, playTo)) + if startFrame == playTo { + startFrame = playFrom + } + + let timeOffset: TimeInterval = playingForward ? + Double(startFrame - min(playFrom, playTo)) / framerate : + Double(max(playFrom, playTo) - startFrame) / framerate + + let layerAnimation = CABasicAnimation(keyPath: "currentFrame") + layerAnimation.fromValue = playFrom + layerAnimation.toValue = playTo + layerAnimation.speed = Float(animationSpeed) + layerAnimation.duration = TimeInterval(duration) + layerAnimation.fillMode = CAMediaTimingFillMode.both + + switch loopMode { + case .playOnce: + layerAnimation.repeatCount = 1 + case .loop: + layerAnimation.repeatCount = HUGE + case .autoReverse: + layerAnimation.repeatCount = HUGE + layerAnimation.autoreverses = true + case .repeat(let amount): + layerAnimation.repeatCount = amount + case .repeatBackwards(let amount): + layerAnimation.repeatCount = amount + layerAnimation.autoreverses = true + } + + layerAnimation.isRemovedOnCompletion = false + if timeOffset != 0 { + let currentLayerTime = viewLayer?.convertTime(CACurrentMediaTime(), from: nil) ?? 0 + layerAnimation.beginTime = currentLayerTime - (timeOffset * 1 / Double(abs(animationSpeed))) + } + layerAnimation.delegate = animationContext.closure + animationContext.closure.animationLayer = animationlayer + animationContext.closure.animationKey = activeAnimationName + + animationlayer.add(layerAnimation, forKey: activeAnimationName) + updateRasterizationState() + } + + // MARK: Private + + static private let animationName: String = "Lottie" +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift new file mode 100644 index 00000000..e97fb15c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift @@ -0,0 +1,96 @@ +// +// AnimationViewInitializers.swift +// lottie-swift-iOS +// +// Created by Brandon Withrow on 2/6/19. +// + +import Foundation + +extension AnimationView { + + // MARK: Lifecycle + + /** + Loads a Lottie animation from a JSON file in the supplied bundle. + + - Parameter name: The string name of the lottie animation with no file + extension provided. + - Parameter bundle: The bundle in which the animation is located. + Defaults to the Main bundle. + - Parameter imageProvider: An image provider for the animation's image data. + If none is supplied Lottie will search in the supplied bundle for images. + */ + public convenience init( + name: String, + bundle: Bundle = Bundle.main, + imageProvider: AnimationImageProvider? = nil, + animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) + { + let animation = Animation.named(name, bundle: bundle, subdirectory: nil, animationCache: animationCache) + let provider = imageProvider ?? BundleImageProvider(bundle: bundle, searchPath: nil) + self.init(animation: animation, imageProvider: provider) + } + + /** + Loads a Lottie animation from a JSON file in a specific path on disk. + + - Parameter name: The absolute path of the Lottie Animation. + - Parameter imageProvider: An image provider for the animation's image data. + If none is supplied Lottie will search in the supplied filepath for images. + */ + public convenience init( + filePath: String, + imageProvider: AnimationImageProvider? = nil, + animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) + { + let animation = Animation.filepath(filePath, animationCache: animationCache) + let provider = imageProvider ?? + FilepathImageProvider(filepath: URL(fileURLWithPath: filePath).deletingLastPathComponent().path) + self.init(animation: animation, imageProvider: provider) + } + + /** + Loads a Lottie animation asynchronously from the URL + + - Parameter url: The url to load the animation from. + - Parameter imageProvider: An image provider for the animation's image data. + If none is supplied Lottie will search in the main bundle for images. + - Parameter closure: A closure to be called when the animation has loaded. + */ + public convenience init( + url: URL, + imageProvider: AnimationImageProvider? = nil, + closure: @escaping AnimationView.DownloadClosure, + animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) + { + + if let animationCache = animationCache, let animation = animationCache.animation(forKey: url.absoluteString) { + self.init(animation: animation, imageProvider: imageProvider) + closure(nil) + } else { + + self.init(animation: nil, imageProvider: imageProvider) + + Animation.loadedFrom(url: url, closure: { animation in + if let animation = animation { + self.animation = animation + closure(nil) + } else { + closure(LottieDownloadError.downloadFailed) + } + }, animationCache: animationCache) + } + } + + // MARK: Public + + public typealias DownloadClosure = (Error?) -> Void + +} + +// MARK: - LottieDownloadError + +enum LottieDownloadError: Error { + case downloadFailed +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift new file mode 100644 index 00000000..88b416c8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift @@ -0,0 +1,24 @@ +// +// AnimationCacheProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/5/19. +// + +import Foundation +/** + `AnimationCacheProvider` is a protocol that describes an Animation Cache. + Animation Cache is used when loading `Animation` models. Using an Animation Cache + can increase performance when loading an animation multiple times. + + Lottie comes with a prebuilt LRU Animation Cache. + */ +public protocol AnimationCacheProvider { + + func animation(forKey: String) -> Animation? + + func setAnimation(_ animation: Animation, forKey: String) + + func clearCache() + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift new file mode 100644 index 00000000..c7fbc668 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift @@ -0,0 +1,63 @@ +// +// LRUAnimationCache.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/5/19. +// + +import Foundation + +/** + An Animation Cache that will store animations up to `cacheSize`. + + Once `cacheSize` is reached, the least recently used animation will be ejected. + The default size of the cache is 100. + */ +public class LRUAnimationCache: AnimationCacheProvider { + + // MARK: Lifecycle + + public init() { } + + // MARK: Public + + /// The global shared Cache. + public static let sharedCache = LRUAnimationCache() + + /// The size of the cache. + public var cacheSize: Int = 100 + + /// Clears the Cache. + public func clearCache() { + cacheMap.removeAll() + lruList.removeAll() + } + + public func animation(forKey: String) -> Animation? { + guard let animation = cacheMap[forKey] else { + return nil + } + if let index = lruList.firstIndex(of: forKey) { + lruList.remove(at: index) + lruList.append(forKey) + } + return animation + } + + public func setAnimation(_ animation: Animation, forKey: String) { + cacheMap[forKey] = animation + lruList.append(forKey) + if lruList.count > cacheSize { + let removed = lruList.remove(at: 0) + if removed != forKey { + cacheMap[removed] = nil + } + } + } + + // MARK: Fileprivate + + fileprivate var cacheMap: [String: Animation] = [:] + fileprivate var lruList: [String] = [] + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift new file mode 100644 index 00000000..aabcbd0f --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift @@ -0,0 +1,46 @@ +// +// AnimationKeypath.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation + +/** + `AnimationKeypath` is an object that describes a keypath search for nodes in the + animation JSON. `AnimationKeypath` matches views and properties inside of `AnimationView` + to their backing `Animation` model by name. + + A keypath can be used to set properties on an existing animation, or can be validated + with an existing `Animation`. + + `AnimationKeypath` can describe a specific object, or can use wildcards for fuzzy matching + of objects. Acceptable wildcards are either "*" (star) or "**" (double star). + Single star will search a single depth for the next object. + Double star will search any depth. + + Read More at https://airbnb.io/lottie/#/ios?id=dynamic-animation-properties + + EG: + @"Layer.Shape Group.Stroke 1.Color" + Represents a specific color node on a specific stroke. + + @"**.Stroke 1.Color" + Represents the color node for every Stroke named "Stroke 1" in the animation. + */ +public struct AnimationKeypath { + + /// Creates a keypath from a dot separated string. The string is separated by "." + public init(keypath: String) { + keys = keypath.components(separatedBy: ".") + } + + /// Creates a keypath from a list of strings. + public init(keys: [String]) { + self.keys = keys + } + + let keys: [String] + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift new file mode 100644 index 00000000..957b74c8 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift @@ -0,0 +1,29 @@ +// +// AnyValueProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/30/19. +// + +import CoreGraphics +import Foundation + +/** + `AnyValueProvider` is a protocol that return animation data for a property at a + given time. Every fame an `AnimationView` queries all of its properties and asks + if their ValueProvider has an update. If it does the AnimationView will read the + property and update that portion of the animation. + + Value Providers can be used to dynamically set animation properties at run time. + */ +public protocol AnyValueProvider { + + /// The Type of the value provider + var valueType: Any.Type { get } + + /// Asks the provider if it has an update for the given frame. + func hasUpdate(frame: AnimationFrameTime) -> Bool + + /// Asks the provider to update the container with its value for the frame. + func value(frame: AnimationFrameTime) -> Any +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift new file mode 100644 index 00000000..c3a3e383 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift @@ -0,0 +1,70 @@ +// +// ColorValueProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import CoreGraphics +import Foundation + +/// A `ValueProvider` that returns a CGColor Value +public final class ColorValueProvider: AnyValueProvider { + + // MARK: Lifecycle + + /// Initializes with a block provider + public init(block: @escaping ColorValueBlock) { + self.block = block + color = Color(r: 0, g: 0, b: 0, a: 1) + } + + /// Initializes with a single color. + public init(_ color: Color) { + self.color = color + block = nil + hasUpdate = true + } + + // MARK: Public + + /// Returns a Color for a CGColor(Frame Time) + public typealias ColorValueBlock = (CGFloat) -> Color + + /// The color value of the provider. + public var color: Color { + didSet { + hasUpdate = true + } + } + + // MARK: ValueProvider Protocol + + public var valueType: Any.Type { + Color.self + } + + public func hasUpdate(frame _: CGFloat) -> Bool { + if block != nil { + return true + } + return hasUpdate + } + + public func value(frame: CGFloat) -> Any { + hasUpdate = false + let newColor: Color + if let block = block { + newColor = block(frame) + } else { + newColor = color + } + return newColor + } + + // MARK: Private + + private var hasUpdate: Bool = true + + private var block: ColorValueBlock? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift new file mode 100644 index 00000000..8bea6660 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift @@ -0,0 +1,69 @@ +// +// DoubleValueProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import CoreGraphics +import Foundation + +/// A `ValueProvider` that returns a CGFloat Value +public final class FloatValueProvider: AnyValueProvider { + + // MARK: Lifecycle + + /// Initializes with a block provider + public init(block: @escaping CGFloatValueBlock) { + self.block = block + float = 0 + } + + /// Initializes with a single float. + public init(_ float: CGFloat) { + self.float = float + block = nil + hasUpdate = true + } + + // MARK: Public + + /// Returns a CGFloat for a CGFloat(Frame Time) + public typealias CGFloatValueBlock = (CGFloat) -> CGFloat + + public var float: CGFloat { + didSet { + hasUpdate = true + } + } + + // MARK: ValueProvider Protocol + + public var valueType: Any.Type { + Vector1D.self + } + + public func hasUpdate(frame _: CGFloat) -> Bool { + if block != nil { + return true + } + return hasUpdate + } + + public func value(frame: CGFloat) -> Any { + hasUpdate = false + let newCGFloat: CGFloat + if let block = block { + newCGFloat = block(frame) + } else { + newCGFloat = float + } + return Vector1D(Double(newCGFloat)) + } + + // MARK: Private + + private var hasUpdate: Bool = true + + private var block: CGFloatValueBlock? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift new file mode 100644 index 00000000..b71568ca --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift @@ -0,0 +1,122 @@ +// +// GradientValueProvider.swift +// lottie-swift +// +// Created by Enrique Bermúdez on 10/27/19. +// + +import CoreGraphics +import Foundation + +/// A `ValueProvider` that returns a Gradient Color Value. +public final class GradientValueProvider: AnyValueProvider { + + // MARK: Lifecycle + + /// Initializes with a block provider. + public init( + block: @escaping ColorsValueBlock, + locations: ColorLocationsBlock? = nil) + { + self.block = block + locationsBlock = locations + colors = [] + self.locations = [] + } + + /// Initializes with an array of colors. + public init( + _ colors: [Color], + locations: [Double] = []) + { + self.colors = colors + self.locations = locations + updateValueArray() + hasUpdate = true + } + + // MARK: Public + + /// Returns a [Color] for a CGFloat(Frame Time). + public typealias ColorsValueBlock = (CGFloat) -> [Color] + /// Returns a [Double](Color locations) for a CGFloat(Frame Time). + public typealias ColorLocationsBlock = (CGFloat) -> [Double] + + /// The colors values of the provider. + public var colors: [Color] { + didSet { + updateValueArray() + hasUpdate = true + } + } + + /// The color location values of the provider. + public var locations: [Double] { + didSet { + updateValueArray() + hasUpdate = true + } + } + + // MARK: ValueProvider Protocol + + public var valueType: Any.Type { + [Double].self + } + + public func hasUpdate(frame _: CGFloat) -> Bool { + if block != nil || locationsBlock != nil { + return true + } + return hasUpdate + } + + public func value(frame: CGFloat) -> Any { + hasUpdate = false + + if let block = block { + let newColors = block(frame) + let newLocations = locationsBlock?(frame) ?? [] + value = value(from: newColors, locations: newLocations) + } + + return value + } + + // MARK: Private + + private var hasUpdate: Bool = true + + private var block: ColorsValueBlock? + private var locationsBlock: ColorLocationsBlock? + private var value: [Double] = [] + + private func value(from colors: [Color], locations: [Double]) -> [Double] { + + var colorValues = [Double]() + var alphaValues = [Double]() + var shouldAddAlphaValues = false + + for i in 0.. i ? locations[i] : + (Double(i) / Double(colors.count - 1)) + + colorValues.append(location) + colorValues.append(colors[i].r) + colorValues.append(colors[i].g) + colorValues.append(colors[i].b) + + alphaValues.append(location) + alphaValues.append(colors[i].a) + } + + return colorValues + (shouldAddAlphaValues ? alphaValues : []) + } + + private func updateValueArray() { + value = value(from: colors, locations: locations) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift new file mode 100644 index 00000000..29d6a47a --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift @@ -0,0 +1,68 @@ +// +// PointValueProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import CoreGraphics +import Foundation +/// A `ValueProvider` that returns a CGPoint Value +public final class PointValueProvider: AnyValueProvider { + + // MARK: Lifecycle + + /// Initializes with a block provider + public init(block: @escaping PointValueBlock) { + self.block = block + point = .zero + } + + /// Initializes with a single point. + public init(_ point: CGPoint) { + self.point = point + block = nil + hasUpdate = true + } + + // MARK: Public + + /// Returns a CGPoint for a CGFloat(Frame Time) + public typealias PointValueBlock = (CGFloat) -> CGPoint + + public var point: CGPoint { + didSet { + hasUpdate = true + } + } + + // MARK: ValueProvider Protocol + + public var valueType: Any.Type { + Vector3D.self + } + + public func hasUpdate(frame _: CGFloat) -> Bool { + if block != nil { + return true + } + return hasUpdate + } + + public func value(frame: CGFloat) -> Any { + hasUpdate = false + let newPoint: CGPoint + if let block = block { + newPoint = block(frame) + } else { + newPoint = point + } + return newPoint.vector3dValue + } + + // MARK: Private + + private var hasUpdate: Bool = true + + private var block: PointValueBlock? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift new file mode 100644 index 00000000..5b639911 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift @@ -0,0 +1,69 @@ +// +// SizeValueProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import CoreGraphics +import Foundation + +/// A `ValueProvider` that returns a CGSize Value +public final class SizeValueProvider: AnyValueProvider { + + // MARK: Lifecycle + + /// Initializes with a block provider + public init(block: @escaping SizeValueBlock) { + self.block = block + size = .zero + } + + /// Initializes with a single size. + public init(_ size: CGSize) { + self.size = size + block = nil + hasUpdate = true + } + + // MARK: Public + + /// Returns a CGSize for a CGFloat(Frame Time) + public typealias SizeValueBlock = (CGFloat) -> CGSize + + public var size: CGSize { + didSet { + hasUpdate = true + } + } + + // MARK: ValueProvider Protocol + + public var valueType: Any.Type { + Vector3D.self + } + + public func hasUpdate(frame _: CGFloat) -> Bool { + if block != nil { + return true + } + return hasUpdate + } + + public func value(frame: CGFloat) -> Any { + hasUpdate = false + let newSize: CGSize + if let block = block { + newSize = block(frame) + } else { + newSize = size + } + return newSize.vector3dValue + } + + // MARK: Private + + private var hasUpdate: Bool = true + + private var block: SizeValueBlock? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift new file mode 100644 index 00000000..4662cb27 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift @@ -0,0 +1,37 @@ +// +// AnimationFontProvider.swift +// Lottie +// +// Created by Brandon Withrow on 8/5/20. +// Copyright © 2020 YurtvilleProds. All rights reserved. +// + +import CoreGraphics +import CoreText +import Foundation + +// MARK: - AnimationFontProvider + +/** + Font provider is a protocol that is used to supply fonts to `AnimationView`. + + */ +public protocol AnimationFontProvider { + func fontFor(family: String, size: CGFloat) -> CTFont? +} + +// MARK: - DefaultFontProvider + +/// Default Font provider. +public final class DefaultFontProvider: AnimationFontProvider { + + // MARK: Lifecycle + + public init() {} + + // MARK: Public + + public func fontFor(family: String, size: CGFloat) -> CTFont? { + CTFontCreateWithName(family as CFString, size, nil) + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift new file mode 100644 index 00000000..6d7a708e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift @@ -0,0 +1,23 @@ +// +// LottieImageProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import CoreGraphics +import Foundation + +/** + Image provider is a protocol that is used to supply images to `AnimationView`. + + Some animations require a reference to an image. The image provider loads and + provides those images to the `AnimationView`. Lottie includes a couple of + prebuilt Image Providers that supply images from a Bundle, or from a FilePath. + + Additionally custom Image Providers can be made to load images from a URL, + or to Cache images. + */ +public protocol AnimationImageProvider { + func imageForAsset(asset: ImageAsset) -> CGImage? +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift new file mode 100644 index 00000000..2c33e2b4 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift @@ -0,0 +1,15 @@ +// +// AnimationTime.swift +// lottie-swift-iOS +// +// Created by Brandon Withrow on 2/6/19. +// + +import CoreGraphics +import Foundation + +/// Defines animation time in Frames (Seconds * Framerate). +public typealias AnimationFrameTime = CGFloat + +/// Defines animation time by a progress from 0 (beginning of the animation) to 1 (end of the animation) +public typealias AnimationProgressTime = CGFloat diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift new file mode 100644 index 00000000..55fc093d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift @@ -0,0 +1,45 @@ +// +// Color.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation + +// MARK: - ColorFormatDenominator + +public enum ColorFormatDenominator { + case One + case OneHundred + case TwoFiftyFive + + var value: Double { + switch self { + case .One: + return 1.0 + case .OneHundred: + return 100.0 + case .TwoFiftyFive: + return 255.0 + } + } +} + +// MARK: - Color + +public struct Color { + + public var r: Double + public var g: Double + public var b: Double + public var a: Double + + public init(r: Double, g: Double, b: Double, a: Double, denominator: ColorFormatDenominator = .One) { + self.r = r / denominator.value + self.g = g / denominator.value + self.b = b / denominator.value + self.a = a / denominator.value + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift new file mode 100644 index 00000000..8efb3483 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift @@ -0,0 +1,40 @@ +// +// Vectors.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation + +// MARK: - Vector1D + +public struct Vector1D { + + public init(_ value: Double) { + self.value = value + } + + public let value: Double + +} + +// MARK: - Vector3D + +/** + A three dimensional vector. + These vectors are encoded and decoded from [Double] + */ +public struct Vector3D { + + public let x: Double + public let y: Double + public let z: Double + + public init(x: Double, y: Double, z: Double) { + self.x = x + self.y = y + self.z = z + } + +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift new file mode 100644 index 00000000..b3e4e52e --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift @@ -0,0 +1,55 @@ +// +// AnimationImageProvider.swift +// Lottie_iOS +// +// Created by Alexandr Goncharov on 07/06/2019. +// + +import Foundation + +// MARK: - AnimationTextProvider + +/** + Text provider is a protocol that is used to supply text to `AnimationView`. + */ +public protocol AnimationTextProvider: AnyObject { + func textFor(keypathName: String, sourceText: String) -> String +} + +// MARK: - DictionaryTextProvider + +/// Text provider that simply map values from dictionary +public final class DictionaryTextProvider: AnimationTextProvider { + + // MARK: Lifecycle + + public init(_ values: [String: String]) { + self.values = values + } + + // MARK: Public + + public func textFor(keypathName: String, sourceText: String) -> String { + values[keypathName] ?? sourceText + } + + // MARK: Internal + + let values: [String: String] +} + +// MARK: - DefaultTextProvider + +/// Default text provider. Uses text in the animation file +public final class DefaultTextProvider: AnimationTextProvider { + + // MARK: Lifecycle + + public init() {} + + // MARK: Public + + public func textFor(keypathName _: String, sourceText: String) -> String { + sourceText + } +} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift new file mode 100644 index 00000000..0d5e3cc5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift @@ -0,0 +1,77 @@ +// +// AnimatedButton.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit +/** + An interactive button that plays an animation when pressed. + */ +open class AnimatedButton: AnimatedControl { + + // MARK: Lifecycle + + public override init(animation: Animation) { + super.init(animation: animation) + accessibilityTraits = UIAccessibilityTraits.button + } + + public override init() { + super.init() + accessibilityTraits = UIAccessibilityTraits.button + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + // MARK: Public + + /// Sets the play range for the given UIControlEvent. + public func setPlayRange(fromProgress: AnimationProgressTime, toProgress: AnimationProgressTime, event: UIControl.Event) { + rangesForEvents[event.rawValue] = (from: fromProgress, to: toProgress) + } + + /// Sets the play range for the given UIControlEvent. + public func setPlayRange(fromMarker fromName: String, toMarker toName: String, event: UIControl.Event) { + if + let start = animationView.progressTime(forMarker: fromName), + let end = animationView.progressTime(forMarker: toName) + { + rangesForEvents[event.rawValue] = (from: start, to: end) + } + } + + public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { + let _ = super.beginTracking(touch, with: event) + let touchEvent = UIControl.Event.touchDown + if let playrange = rangesForEvents[touchEvent.rawValue] { + animationView.play(fromProgress: playrange.from, toProgress: playrange.to, loopMode: LottieLoopMode.playOnce) + } + return true + } + + public override func endTracking(_ touch: UITouch?, with event: UIEvent?) { + super.endTracking(touch, with: event) + let touchEvent: UIControl.Event + if let touch = touch, bounds.contains(touch.location(in: self)) { + touchEvent = UIControl.Event.touchUpInside + } else { + touchEvent = UIControl.Event.touchUpOutside + } + + if let playrange = rangesForEvents[touchEvent.rawValue] { + animationView.play(fromProgress: playrange.from, toProgress: playrange.to, loopMode: LottieLoopMode.playOnce) + } + } + + // MARK: Fileprivate + + fileprivate var rangesForEvents: [UInt : (from: CGFloat, to: CGFloat)] = + [UIControl.Event.touchUpInside.rawValue : (from: 0, to: 1)] +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift new file mode 100644 index 00000000..e7fb063b --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift @@ -0,0 +1,173 @@ +// +// AnimatedControl.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +/** + Lottie comes prepacked with a two Animated Controls, `AnimatedSwitch` and + `AnimatedButton`. Both of these controls are built on top of `AnimatedControl` + + `AnimatedControl` is a subclass of `UIControl` that provides an interactive + mechanism for controlling the visual state of an animation in response to + user actions. + + The `AnimatedControl` will show and hide layers depending on the current + `UIControl.State` of the control. + + Users of `AnimationControl` can set a Layer Name for each `UIControl.State`. + When the state is change the `AnimationControl` will change the visibility + of its layers. + + NOTE: Do not initialize directly. This is intended to be subclassed. + */ +open class AnimatedControl: UIControl { + + // MARK: Lifecycle + + // MARK: Initializers + + public init(animation: Animation) { + animationView = AnimationView(animation: animation) + super.init(frame: animation.bounds) + commonInit() + } + + public init() { + animationView = AnimationView() + super.init(frame: .zero) + commonInit() + } + + required public init?(coder aDecoder: NSCoder) { + animationView = AnimationView() + super.init(coder: aDecoder) + commonInit() + } + + // MARK: Open + + // MARK: UIControl Overrides + + open override var isEnabled: Bool { + didSet { + updateForState() + } + } + + open override var isSelected: Bool { + didSet { + updateForState() + } + } + + open override var isHighlighted: Bool { + didSet { + updateForState() + } + } + + open override var intrinsicContentSize: CGSize { + animationView.intrinsicContentSize + } + + open override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { + updateForState() + return super.beginTracking(touch, with: event) + } + + open override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { + updateForState() + return super.continueTracking(touch, with: event) + } + + open override func endTracking(_ touch: UITouch?, with event: UIEvent?) { + updateForState() + return super.endTracking(touch, with: event) + } + + open override func cancelTracking(with event: UIEvent?) { + updateForState() + super.cancelTracking(with: event) + } + + open func animationDidSet() { + + } + + // MARK: Public + + /// The animation view in which the animation is rendered. + public let animationView: AnimationView + + /// The animation backing the animated control. + public var animation: Animation? { + didSet { + animationView.animation = animation + animationView.bounds = animation?.bounds ?? .zero + setNeedsLayout() + updateForState() + animationDidSet() + } + } + + /// The speed of the animation playback. Defaults to 1 + public var animationSpeed: CGFloat { + set { animationView.animationSpeed = newValue } + get { animationView.animationSpeed } + } + + /// Sets which Animation Layer should be visible for the given state. + public func setLayer(named: String, forState: UIControl.State) { + stateMap[forState.rawValue] = named + updateForState() + } + + /// Sets a ValueProvider for the specified keypath + public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) { + animationView.setValueProvider(valueProvider, keypath: keypath) + } + + // MARK: Internal + + var stateMap: [UInt: String] = [:] + + func updateForState() { + guard let animationLayer = animationView.animationLayer else { return } + if + let layerName = stateMap[state.rawValue], + let stateLayer = animationLayer.layer(for: AnimationKeypath(keypath: layerName)) + { + for layer in animationLayer.animationLayers { + layer.isHidden = true + } + stateLayer.isHidden = false + } else { + for layer in animationLayer.animationLayers { + layer.isHidden = false + } + } + } + + // MARK: Fileprivate + + fileprivate func commonInit() { + animationView.clipsToBounds = false + clipsToBounds = true + animationView.translatesAutoresizingMaskIntoConstraints = false + animationView.backgroundBehavior = .forceFinish + addSubview(animationView) + animationView.contentMode = .scaleAspectFit + animationView.isUserInteractionEnabled = false + animationView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + animationView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true + animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + } +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift new file mode 100644 index 00000000..ed76dcb5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift @@ -0,0 +1,216 @@ +// +// AnimatedSwitch.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +/** + An interactive switch with an 'On' and 'Off' state. When the user taps on the + switch the state is toggled and the appropriate animation is played. + + Both the 'On' and 'Off' have an animation play range associated with their state. + */ +open class AnimatedSwitch: AnimatedControl { + + // MARK: Lifecycle + + public override init(animation: Animation) { + /// Generate a haptic generator if available. + #if os(iOS) + if #available(iOS 10.0, *) { + self.hapticGenerator = HapticGenerator() + } else { + hapticGenerator = NullHapticGenerator() + } + #else + hapticGenerator = NullHapticGenerator() + #endif + super.init(animation: animation) + updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false) + accessibilityTraits = UIAccessibilityTraits.button + } + + public override init() { + /// Generate a haptic generator if available. + #if os(iOS) + if #available(iOS 10.0, *) { + self.hapticGenerator = HapticGenerator() + } else { + hapticGenerator = NullHapticGenerator() + } + #else + hapticGenerator = NullHapticGenerator() + #endif + super.init() + updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false) + accessibilityTraits = UIAccessibilityTraits.button + } + + required public init?(coder aDecoder: NSCoder) { + /// Generate a haptic generator if available. + #if os(iOS) + if #available(iOS 10.0, *) { + self.hapticGenerator = HapticGenerator() + } else { + hapticGenerator = NullHapticGenerator() + } + #else + hapticGenerator = NullHapticGenerator() + #endif + super.init(coder: aDecoder) + accessibilityTraits = UIAccessibilityTraits.button + } + + // MARK: Public + + /// Defines what happens when the user taps the switch while an + /// animation is still in flight + public enum CancelBehavior { + case reverse // default - plays the current animation in reverse + case none // does not update the animation when canceled + } + + /// The cancel behavior for the switch. See CancelBehavior for options + public var cancelBehavior: CancelBehavior = .reverse + + /// The current state of the switch. + public var isOn: Bool { + set { + /// This is forwarded to a private variable because the animation needs to be updated without animation when set externally and with animation when set internally. + guard _isOn != newValue else { return } + updateOnState(isOn: newValue, animated: false, shouldFireHaptics: false) + } + get { + _isOn + } + } + + /// Set the state of the switch and specify animation and haptics + public func setIsOn(_ isOn: Bool, animated: Bool, shouldFireHaptics: Bool = true) { + guard isOn != _isOn else { return } + updateOnState(isOn: isOn, animated: animated, shouldFireHaptics: shouldFireHaptics) + } + + /// Sets the play range for the given state. When the switch is toggled, the animation range is played. + public func setProgressForState( + fromProgress: AnimationProgressTime, + toProgress: AnimationProgressTime, + forOnState: Bool) + { + if forOnState { + onStartProgress = fromProgress + onEndProgress = toProgress + } else { + offStartProgress = fromProgress + offEndProgress = toProgress + } + + updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false) + } + + public override func endTracking(_ touch: UITouch?, with event: UIEvent?) { + super.endTracking(touch, with: event) + updateOnState(isOn: !_isOn, animated: true, shouldFireHaptics: true) + sendActions(for: .valueChanged) + } + + public override func animationDidSet() { + updateOnState(isOn: _isOn, animated: true, shouldFireHaptics: false) + } + + // MARK: Internal + + // MARK: Animation State + + func updateOnState(isOn: Bool, animated: Bool, shouldFireHaptics: Bool) { + _isOn = isOn + var startProgress = isOn ? onStartProgress : offStartProgress + var endProgress = isOn ? onEndProgress : offEndProgress + let finalProgress = endProgress + + if cancelBehavior == .reverse { + let realtimeProgress = animationView.realtimeAnimationProgress + + let previousStateStart = isOn ? offStartProgress : onStartProgress + let previousStateEnd = isOn ? offEndProgress : onEndProgress + if + realtimeProgress.isInRange( + min(previousStateStart, previousStateEnd), + max(previousStateStart, previousStateEnd)) + { + /// Animation is currently in the previous time range. Reverse the previous play. + startProgress = previousStateEnd + endProgress = previousStateStart + } + } + + updateAccessibilityLabel() + + guard animated == true else { + animationView.currentProgress = finalProgress + return + } + + if shouldFireHaptics { + hapticGenerator.generateImpact() + } + + animationView.play(fromProgress: startProgress, toProgress: endProgress, loopMode: LottieLoopMode.playOnce) { finished in + if finished == true { + self.animationView.currentProgress = finalProgress + } + } + } + + // MARK: Fileprivate + + fileprivate var onStartProgress: CGFloat = 0 + fileprivate var onEndProgress: CGFloat = 1 + fileprivate var offStartProgress: CGFloat = 1 + fileprivate var offEndProgress: CGFloat = 0 + fileprivate var _isOn: Bool = false + fileprivate var hapticGenerator: ImpactGenerator + + // MARK: Private + + private func updateAccessibilityLabel() { + accessibilityValue = _isOn ? NSLocalizedString("On", comment: "On") : NSLocalizedString("Off", comment: "Off") + } + +} +#endif + +// MARK: - ImpactGenerator + +protocol ImpactGenerator { + func generateImpact() +} + +// MARK: - NullHapticGenerator + +class NullHapticGenerator: ImpactGenerator { + func generateImpact() { + + } +} + +#if os(iOS) +@available(iOS 10.0, *) +class HapticGenerator: ImpactGenerator { + + // MARK: Internal + + func generateImpact() { + impact.impactOccurred() + } + + // MARK: Fileprivate + + fileprivate let impact = UIImpactFeedbackGenerator(style: .light) +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift new file mode 100644 index 00000000..bc4df54d --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift @@ -0,0 +1,20 @@ +// +// AnimationSubview.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +/// A view that can be added to a keypath of an AnimationView +public final class AnimationSubview: UIView { + + var viewLayer: CALayer? { + layer + } + +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift new file mode 100644 index 00000000..83cbe37c --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift @@ -0,0 +1,94 @@ +// +// LottieBundleImageProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 1/25/19. +// + +import CoreGraphics +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +/** + An `AnimationImageProvider` that provides images by name from a specific bundle. + The BundleImageProvider is initialized with a bundle and an optional searchPath. + */ +public class BundleImageProvider: AnimationImageProvider { + + // MARK: Lifecycle + + /** + Initializes an image provider with a bundle and an optional subpath. + + Provides images for an animation from a bundle. Additionally the provider can + search a specific subpath for the images. + + - Parameter bundle: The bundle containing images for the provider. + - Parameter searchPath: The subpath is a path within the bundle to search for image assets. + + */ + public init(bundle: Bundle, searchPath: String?) { + self.bundle = bundle + self.searchPath = searchPath + } + + // MARK: Public + + public func imageForAsset(asset: ImageAsset) -> CGImage? { + + if + asset.name.hasPrefix("data:"), + let url = URL(string: asset.name), + let data = try? Data(contentsOf: url), + let image = UIImage(data: data) + { + return image.cgImage + } + + let imagePath: String? + /// Try to find the image in the bundle. + if let searchPath = searchPath { + /// Search in the provided search path for the image + var directoryPath = URL(fileURLWithPath: searchPath) + directoryPath.appendPathComponent(asset.directory) + + if let path = bundle.path(forResource: asset.name, ofType: nil, inDirectory: directoryPath.path) { + /// First search for the image in the asset provided sub directory. + imagePath = path + } else if let path = bundle.path(forResource: asset.name, ofType: nil, inDirectory: searchPath) { + /// Try finding the image in the search path. + imagePath = path + } else { + imagePath = bundle.path(forResource: asset.name, ofType: nil) + } + } else { + if let path = bundle.path(forResource: asset.name, ofType: nil, inDirectory: asset.directory) { + /// First search for the image in the asset provided sub directory. + imagePath = path + } else { + /// First search for the image in bundle. + imagePath = bundle.path(forResource: asset.name, ofType: nil) + } + } + + if imagePath == nil { + guard let image = UIImage(named: asset.name, in: bundle, compatibleWith: nil) else { + return nil + } + return image.cgImage + } + + guard let foundPath = imagePath, let image = UIImage(contentsOfFile: foundPath) else { + /// No image found. + return nil + } + return image.cgImage + } + + // MARK: Internal + + let bundle: Bundle + let searchPath: String? +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift new file mode 100644 index 00000000..73963557 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift @@ -0,0 +1,33 @@ +// +// CompatibleAnimationKeypath.swift +// Lottie_iOS +// +// Created by Tyler Hedrick on 3/6/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) + +/// An Objective-C compatible wrapper around Lottie's AnimationKeypath +@objc +public final class CompatibleAnimationKeypath: NSObject { + + // MARK: Lifecycle + + /// Creates a keypath from a dot separated string. The string is separated by "." + @objc + public init(keypath: String) { + animationKeypath = AnimationKeypath(keypath: keypath) + } + + /// Creates a keypath from a list of strings. + @objc + public init(keys: [String]) { + animationKeypath = AnimationKeypath(keys: keys) + } + + // MARK: Public + + public let animationKeypath: AnimationKeypath +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift new file mode 100644 index 00000000..6dd08ae5 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift @@ -0,0 +1,325 @@ +// +// CompatibleAnimationView.swift +// Lottie_iOS +// +// Created by Tyler Hedrick on 3/6/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +/// An Objective-C compatible wrapper around Lottie's Animation class. +/// Use in tandem with CompatibleAnimationView when using Lottie in Objective-C +@objc +public final class CompatibleAnimation: NSObject { + + // MARK: Lifecycle + + @objc + public init(name: String, bundle: Bundle = Bundle.main) { + self.name = name + self.bundle = bundle + super.init() + } + + // MARK: Internal + + internal var animation: Animation? { + Animation.named(name, bundle: bundle) + } + + @objc + static func named(_ name: String) -> CompatibleAnimation { + CompatibleAnimation(name: name) + } + + // MARK: Private + + private let name: String + private let bundle: Bundle +} + +/// An Objective-C compatible wrapper around Lottie's AnimationView. +@objc +public final class CompatibleAnimationView: UIView { + + // MARK: Lifecycle + + @objc + public init(compatibleAnimation: CompatibleAnimation) { + animationView = AnimationView(animation: compatibleAnimation.animation) + self.compatibleAnimation = compatibleAnimation + super.init(frame: .zero) + commonInit() + } + + @objc + public override init(frame: CGRect) { + animationView = AnimationView() + super.init(frame: frame) + commonInit() + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Public + + @objc + public var compatibleAnimation: CompatibleAnimation? { + didSet { + animationView.animation = compatibleAnimation?.animation + } + } + + @objc + public var loopAnimationCount: CGFloat = 0 { + didSet { + animationView.loopMode = loopAnimationCount == -1 ? .loop : .repeat(Float(loopAnimationCount)) + } + } + + @objc + public override var contentMode: UIView.ContentMode { + set { animationView.contentMode = newValue } + get { animationView.contentMode } + } + + @objc + public var shouldRasterizeWhenIdle: Bool { + set { animationView.shouldRasterizeWhenIdle = newValue } + get { animationView.shouldRasterizeWhenIdle } + } + + @objc + public var currentProgress: CGFloat { + set { animationView.currentProgress = newValue } + get { animationView.currentProgress } + } + + @objc + public var currentTime: TimeInterval { + set { animationView.currentTime = newValue } + get { animationView.currentTime } + } + + @objc + public var currentFrame: CGFloat { + set { animationView.currentFrame = newValue } + get { animationView.currentFrame } + } + + @objc + public var realtimeAnimationFrame: CGFloat { + animationView.realtimeAnimationFrame + } + + @objc + public var realtimeAnimationProgress: CGFloat { + animationView.realtimeAnimationProgress + } + + @objc + public var animationSpeed: CGFloat { + set { animationView.animationSpeed = newValue } + get { animationView.animationSpeed } + } + + @objc + public var respectAnimationFrameRate: Bool { + set { animationView.respectAnimationFrameRate = newValue } + get { animationView.respectAnimationFrameRate } + } + + @objc + public var isAnimationPlaying: Bool { + animationView.isAnimationPlaying + } + + @objc + public func play() { + play(completion: nil) + } + + @objc + public func play(completion: ((Bool) -> Void)?) { + animationView.play(completion: completion) + } + + @objc + public func play( + fromProgress: CGFloat, + toProgress: CGFloat, + completion: ((Bool) -> Void)? = nil) + { + animationView.play( + fromProgress: fromProgress, + toProgress: toProgress, + loopMode: nil, + completion: completion) + } + + @objc + public func play( + fromFrame: CGFloat, + toFrame: CGFloat, + completion: ((Bool) -> Void)? = nil) + { + animationView.play( + fromFrame: fromFrame, + toFrame: toFrame, + loopMode: nil, + completion: completion) + } + + @objc + public func play( + fromMarker: String, + toMarker: String, + completion: ((Bool) -> Void)? = nil) + { + animationView.play( + fromMarker: fromMarker, + toMarker: toMarker, + completion: completion) + } + + @objc + public func stop() { + animationView.stop() + } + + @objc + public func pause() { + animationView.pause() + } + + @objc + public func reloadImages() { + animationView.reloadImages() + } + + @objc + public func forceDisplayUpdate() { + animationView.forceDisplayUpdate() + } + + @objc + public func getValue( + for keypath: CompatibleAnimationKeypath, + atFrame: CGFloat) + -> Any? + { + animationView.getValue( + for: keypath.animationKeypath, + atFrame: atFrame) + } + + @objc + public func logHierarchyKeypaths() { + animationView.logHierarchyKeypaths() + } + + @objc + public func setColorValue(_ color: UIColor, forKeypath keypath: CompatibleAnimationKeypath) + { + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + // TODO: Fix color spaces + let colorspace = CGColorSpaceCreateDeviceRGB() + + let convertedColor = color.cgColor.converted(to: colorspace, intent: .defaultIntent, options: nil) + + if let components = convertedColor?.components, components.count == 4 { + red = components[0] + green = components[1] + blue = components[2] + alpha = components[3] + } else { + color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + } + + let valueProvider = ColorValueProvider(Color(r: Double(red), g: Double(green), b: Double(blue), a: Double(alpha))) + animationView.setValueProvider(valueProvider, keypath: keypath.animationKeypath) + } + + @objc + public func getColorValue(for keypath: CompatibleAnimationKeypath, atFrame: CGFloat) -> UIColor? + { + let value = animationView.getValue(for: keypath.animationKeypath, atFrame: atFrame) + guard let colorValue = value as? Color else { + return nil; + } + + return UIColor( + red: CGFloat(colorValue.r), + green: CGFloat(colorValue.g), + blue: CGFloat(colorValue.b), + alpha: CGFloat(colorValue.a)) + } + + @objc + public func addSubview( + _ subview: AnimationSubview, + forLayerAt keypath: CompatibleAnimationKeypath) + { + animationView.addSubview( + subview, + forLayerAt: keypath.animationKeypath) + } + + @objc + public func convert( + rect: CGRect, + toLayerAt keypath: CompatibleAnimationKeypath?) + -> CGRect + { + animationView.convert( + rect, + toLayerAt: keypath?.animationKeypath) ?? .zero + } + + @objc + public func convert( + point: CGPoint, + toLayerAt keypath: CompatibleAnimationKeypath?) + -> CGPoint + { + animationView.convert( + point, + toLayerAt: keypath?.animationKeypath) ?? .zero + } + + @objc + public func progressTime(forMarker named: String) -> CGFloat { + animationView.progressTime(forMarker: named) ?? 0 + } + + @objc + public func frameTime(forMarker named: String) -> CGFloat { + animationView.frameTime(forMarker: named) ?? 0 + } + + // MARK: Private + + private let animationView: AnimationView + + private func commonInit() { + translatesAutoresizingMaskIntoConstraints = false + setUpViews() + } + + private func setUpViews() { + animationView.translatesAutoresizingMaskIntoConstraints = false + addSubview(animationView) + animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true + animationView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + animationView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + } +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift new file mode 100644 index 00000000..41705edf --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift @@ -0,0 +1,63 @@ +// +// FilepathImageProvider.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/1/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +/** + Provides an image for a lottie animation from a provided Bundle. + */ +public class FilepathImageProvider: AnimationImageProvider { + + // MARK: Lifecycle + + /** + Initializes an image provider with a specific filepath. + + - Parameter filepath: The absolute filepath containing the images. + + */ + public init(filepath: String) { + self.filepath = URL(fileURLWithPath: filepath) + } + + public init(filepath: URL) { + self.filepath = filepath + } + + // MARK: Public + + public func imageForAsset(asset: ImageAsset) -> CGImage? { + + if + asset.name.hasPrefix("data:"), + let url = URL(string: asset.name), + let data = try? Data(contentsOf: url), + let image = UIImage(data: data) + { + return image.cgImage + } + + let directPath = filepath.appendingPathComponent(asset.name).path + if FileManager.default.fileExists(atPath: directPath) { + return UIImage(contentsOfFile: directPath)?.cgImage + } + + let pathWithDirectory = filepath.appendingPathComponent(asset.directory).appendingPathComponent(asset.name).path + if FileManager.default.fileExists(atPath: pathWithDirectory) { + return UIImage(contentsOfFile: pathWithDirectory)?.cgImage + } + + return nil + } + + // MARK: Internal + + let filepath: URL +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift new file mode 100644 index 00000000..352bb1c6 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift @@ -0,0 +1,76 @@ +// +// LottieView.swift +// lottie-swift-iOS +// +// Created by Brandon Withrow on 2/6/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +//public typealias LottieView = UIView + +open class LottieView: UIView { + + // MARK: Open + + open override var contentMode: UIView.ContentMode { + didSet { + setNeedsLayout() + } + } + + open override func didMoveToWindow() { + super.didMoveToWindow() + animationMovedToWindow() + } + + open override func layoutSubviews() { + super.layoutSubviews() + layoutAnimation() + } + + // MARK: Internal + + var viewLayer: CALayer? { + layer + } + + var screenScale: CGFloat { + UIScreen.main.scale + } + + func layoutAnimation() { + + } + + func animationMovedToWindow() { + + } + + func commonInit() { + contentMode = .scaleAspectFit + clipsToBounds = true + NotificationCenter.default.addObserver( + self, + selector: #selector(animationWillEnterForeground), + name: UIApplication.willEnterForegroundNotification, + object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(animationWillMoveToBackground), + name: UIApplication.didEnterBackgroundNotification, + object: nil) + } + + @objc + func animationWillMoveToBackground() { + } + + @objc + func animationWillEnterForeground() { + } + +} +#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift new file mode 100644 index 00000000..4cf335e0 --- /dev/null +++ b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift @@ -0,0 +1,21 @@ +// +// UIColorExtension.swift +// lottie-swift +// +// Created by Brandon Withrow on 2/4/19. +// + +import Foundation +#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) +import UIKit + +extension UIColor { + + public var lottieColorValue: Color { + var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 + getRed(&r, green: &g, blue: &b, alpha: &a) + return Color(r: Double(r), g: Double(g), b: Double(b), a: Double(a)) + } + +} +#endif diff --git a/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj b/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj new file mode 100644 index 00000000..58afa91b --- /dev/null +++ b/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj @@ -0,0 +1,500 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 0CF5F21A69185AE3EF49F2E4 /* Pods_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75118201331B9B43260F206F /* Pods_iosTests.framework */; }; + 206D51D19909DDC2DF2F8F9B /* Pods_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 07BD39C03FE8E8CAFD12089D /* Pods_ios.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E3999824D112043DE8E88846 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = EFF3EB6186DBCF3E4AC26B8F /* Project object */; + proxyType = 1; + remoteGlobalIDString = C67C026B3ECC938BFEAA06A4; + remoteInfo = ios; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 07598229C97B25EC09E1E750 /* ios.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = ios.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 07BD39C03FE8E8CAFD12089D /* Pods_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0E0BF18A09067936D0265C86 /* Pods-iosTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosTests.release.xcconfig"; path = "Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig"; sourceTree = ""; }; + 56F698D1A2EE3DA1EA36472F /* iosTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = iosTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 75118201331B9B43260F206F /* Pods_iosTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D8C67DC4A1CD159A069C9AEE /* Pods-iosTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosTests.debug.xcconfig"; path = "Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig"; sourceTree = ""; }; + F3A1614067EF4812EDA3D314 /* Pods-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.debug.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.debug.xcconfig"; sourceTree = ""; }; + FC5F267974B29AC83A364346 /* Pods-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.release.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 179C02C13A01CC36806AC803 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CF5F21A69185AE3EF49F2E4 /* Pods_iosTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1E3BD8F8C521406FB9F27C04 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 206D51D19909DDC2DF2F8F9B /* Pods_ios.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 036BA08F9EE41F4E16965C82 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 07BD39C03FE8E8CAFD12089D /* Pods_ios.framework */, + 75118201331B9B43260F206F /* Pods_iosTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 253D585F95682170F1B976EF /* Pods */ = { + isa = PBXGroup; + children = ( + F3A1614067EF4812EDA3D314 /* Pods-ios.debug.xcconfig */, + FC5F267974B29AC83A364346 /* Pods-ios.release.xcconfig */, + D8C67DC4A1CD159A069C9AEE /* Pods-iosTests.debug.xcconfig */, + 0E0BF18A09067936D0265C86 /* Pods-iosTests.release.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 56DF4E8E10A8991E1530FAE8 /* iosTests */ = { + isa = PBXGroup; + children = ( + ); + path = iosTests; + sourceTree = ""; + }; + 98DCE871D516BA368C096FB6 /* Products */ = { + isa = PBXGroup; + children = ( + 07598229C97B25EC09E1E750 /* ios.app */, + 56F698D1A2EE3DA1EA36472F /* iosTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + AD5CE3D685274DEA767D3FBA /* ios */ = { + isa = PBXGroup; + children = ( + ); + path = ios; + sourceTree = ""; + }; + DCC682D4A34A47286DC3668D = { + isa = PBXGroup; + children = ( + AD5CE3D685274DEA767D3FBA /* ios */, + 56DF4E8E10A8991E1530FAE8 /* iosTests */, + 98DCE871D516BA368C096FB6 /* Products */, + 253D585F95682170F1B976EF /* Pods */, + 036BA08F9EE41F4E16965C82 /* Frameworks */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3D1E8D49A855CF3E46D3D051 /* iosTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B09050E69F79472FFA774918 /* Build configuration list for PBXNativeTarget "iosTests" */; + buildPhases = ( + C7CB9ED39206B4C10DB34AFF /* [CP] Check Pods Manifest.lock */, + 4ADB27C84D37D42F473C6F1A /* Sources */, + 179C02C13A01CC36806AC803 /* Frameworks */, + D0657FBA6383576028DBBEE7 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 68B12C96D49C4EB6B147E614 /* PBXTargetDependency */, + ); + name = iosTests; + productName = iosTests; + productReference = 56F698D1A2EE3DA1EA36472F /* iosTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + C67C026B3ECC938BFEAA06A4 /* ios */ = { + isa = PBXNativeTarget; + buildConfigurationList = 254407FBAE608B768D0F34E8 /* Build configuration list for PBXNativeTarget "ios" */; + buildPhases = ( + 45255666F5A9D2C9859414B9 /* [CP] Check Pods Manifest.lock */, + 1A12E8A67ABB0C6B7845294E /* Sources */, + 1E3BD8F8C521406FB9F27C04 /* Frameworks */, + ADE380BA7BE133CCE1852A82 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ios; + productName = ios; + productReference = 07598229C97B25EC09E1E750 /* ios.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + EFF3EB6186DBCF3E4AC26B8F /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1200; + TargetAttributes = { + }; + }; + buildConfigurationList = 78809F4A16F4117614361355 /* Build configuration list for PBXProject "ios" */; + compatibilityVersion = "Xcode 11.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = DCC682D4A34A47286DC3668D; + productRefGroup = 98DCE871D516BA368C096FB6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C67C026B3ECC938BFEAA06A4 /* ios */, + 3D1E8D49A855CF3E46D3D051 /* iosTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 45255666F5A9D2C9859414B9 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ios-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + ADE380BA7BE133CCE1852A82 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C7CB9ED39206B4C10DB34AFF /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-iosTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D0657FBA6383576028DBBEE7 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1A12E8A67ABB0C6B7845294E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4ADB27C84D37D42F473C6F1A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 68B12C96D49C4EB6B147E614 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C67C026B3ECC938BFEAA06A4 /* ios */; + targetProxy = E3999824D112043DE8E88846 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1588C5088418414A4D125A34 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0E0BF18A09067936D0265C86 /* Pods-iosTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ios.app/ios"; + }; + name = Release; + }; + 3F06443FC191E03534A2DE7A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D8C67DC4A1CD159A069C9AEE /* Pods-iosTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ios.app/ios"; + }; + name = Debug; + }; + 94AAA1C5A9C82E2F2012CF9C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FC5F267974B29AC83A364346 /* Pods-ios.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + AB1A811F5CA8792139DE10E5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + CBFC39C2A51BF6D75162C1D0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + F406B4E41AD59D4051593F2C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F3A1614067EF4812EDA3D314 /* Pods-ios.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 254407FBAE608B768D0F34E8 /* Build configuration list for PBXNativeTarget "ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F406B4E41AD59D4051593F2C /* Debug */, + 94AAA1C5A9C82E2F2012CF9C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + 78809F4A16F4117614361355 /* Build configuration list for PBXProject "ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CBFC39C2A51BF6D75162C1D0 /* Debug */, + AB1A811F5CA8792139DE10E5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + B09050E69F79472FFA774918 /* Build configuration list for PBXNativeTarget "iosTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F06443FC191E03534A2DE7A /* Debug */, + 1588C5088418414A4D125A34 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = EFF3EB6186DBCF3E4AC26B8F /* Project object */; +} diff --git a/test/fixtures/cocoapods/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/test/fixtures/cocoapods/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/test/fixtures/cocoapods/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/test/fixtures/cocoapods/ios.xcworkspace/contents.xcworkspacedata b/test/fixtures/cocoapods/ios.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..b984d5dd --- /dev/null +++ b/test/fixtures/cocoapods/ios.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/test/fixtures/cocoapods/project.yml b/test/fixtures/cocoapods/project.yml new file mode 100644 index 00000000..8561b740 --- /dev/null +++ b/test/fixtures/cocoapods/project.yml @@ -0,0 +1,19 @@ +name: ios + +options: + deploymentTarget: "13.0" + +targets: + iosTests: + type: bundle.unit-test + platform: iOS + sources: + - path: iosTests + dependencies: + - target: ios + ios: + type: application + platform: iOS + deploymentTarget: "13.0" + sources: + - path: ios diff --git a/test/sources/cocoapods_test.rb b/test/sources/cocoapods_test.rb new file mode 100644 index 00000000..d7dfab67 --- /dev/null +++ b/test/sources/cocoapods_test.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true +require "test_helper" +require "tmpdir" + +if Licensed::Shell.tool_available?("pod") + describe Licensed::Sources::Cocoapods do + let(:fixtures) { File.expand_path("../../fixtures/cocoapods", __FILE__) } + let(:config) { Licensed::AppConfiguration.new({ "source_path" => Dir.pwd }) } + let(:source) { Licensed::Sources::Cocoapods.new(config) } + + describe "enabled?" do + it "is true if Podfiles exist" do + Dir.chdir(fixtures) do + assert source.enabled? + end + end + + it "is false if Podfiles do not exist" do + Dir.mktmpdir do |dir| + Dir.chdir(dir) do + refute source.enabled? + end + end + end + end + + describe "dependencies" do + it "finds Cocoapods dependencies" do + Dir.chdir(fixtures) do + dep = source.dependencies.find { |d| d.name == "Alamofire" } + assert dep + assert_equal "5.4.3", dep.version + end + end + + it "handle multiple subspecs from the same root dependencies" do + Dir.chdir fixtures do + assert source.dependencies.detect { |dep| dep.name == "MaterialComponents/Cards" } + assert source.dependencies.detect { |dep| dep.name == "MaterialComponents/Buttons" } + end + end + + it "supports pods from git" do + Dir.chdir(fixtures) do + dep = source.dependencies.detect { |d| d.name == "Chatto" } + end + end + + it "it adds metadata when available" do + Dir.chdir(fixtures) do + dep = source.dependencies.detect { |d| d.name == "Alamofire" } + assert dep.record["homepage"] != nil + assert dep.record["summary"] != nil + end + end + end + + describe "targets" do + it "includes only dependencies from target if configured" do + Dir.chdir fixtures do + config["cocoapods"] = { "targets" => ["iosTests"] } + assert source.dependencies.detect { |dep| dep.name == "lottie-ios" } + assert_nil source.dependencies.detect { |dep| dep.name == "Alamofire" } + end + end + end + end +end From 8af95de5f1ab31f9d698c1f129df724938a1eedd Mon Sep 17 00:00:00 2001 From: Louis Boudreau Date: Fri, 9 Dec 2022 10:56:02 -0500 Subject: [PATCH 2/5] Fix cocoa pods source-setup script --- script/source-setup/cocoapods | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 script/source-setup/cocoapods diff --git a/script/source-setup/cocoapods b/script/source-setup/cocoapods old mode 100644 new mode 100755 index e03a4a09..0e9795a8 --- a/script/source-setup/cocoapods +++ b/script/source-setup/cocoapods @@ -1,13 +1,13 @@ #!/bin/bash set -e -if [ -z "$(which pods)" ]; then +if [ -z "$(which pod)" ]; then echo "A local pod installation is required for cocoapods development." >&2 exit 127 fi # setup test fixtures -BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd $BASE_PATH/test/fixtures/cocoapods if [ "$1" == "-f" ]; then From cf41569ff447ac8a339c1ac40790d26b6482b3af Mon Sep 17 00:00:00 2001 From: Louis Boudreau Date: Mon, 12 Dec 2022 11:13:50 -0500 Subject: [PATCH 3/5] Requested changes --- .gitignore | 3 + lib/licensed/sources/cocoapods.rb | 34 +- licensed.gemspec | 4 +- test/fixtures/cocoapods/Podfile | 1 - test/fixtures/cocoapods/Podfile.lock | 102 - .../fixtures/cocoapods/Pods/Alamofire/LICENSE | 19 - .../cocoapods/Pods/Alamofire/README.md | 206 - .../Pods/Alamofire/Source/AFError.swift | 854 ---- .../Pods/Alamofire/Source/Alamofire.swift | 29 - .../Alamofire/Source/AlamofireExtended.swift | 61 - .../Source/AuthenticationInterceptor.swift | 404 -- .../Source/CachedResponseHandler.swift | 91 - .../Pods/Alamofire/Source/Combine.swift | 622 --- .../Source/DispatchQueue+Alamofire.swift | 37 - .../Pods/Alamofire/Source/EventMonitor.swift | 892 ----- .../Pods/Alamofire/Source/HTTPHeaders.swift | 449 --- .../Pods/Alamofire/Source/HTTPMethod.swift | 54 - .../Alamofire/Source/MultipartFormData.swift | 553 --- .../Alamofire/Source/MultipartUpload.swift | 89 - .../Source/NetworkReachabilityManager.swift | 267 -- .../Pods/Alamofire/Source/Notifications.swift | 115 - .../Source/OperationQueue+Alamofire.swift | 49 - .../Alamofire/Source/ParameterEncoder.swift | 184 - .../Alamofire/Source/ParameterEncoding.swift | 317 -- .../Pods/Alamofire/Source/Protected.swift | 224 -- .../Alamofire/Source/RedirectHandler.swift | 95 - .../Pods/Alamofire/Source/Request.swift | 1882 --------- .../Alamofire/Source/RequestInterceptor.swift | 244 -- .../Alamofire/Source/RequestTaskMap.swift | 149 - .../Pods/Alamofire/Source/Response.swift | 454 --- .../Source/ResponseSerialization.swift | 1116 ------ .../Alamofire/Source/Result+Alamofire.swift | 120 - .../Pods/Alamofire/Source/RetryPolicy.swift | 370 -- .../Source/ServerTrustEvaluation.swift | 619 --- .../Pods/Alamofire/Source/Session.swift | 1258 ------ .../Alamofire/Source/SessionDelegate.swift | 330 -- .../Source/StringEncoding+Alamofire.swift | 55 - ...URLConvertible+URLRequestConvertible.swift | 105 - .../Source/URLEncodedFormEncoder.swift | 976 ----- .../Source/URLRequest+Alamofire.swift | 39 - .../URLSessionConfiguration+Alamofire.swift | 37 - .../Pods/Alamofire/Source/Validation.swift | 302 -- .../Chat Items/BaseChatItemPresenter.swift | 123 - .../Source/Chat Items/ChatItemCompanion.swift | 52 - .../ChatItemProtocolDefinitions.swift | 84 - .../Chat Items/DummyChatItemPresenter.swift | 57 - ...ViewController+CellPanGestureHandler.swift | 36 - .../BaseChatViewController+Changes.swift | 373 -- .../BaseChatViewController+Presenters.swift | 137 - .../BaseChatViewController+Scrolling.swift | 205 - .../BaseChatViewController.swift | 600 --- .../ChatLayoutConfiguration.swift | 49 - .../BaseChatViewControllerView.swift | 45 - .../Collaborators/CellPanGestureHandler.swift | 200 - .../ChatCollectionViewLayout.swift | 170 - .../ChatDataSourceProtocol.swift | 49 - .../ChatItemPresenterFactory.swift | 54 - .../Collaborators/CollectionChanges.swift | 123 - .../Collaborators/KeyboardTracker.swift | 277 -- .../ReplyFeedbackGenerator.swift | 38 - .../InputPositionControlling.swift | 39 - .../Source/ChatItemCompanionCollection.swift | 79 - .../Chatto/Source/SerialTaskQueue.swift | 80 - .../Pods/Chatto/Chatto/Source/Utils.swift | 69 - test/fixtures/cocoapods/Pods/Chatto/LICENSE | 22 - test/fixtures/cocoapods/Pods/Chatto/README.md | 92 - .../Pods/Local Podspecs/Chatto.podspec.json | 26 - .../Pods/MDFInternationalization/LICENSE | 202 - .../Pods/MDFInternationalization/README.md | 74 - .../Sources/MDFInternationalization.h | 29 - .../MDFInternationalization/Sources/MDFRTL.h | 105 - .../MDFInternationalization/Sources/MDFRTL.m | 73 - .../Sources/NSLocale+MaterialRTL.h | 36 - .../Sources/NSLocale+MaterialRTL.m | 38 - .../Sources/NSString+MaterialBidi.h | 84 - .../Sources/NSString+MaterialBidi.m | 165 - .../Sources/UIImage+MaterialRTL.h | 33 - .../Sources/UIImage+MaterialRTL.m | 168 - .../Sources/UIView+MaterialRTL.h | 91 - .../Sources/UIView+MaterialRTL.m | 167 - .../Pods/MDFTextAccessibility/LICENSE | 202 - .../Pods/MDFTextAccessibility/README.md | 101 - .../MDFTextAccessibility-Bridging-Header.h | 17 - .../src/MDFTextAccessibility.h | 199 - .../src/MDFTextAccessibility.m | 188 - .../src/private/MDFColorCalculations.h | 70 - .../src/private/MDFColorCalculations.m | 184 - .../src/private/MDFImageCalculations.h | 37 - .../src/private/MDFImageCalculations.m | 54 - .../src/private/NSArray+MDFUtils.h | 53 - .../src/private/NSArray+MDFUtils.m | 68 - test/fixtures/cocoapods/Pods/Manifest.lock | 102 - .../cocoapods/Pods/MaterialComponents/LICENSE | 202 - .../Pods/MaterialComponents/README.md | 71 - ...CAMediaTimingFunction+MDCAnimationTiming.h | 82 - ...CAMediaTimingFunction+MDCAnimationTiming.m | 37 - .../src/MaterialAnimationTiming.h | 16 - .../src/UIView+MDCTimingFunction.h | 37 - .../src/UIView+MDCTimingFunction.m | 35 - .../Availability/src/MDCAvailability.h | 39 - .../Availability/src/MaterialAvailability.h | 16 - .../components/Buttons/src/MDCButton.h | 453 --- .../components/Buttons/src/MDCButton.m | 1452 ------- .../components/Buttons/src/MDCFlatButton.h | 49 - .../components/Buttons/src/MDCFlatButton.m | 47 - .../Buttons/src/MDCFloatingButton+Animation.h | 49 - .../Buttons/src/MDCFloatingButton+Animation.m | 285 -- .../Buttons/src/MDCFloatingButton.h | 275 -- .../Buttons/src/MDCFloatingButton.m | 573 --- .../components/Buttons/src/MDCRaisedButton.h | 32 - .../components/Buttons/src/MDCRaisedButton.m | 28 - .../components/Buttons/src/MaterialButtons.h | 19 - .../src/private/MDCButton+Subclassing.h | 57 - .../private/MDCFloatingButtonModeAnimator.h | 49 - .../private/MDCFloatingButtonModeAnimator.m | 206 - .../MDCFloatingButtonModeAnimatorDelegate.h | 35 - .../components/Cards/src/MDCCard.h | 169 - .../components/Cards/src/MDCCard.m | 379 -- .../Cards/src/MDCCardCollectionCell.h | 334 -- .../Cards/src/MDCCardCollectionCell.m | 659 ---- .../components/Cards/src/MaterialCards.h | 16 - ...llectionViewController+MDCCardReordering.h | 28 - ...llectionViewController+MDCCardReordering.m | 39 - .../components/Elevation/src/MDCElevatable.h | 42 - .../Elevation/src/MDCElevationOverriding.h | 34 - .../Elevation/src/MaterialElevation.h | 18 - .../Elevation/src/UIColor+MaterialElevation.h | 65 - .../Elevation/src/UIColor+MaterialElevation.m | 85 - .../src/UIView+MaterialElevationResponding.h | 53 - .../src/UIView+MaterialElevationResponding.m | 108 - .../Ink/src/MDCInkGestureRecognizer.h | 60 - .../Ink/src/MDCInkGestureRecognizer.m | 94 - .../Ink/src/MDCInkTouchController.h | 129 - .../Ink/src/MDCInkTouchController.m | 222 -- .../Ink/src/MDCInkTouchControllerDelegate.h | 84 - .../components/Ink/src/MDCInkView.h | 180 - .../components/Ink/src/MDCInkView.m | 326 -- .../components/Ink/src/MDCInkViewDelegate.h | 41 - .../components/Ink/src/MaterialInk.h | 19 - .../components/Ink/src/private/MDCInkLayer.h | 108 - .../components/Ink/src/private/MDCInkLayer.m | 246 -- .../Ink/src/private/MDCInkLayerDelegate.h | 41 - .../src/private/MDCLegacyInkLayer+Private.h | 46 - .../Ink/src/private/MDCLegacyInkLayer.h | 112 - .../Ink/src/private/MDCLegacyInkLayer.m | 664 ---- .../src/private/MDCLegacyInkLayerDelegate.h | 41 - .../private/MDCLegacyInkLayerRippleDelegate.h | 29 - .../Ripple/src/MDCRippleTouchController.h | 113 - .../Ripple/src/MDCRippleTouchController.m | 205 - .../src/MDCRippleTouchControllerDelegate.h | 87 - .../components/Ripple/src/MDCRippleView.h | 170 - .../components/Ripple/src/MDCRippleView.m | 349 -- .../Ripple/src/MDCRippleViewDelegate.h | 55 - .../Ripple/src/MDCStatefulRippleView.h | 165 - .../Ripple/src/MDCStatefulRippleView.m | 273 -- .../components/Ripple/src/MaterialRipple.h | 19 - .../Ripple/src/private/MDCRippleLayer.h | 96 - .../Ripple/src/private/MDCRippleLayer.m | 194 - .../src/private/MDCRippleLayerDelegate.h | 53 - .../components/Shadow/src/MDCShadow.h | 59 - .../components/Shadow/src/MDCShadow.m | 69 - .../Shadow/src/MDCShadowsCollection.h | 145 - .../Shadow/src/MDCShadowsCollection.m | 165 - .../components/Shadow/src/MaterialShadow.h | 17 - .../src/MDCShadowElevations.h | 106 - .../src/MaterialShadowElevations.h | 15 - .../src/MaterialShadowElevationsDummy.m | 17 - .../ShadowLayer/src/MDCShadowLayer.h | 106 - .../ShadowLayer/src/MDCShadowLayer.m | 471 --- .../ShadowLayer/src/MaterialShadowLayer.h | 15 - .../MDCCornerTreatment+CornerTypeInitalizer.h | 80 - .../MDCCornerTreatment+CornerTypeInitalizer.m | 60 - .../src/MDCCurvedCornerTreatment.h | 40 - .../src/MDCCurvedCornerTreatment.m | 72 - .../src/MDCCurvedRectShapeGenerator.h | 35 - .../src/MDCCurvedRectShapeGenerator.m | 68 - .../ShapeLibrary/src/MDCCutCornerTreatment.h | 57 - .../ShapeLibrary/src/MDCCutCornerTreatment.m | 72 - .../ShapeLibrary/src/MDCPillShapeGenerator.h | 24 - .../ShapeLibrary/src/MDCPillShapeGenerator.m | 52 - .../src/MDCRoundedCornerTreatment.h | 38 - .../src/MDCRoundedCornerTreatment.m | 72 - .../src/MDCSlantedRectShapeGenerator.h | 31 - .../src/MDCSlantedRectShapeGenerator.m | 53 - .../src/MDCTriangleEdgeTreatment.h | 47 - .../src/MDCTriangleEdgeTreatment.m | 43 - .../ShapeLibrary/src/MaterialShapeLibrary.h | 22 - .../Shapes/src/MDCCornerTreatment.h | 73 - .../Shapes/src/MDCCornerTreatment.m | 56 - .../components/Shapes/src/MDCEdgeTreatment.h | 38 - .../components/Shapes/src/MDCEdgeTreatment.m | 35 - .../components/Shapes/src/MDCPathGenerator.h | 116 - .../components/Shapes/src/MDCPathGenerator.m | 190 - .../Shapes/src/MDCRectangleShapeGenerator.h | 64 - .../Shapes/src/MDCRectangleShapeGenerator.m | 251 -- .../Shapes/src/MDCShapeGenerating.h | 30 - .../components/Shapes/src/MDCShapeMediator.h | 116 - .../components/Shapes/src/MDCShapeMediator.m | 197 - .../Shapes/src/MDCShapedShadowLayer.h | 81 - .../Shapes/src/MDCShapedShadowLayer.m | 224 -- .../components/Shapes/src/MDCShapedView.h | 70 - .../components/Shapes/src/MDCShapedView.m | 97 - .../components/Shapes/src/MaterialShapes.h | 22 - .../components/Typography/src/MDCFontScaler.h | 80 - .../components/Typography/src/MDCFontScaler.m | 293 -- .../Typography/src/MDCFontTextStyle.h | 38 - .../components/Typography/src/MDCTypography.h | 196 - .../components/Typography/src/MDCTypography.m | 324 -- .../Typography/src/MaterialTypography.h | 30 - .../Typography/src/UIFont+MaterialScalable.h | 88 - .../Typography/src/UIFont+MaterialScalable.m | 86 - .../src/UIFont+MaterialSimpleEquality.h | 31 - .../src/UIFont+MaterialSimpleEquality.m | 28 - .../src/UIFont+MaterialTypography.h | 49 - .../src/UIFont+MaterialTypography.m | 80 - .../src/UIFontDescriptor+MaterialTypography.h | 41 - .../src/UIFontDescriptor+MaterialTypography.m | 83 - .../Typography/src/private/MDCFontTraits.h | 60 - .../Typography/src/private/MDCFontTraits.m | 511 --- .../src/private/MDCTypographyUtilities.h | 17 - .../src/private/MDCTypographyUtilities.m | 25 - .../UIFont+MaterialTypographyPrivate.h | 42 - .../UIFont+MaterialTypographyPrivate.m | 113 - .../Application/src/MaterialApplication.h | 14 - .../src/UIApplication+MDCAppExtensions.h | 34 - .../src/UIApplication+MDCAppExtensions.m | 41 - .../private/Color/src/MaterialColor.h | 16 - .../Color/src/UIColor+MaterialBlending.h | 29 - .../Color/src/UIColor+MaterialBlending.m | 45 - .../Color/src/UIColor+MaterialDynamic.h | 56 - .../Color/src/UIColor+MaterialDynamic.m | 73 - .../src/MaterialIcons+ic_check_circle.h | 30 - .../src/MaterialIcons+ic_check_circle.m | 34 - .../Contents.json | 6 - .../ic_check_circle.imageset/Contents.json | 23 - .../ic_check_circle.png | Bin 260 -> 0 bytes .../ic_check_circle@2x.png | Bin 493 -> 0 bytes .../ic_check_circle@3x.png | Bin 709 -> 0 bytes .../private/Icons/src/MDCIcons+BundleLoader.h | 32 - .../components/private/Icons/src/MDCIcons.h | 29 - .../components/private/Icons/src/MDCIcons.m | 47 - .../private/Icons/src/MaterialIcons.h | 16 - .../components/private/Math/src/MDCMath.h | 256 -- .../private/Math/src/MaterialMath.h | 15 - .../private/Math/src/MaterialMathDummy.m | 17 - .../Pods/Pods.xcodeproj/project.pbxproj | 3465 ----------------- .../xcschemes/Alamofire.xcscheme | 58 - .../xcschemes/Chatto.xcscheme | 58 - .../MDFInternationalization.xcscheme | 58 - .../xcschemes/MDFTextAccessibility.xcscheme | 58 - ...nts-MaterialIcons_ic_check_circle.xcscheme | 58 - .../xcschemes/MaterialComponents.xcscheme | 58 - .../xcschemes/Pods-ios.xcscheme | 58 - .../xcschemes/Pods-iosTests.xcscheme | 58 - .../xcschemes/lottie-ios.xcscheme | 58 - .../xcschemes/xcschememanagement.plist | 56 - .../Alamofire/Alamofire-Info.plist | 26 - .../Alamofire/Alamofire-dummy.m | 5 - .../Alamofire/Alamofire-prefix.pch | 12 - .../Alamofire/Alamofire-umbrella.h | 16 - .../Alamofire/Alamofire.debug.xcconfig | 14 - .../Alamofire/Alamofire.modulemap | 6 - .../Alamofire/Alamofire.release.xcconfig | 14 - .../Chatto/Chatto-Info.plist | 26 - .../Chatto/Chatto-dummy.m | 5 - .../Chatto/Chatto-prefix.pch | 12 - .../Chatto/Chatto-umbrella.h | 16 - .../Chatto/Chatto.debug.xcconfig | 13 - .../Chatto/Chatto.modulemap | 6 - .../Chatto/Chatto.release.xcconfig | 13 - .../MDFInternationalization-Info.plist | 26 - .../MDFInternationalization-dummy.m | 5 - .../MDFInternationalization-prefix.pch | 12 - .../MDFInternationalization-umbrella.h | 22 - .../MDFInternationalization.debug.xcconfig | 11 - .../MDFInternationalization.modulemap | 6 - .../MDFInternationalization.release.xcconfig | 11 - .../MDFTextAccessibility-Info.plist | 26 - .../MDFTextAccessibility-dummy.m | 5 - .../MDFTextAccessibility-prefix.pch | 12 - .../MDFTextAccessibility-umbrella.h | 17 - .../MDFTextAccessibility.debug.xcconfig | 11 - .../MDFTextAccessibility.modulemap | 6 - .../MDFTextAccessibility.release.xcconfig | 11 - .../MaterialComponents-Info.plist | 26 - .../MaterialComponents-dummy.m | 5 - .../MaterialComponents-prefix.pch | 12 - .../MaterialComponents-umbrella.h | 92 - .../MaterialComponents.debug.xcconfig | 13 - .../MaterialComponents.modulemap | 6 - .../MaterialComponents.release.xcconfig | 13 - ...check_circle-MaterialComponents-Info.plist | 24 - .../Pods-ios/Pods-ios-Info.plist | 26 - .../Pods-ios-acknowledgements.markdown | 670 ---- .../Pods-ios/Pods-ios-acknowledgements.plist | 726 ---- .../Pods-ios/Pods-ios-dummy.m | 5 - ...os-frameworks-Debug-input-files.xcfilelist | 6 - ...s-frameworks-Debug-output-files.xcfilelist | 5 - ...-frameworks-Release-input-files.xcfilelist | 6 - ...frameworks-Release-output-files.xcfilelist | 5 - .../Pods-ios/Pods-ios-frameworks.sh | 194 - .../Pods-ios/Pods-ios-umbrella.h | 16 - .../Pods-ios/Pods-ios.debug.xcconfig | 15 - .../Pods-ios/Pods-ios.modulemap | 6 - .../Pods-ios/Pods-ios.release.xcconfig | 15 - .../Pods-iosTests/Pods-iosTests-Info.plist | 26 - .../Pods-iosTests-acknowledgements.markdown | 208 - .../Pods-iosTests-acknowledgements.plist | 240 -- .../Pods-iosTests/Pods-iosTests-dummy.m | 5 - ...ts-frameworks-Debug-input-files.xcfilelist | 2 - ...s-frameworks-Debug-output-files.xcfilelist | 1 - ...-frameworks-Release-input-files.xcfilelist | 2 - ...frameworks-Release-output-files.xcfilelist | 1 - .../Pods-iosTests/Pods-iosTests-frameworks.sh | 186 - .../Pods-iosTests/Pods-iosTests-umbrella.h | 16 - .../Pods-iosTests.debug.xcconfig | 15 - .../Pods-iosTests/Pods-iosTests.modulemap | 6 - .../Pods-iosTests.release.xcconfig | 15 - .../lottie-ios/lottie-ios-Info.plist | 26 - .../lottie-ios/lottie-ios-dummy.m | 5 - .../lottie-ios/lottie-ios-prefix.pch | 12 - .../lottie-ios/lottie-ios-umbrella.h | 16 - .../lottie-ios/lottie-ios.debug.xcconfig | 14 - .../lottie-ios/lottie-ios.modulemap | 6 - .../lottie-ios/lottie-ios.release.xcconfig | 14 - .../cocoapods/Pods/lottie-ios/LICENSE | 201 - .../cocoapods/Pods/lottie-ios/README.md | 114 - .../LayerContainers/AnimationContainer.swift | 244 -- .../CompLayers/CompositionLayer.swift | 160 - .../CompLayers/ImageCompositionLayer.swift | 50 - .../CompLayers/MaskContainerLayer.swift | 190 - .../CompLayers/NullCompositionLayer.swift | 28 - .../CompLayers/PreCompositionLayer.swift | 121 - .../CompLayers/ShapeCompositionLayer.swift | 60 - .../CompLayers/SolidCompositionLayer.swift | 57 - .../CompLayers/TextCompositionLayer.swift | 149 - .../CompositionLayersInitializer.swift | 90 - .../Utility/InvertedMatteLayer.swift | 61 - .../Utility/LayerFontProvider.swift | 41 - .../Utility/LayerImageProvider.swift | 53 - .../Utility/LayerTextProvider.swift | 40 - .../Utility/LayerTransformNode.swift | 144 - .../LayerContainers/Utility/TextLayer.swift | 321 -- .../Sources/Private/Model/Animation.swift | 116 - .../Sources/Private/Model/Assets/Asset.swift | 33 - .../Private/Model/Assets/AssetLibrary.swift | 52 - .../Private/Model/Assets/ImageAsset.swift | 53 - .../Private/Model/Assets/PrecompAsset.swift | 34 - .../Private/Model/Extensions/Bundle.swift | 21 - .../KeyedDecodingContainerExtensions.swift | 44 - .../Private/Model/Keyframes/Keyframe.swift | 145 - .../Model/Keyframes/KeyframeGroup.swift | 120 - .../Model/Layers/ImageLayerModel.swift | 37 - .../Private/Model/Layers/LayerModel.swift | 166 - .../Model/Layers/PreCompLayerModel.swift | 55 - .../Model/Layers/ShapeLayerModel.swift | 37 - .../Model/Layers/SolidLayerModel.swift | 49 - .../Private/Model/Layers/TextLayerModel.swift | 49 - .../Private/Model/Objects/DashPattern.swift | 29 - .../Private/Model/Objects/Marker.swift | 23 - .../Sources/Private/Model/Objects/Mask.swift | 56 - .../Private/Model/Objects/Transform.swift | 111 - .../Private/Model/ShapeItems/Ellipse.swift | 59 - .../Private/Model/ShapeItems/FillI.swift | 58 - .../Model/ShapeItems/GradientFill.swift | 95 - .../Model/ShapeItems/GradientStroke.swift | 136 - .../Private/Model/ShapeItems/Group.swift | 37 - .../Private/Model/ShapeItems/Merge.swift | 50 - .../Private/Model/ShapeItems/Rectangle.swift | 55 - .../Private/Model/ShapeItems/Repeater.swift | 91 - .../Private/Model/ShapeItems/Shape.swift | 42 - .../Private/Model/ShapeItems/ShapeItem.swift | 106 - .../Model/ShapeItems/ShapeTransform.swift | 76 - .../Private/Model/ShapeItems/Star.swift | 95 - .../Private/Model/ShapeItems/Stroke.swift | 73 - .../Private/Model/ShapeItems/Trim.swift | 63 - .../Sources/Private/Model/Text/Font.swift | 42 - .../Sources/Private/Model/Text/Glyph.swift | 80 - .../Private/Model/Text/TextAnimator.swift | 105 - .../Private/Model/Text/TextDocument.swift | 78 - .../Extensions/ItemsExtension.swift | 97 - .../NodeProperties/NodeProperty.swift | 53 - .../Protocols/AnyNodeProperty.swift | 47 - .../Protocols/AnyValueContainer.swift | 26 - .../Protocols/KeypathSearchable.swift | 24 - .../Protocols/NodePropertyMap.swift | 44 - .../NodeProperties/ValueContainer.swift | 47 - .../ValueProviders/GroupInterpolator.swift | 38 - .../ValueProviders/KeyframeInterpolator.swift | 256 -- .../ValueProviders/SingleValueProvider.swift | 44 - .../Nodes/ModifierNodes/TrimPathNode.swift | 281 -- .../Nodes/OutputNodes/GroupOutputNode.swift | 76 - .../OutputNodes/PassThroughOutputNode.swift | 47 - .../Nodes/OutputNodes/PathOutputNode.swift | 90 - .../Renderables/FillRenderer.swift | 74 - .../Renderables/GradientFillRenderer.swift | 149 - .../Renderables/GradientStrokeRenderer.swift | 62 - .../Renderables/StrokeRenderer.swift | 162 - .../Nodes/PathNodes/EllipseNode.swift | 126 - .../Nodes/PathNodes/PolygonNode.swift | 152 - .../Nodes/PathNodes/RectNode.swift | 205 - .../Nodes/PathNodes/ShapeNode.swift | 74 - .../Nodes/PathNodes/StarNode.swift | 201 - .../Nodes/RenderContainers/GroupNode.swift | 155 - .../Nodes/RenderNodes/FillNode.swift | 90 - .../Nodes/RenderNodes/GradientFillNode.swift | 102 - .../RenderNodes/GradientStrokeNode.swift | 151 - .../Nodes/RenderNodes/StrokeNode.swift | 138 - .../Nodes/Text/TextAnimatorNode.swift | 270 -- .../Protocols/AnimatorNode.swift | 204 - .../NodeRenderSystem/Protocols/PathNode.swift | 22 - .../Protocols/RenderNode.swift | 61 - .../RenderLayers/ShapeContainerLayer.swift | 77 - .../RenderLayers/ShapeRenderLayer.swift | 99 - .../Debugging/AnimatorNodeDebugging.swift | 25 - .../Utility/Debugging/LayerDebugging.swift | 226 -- .../AnimationKeypathExtension.swift | 268 -- .../Extensions/CGFloatExtensions.swift | 152 - .../Private/Utility/Extensions/MathKit.swift | 579 --- .../Utility/Extensions/StringExtensions.swift | 33 - .../Utility/Helpers/AnimationContext.swift | 78 - .../Interpolatable/Interpolatable.swift | 19 - .../InterpolatableExtensions.swift | 217 -- .../Interpolatable/KeyframeExtensions.swift | 47 - .../Utility/Primitives/BezierPath.swift | 423 -- .../Utility/Primitives/ColorExtension.swift | 82 - .../Primitives/CompoundBezierPath.swift | 169 - .../Utility/Primitives/CurveVertex.swift | 192 - .../Utility/Primitives/PathElement.swift | 77 - .../Primitives/VectorsExtensions.swift | 283 -- .../Public/Animation/AnimationPublic.swift | 206 - .../Public/Animation/AnimationView.swift | 1045 ----- .../Animation/AnimationViewInitializers.swift | 96 - .../AnimationCacheProvider.swift | 24 - .../AnimationCache/LRUAnimationCache.swift | 63 - .../DynamicProperties/AnimationKeypath.swift | 46 - .../DynamicProperties/AnyValueProvider.swift | 29 - .../ValueProviders/ColorValueProvider.swift | 70 - .../ValueProviders/FloatValueProvider.swift | 69 - .../GradientValueProvider.swift | 122 - .../ValueProviders/PointValueProvider.swift | 68 - .../ValueProviders/SizeValueProvider.swift | 69 - .../FontProvider/AnimationFontProvider.swift | 37 - .../AnimationImageProvider.swift | 23 - .../Public/Primitives/AnimationTime.swift | 15 - .../Sources/Public/Primitives/Color.swift | 45 - .../Sources/Public/Primitives/Vectors.swift | 40 - .../TextProvider/AnimationTextProvider.swift | 55 - .../Sources/Public/iOS/AnimatedButton.swift | 77 - .../Sources/Public/iOS/AnimatedControl.swift | 173 - .../Sources/Public/iOS/AnimatedSwitch.swift | 216 - .../Sources/Public/iOS/AnimationSubview.swift | 20 - .../Public/iOS/BundleImageProvider.swift | 94 - .../CompatibleAnimationKeypath.swift | 33 - .../CompatibleAnimationView.swift | 325 -- .../Public/iOS/FilepathImageProvider.swift | 63 - .../Sources/Public/iOS/LottieView.swift | 76 - .../Sources/Public/iOS/UIColorExtension.swift | 21 - .../cocoapods/ios.xcodeproj/project.pbxproj | 145 - test/fixtures/command/cocoapods.yml | 5 + 460 files changed, 24 insertions(+), 58328 deletions(-) delete mode 100644 test/fixtures/cocoapods/Podfile.lock delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/LICENSE delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/README.md delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift delete mode 100644 test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/LICENSE delete mode 100644 test/fixtures/cocoapods/Pods/Chatto/README.md delete mode 100644 test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/README.md delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h delete mode 100644 test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m delete mode 100644 test/fixtures/cocoapods/Pods/Manifest.lock delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/README.md delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons+BundleLoader.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h delete mode 100644 test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme delete mode 100644 test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist delete mode 100755 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist delete mode 100755 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap delete mode 100644 test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/LICENSE delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/README.md delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/RectNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/KeyframeExtensions.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/PathElement.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift delete mode 100644 test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift create mode 100644 test/fixtures/command/cocoapods.yml diff --git a/.gitignore b/.gitignore index ef70e6e5..a413ac58 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,9 @@ test/fixtures/cargo/* test/fixtures/swift/.build +test/fixtures/cocoapods/Podfile.lock +test/fixtures/cocoapods/Pods + vendor/licenses .licenses_test *.gem diff --git a/lib/licensed/sources/cocoapods.rb b/lib/licensed/sources/cocoapods.rb index 3eaf67c3..9059def6 100644 --- a/lib/licensed/sources/cocoapods.rb +++ b/lib/licensed/sources/cocoapods.rb @@ -14,41 +14,35 @@ def enabled? end def enumerate_dependencies - pods.map { |pod| - metadata = dependency_metadata(pod) + pods.map do |pod| + name = pod.name path = dependency_path(pod.root_name) + version = lockfile.version(name).version Dependency.new( path: path, - name: metadata["name"], - version: metadata["version"], - metadata: { - "type" => Cocoapods.type, - "homepage" => metadata["homepage"], - "summary" => metadata["summary"], - "platforms" => metadata["platform"] - } + name: name, + version: version, + metadata: { "type" => Cocoapods.type } ) - } + end end private def pods - if targets.nil? - lockfile.dependencies + return lockfile.dependencies if targets.nil? + + targets_to_validate = podfile.target_definition_list.filter { |t| targets.include?(t.label) } + if targets_to_validate.any? + targets_to_validate.map(&:dependencies).flatten else - targets_to_validate = podfile.target_definition_list.filter { |t| targets.include?(t.label) } - if targets_to_validate.any? - targets_to_validate.map(&:dependencies).flatten - else - raise Licensed::Sources::Source::Error, "Unable to find any target in the Podfile matching the ones provided in the config." - end + raise Licensed::Sources::Source::Error, "Unable to find any target in the Podfile matching the ones provided in the config." end end def targets - config.dig("cocoapods", "targets")&.map { |t| "Pods-#{t}" } + @targets ||= config.dig("cocoapods", "targets")&.map { |t| "Pods-#{t}" } end def lockfile diff --git a/licensed.gemspec b/licensed.gemspec index e90847e9..94749b13 100644 --- a/licensed.gemspec +++ b/licensed.gemspec @@ -32,12 +32,12 @@ Gem::Specification.new do |spec| spec.add_dependency "parallel", ">= 0.18.0" spec.add_dependency "reverse_markdown", ">= 1", "< 3" spec.add_dependency "json", ">= 2.6.2" - spec.add_dependency "cocoapods-core", "~> 1.0.0" + spec.add_dependency "cocoapods-core", "~> 1.0" spec.add_development_dependency "rake", ">= 12.3.3" spec.add_development_dependency "minitest", "~> 5.8" spec.add_development_dependency "mocha", "~> 2.0" spec.add_development_dependency "rubocop-github", "~> 0.6" spec.add_development_dependency "byebug", "~> 11.1.3" - spec.add_development_dependency "cocoapods", "~> 1.0.0" + spec.add_development_dependency "cocoapods", "~> 1.0" end diff --git a/test/fixtures/cocoapods/Podfile b/test/fixtures/cocoapods/Podfile index 15761896..19f5fe13 100644 --- a/test/fixtures/cocoapods/Podfile +++ b/test/fixtures/cocoapods/Podfile @@ -15,5 +15,4 @@ target 'ios' do pod "lottie-ios", "3.3.0" end - end diff --git a/test/fixtures/cocoapods/Podfile.lock b/test/fixtures/cocoapods/Podfile.lock deleted file mode 100644 index dd84759f..00000000 --- a/test/fixtures/cocoapods/Podfile.lock +++ /dev/null @@ -1,102 +0,0 @@ -PODS: - - Alamofire (5.4.3) - - Chatto (4.1.0) - - lottie-ios (3.3.0) - - MaterialComponents/AnimationTiming (124.2.0) - - MaterialComponents/Availability (124.2.0) - - MaterialComponents/Buttons (124.2.0): - - MaterialComponents/Elevation - - MaterialComponents/Ink - - MaterialComponents/private/Math - - MaterialComponents/Ripple - - MaterialComponents/Shadow - - MaterialComponents/ShadowElevations - - MaterialComponents/ShadowLayer - - MaterialComponents/ShapeLibrary - - MaterialComponents/Shapes - - MaterialComponents/Typography - - MDFInternationalization - - MDFTextAccessibility - - MaterialComponents/Cards (124.2.0): - - MaterialComponents/Elevation - - MaterialComponents/Ink - - MaterialComponents/private/Icons/ic_check_circle - - MaterialComponents/private/Math - - MaterialComponents/Ripple - - MaterialComponents/ShadowLayer - - MaterialComponents/Shapes - - MaterialComponents/Elevation (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/Ink (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/private/Application (124.2.0) - - MaterialComponents/private/Color (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/private/Icons/Base (124.2.0) - - MaterialComponents/private/Icons/ic_check_circle (124.2.0): - - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Math (124.2.0) - - MaterialComponents/Ripple (124.2.0): - - MaterialComponents/AnimationTiming - - MaterialComponents/Availability - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/Shadow (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/ShadowElevations (124.2.0) - - MaterialComponents/ShadowLayer (124.2.0): - - MaterialComponents/ShadowElevations - - MaterialComponents/ShapeLibrary (124.2.0): - - MaterialComponents/private/Math - - MaterialComponents/Shapes - - MaterialComponents/Shapes (124.2.0): - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/ShadowLayer - - MaterialComponents/Typography (124.2.0): - - MaterialComponents/private/Application - - MaterialComponents/private/Math - - MDFTextAccessibility - - MDFInternationalization (3.0.0) - - MDFTextAccessibility (2.0.1) - -DEPENDENCIES: - - Alamofire (= 5.4.3) - - Chatto (from `https://github.com/badoo/Chatto`, tag `4.1.0`) - - lottie-ios (= 3.3.0) - - MaterialComponents/Buttons (= 124.2.0) - - MaterialComponents/Cards (= 124.2.0) - -SPEC REPOS: - trunk: - - Alamofire - - lottie-ios - - MaterialComponents - - MDFInternationalization - - MDFTextAccessibility - -EXTERNAL SOURCES: - Chatto: - :git: https://github.com/badoo/Chatto - :tag: 4.1.0 - -CHECKOUT OPTIONS: - Chatto: - :git: https://github.com/badoo/Chatto - :tag: 4.1.0 - -SPEC CHECKSUMS: - Alamofire: e447a2774a40c996748296fa2c55112fdbbc42f9 - Chatto: dae34c377e0eca8bdcfe7a05a4d79d55eaccec3e - lottie-ios: 6ac74dcc09904798f59b18cb3075c089d76be9ae - MaterialComponents: 1a9b2d9d45b1601ae544de85089adc4c464306d4 - MDFInternationalization: d697c55307816222a55685c4ccb1044ffb030c12 - MDFTextAccessibility: f4bb4cc2194286651b59a215fdeaa0e05dc90ba5 - -PODFILE CHECKSUM: 5288d14d2586d983b7a18fae6fc10abda0f306a9 - -COCOAPODS: 1.11.2 diff --git a/test/fixtures/cocoapods/Pods/Alamofire/LICENSE b/test/fixtures/cocoapods/Pods/Alamofire/LICENSE deleted file mode 100644 index 6b4d719a..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014-2021 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. diff --git a/test/fixtures/cocoapods/Pods/Alamofire/README.md b/test/fixtures/cocoapods/Pods/Alamofire/README.md deleted file mode 100644 index 9a5a8063..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/README.md +++ /dev/null @@ -1,206 +0,0 @@ -![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png) - -[![Build Status](https://github.com/Alamofire/Alamofire/workflows/Alamofire%20CI/badge.svg?branch=master)](https://github.com/Alamofire/Alamofire/actions) -[![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)](https://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) -[![Open Source Helpers](https://www.codetriage.com/alamofire/alamofire/badges/users.svg)](https://www.codetriage.com/alamofire/alamofire) - -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](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#using-alamofire) - - [**Introduction -**](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#introduction) [Making Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-requests), [Response Handling](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-handling), [Response Validation](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-validation), [Response Caching](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-caching) - - **HTTP -** [HTTP Methods](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-methods), [Parameters and Parameter Encoder](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md##request-parameters-and-parameter-encoders), [HTTP Headers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-headers), [Authentication](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#authentication) - - **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server) - - **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output) -- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md) - - **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#sessiondelegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request) - - **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor) - - **Model Objects -** [Custom Response Handlers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#customizing-response-handlers) - - **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability) -- [Open Radars](#open-radars) -- [FAQ](#faq) -- [Credits](#credits) -- [Donations](#donations) -- [License](#license) - -## Features - -- [x] Chainable Request / Response Methods -- [x] Combine Support -- [x] URL / JSON 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 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ -- Xcode 11+ -- Swift 5.1+ - -## Migration Guides - -- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md) -- [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 with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`. -- If you need to **find or understand an API**, check [our documentation](http://alamofire.github.io/Alamofire/) or [Apple's documentation for `URLSession`](https://developer.apple.com/documentation/foundation/url_loading_system), on top of which Alamofire is built. -- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). -- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). -- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). -- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better! -- If you **want to contribute**, submit a pull request! - -## Installation - -### CocoaPods - -[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```ruby -pod 'Alamofire', '~> 5.2' -``` - -### Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: - -```ogdl -github "Alamofire/Alamofire" ~> 5.2 -``` - -### 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", .upToNextMajor(from: "5.2.0")) -] -``` - -### 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](https://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 macOS. - - > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as `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. - -## 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 the test case -- `rdar://26870455` - Background URL Session Configurations do not work in the simulator -- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` -- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS - -## 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): 9/1/17 in Xcode 9 beta 6. -- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+ - - (Resolved): Just add `CFNetwork` to your linked frameworks. - -## Workarounds - -- Collection of `URLSessionTaskMetrics` is currently disabled on watchOS due to `FB7624529`. - -## 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. - -## 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 stay registered as a federal non-profit organization. -Registering will allow Foundation 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 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. -Any amount you can donate today to help us reach our goal would be greatly appreciated. - -[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W34WPEE74APJQ) - -## License - -Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details. diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift deleted file mode 100644 index e8e4fe83..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/AFError.swift +++ /dev/null @@ -1,854 +0,0 @@ -// -// AFError.swift -// -// Copyright (c) 2014-2018 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. -public enum AFError: Error { - /// The underlying reason the `.multipartEncodingFailed` error occurred. - public enum MultipartEncodingFailureReason { - /// The `fileURL` provided for reading an encodable body part isn't a file `URL`. - case bodyPartURLInvalid(url: URL) - /// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension. - case bodyPartFilenameInvalid(in: URL) - /// The file at the `fileURL` provided was not reachable. - case bodyPartFileNotReachable(at: URL) - /// Attempting to check the reachability of the `fileURL` provided threw an error. - case bodyPartFileNotReachableWithError(atURL: URL, error: Error) - /// The file at the `fileURL` provided is actually a directory. - case bodyPartFileIsDirectory(at: URL) - /// The size of the file at the `fileURL` provided was not returned by the system. - case bodyPartFileSizeNotAvailable(at: URL) - /// The attempt to find the size of the file at the `fileURL` provided threw an error. - case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) - /// An `InputStream` could not be created for the provided `fileURL`. - case bodyPartInputStreamCreationFailed(for: URL) - /// An `OutputStream` could not be created when attempting to write the encoded data to disk. - case outputStreamCreationFailed(for: URL) - /// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`. - case outputStreamFileAlreadyExists(at: URL) - /// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`. - case outputStreamURLInvalid(url: URL) - /// The attempt to write the encoded body data to disk failed with an underlying error. - case outputStreamWriteFailed(error: Error) - /// The attempt to read an encoded body part `InputStream` failed with underlying system error. - case inputStreamReadFailed(error: Error) - } - - /// Represents unexpected input stream length that occur when encoding the `MultipartFormData`. Instances will be - /// embedded within an `AFError.multipartEncodingFailed` `.inputStreamReadFailed` case. - public struct UnexpectedInputStreamLength: Error { - /// The expected byte count to read. - public var bytesExpected: UInt64 - /// The actual byte count read. - public var bytesRead: UInt64 - } - - /// The underlying reason the `.parameterEncodingFailed` error occurred. - public enum ParameterEncodingFailureReason { - /// The `URLRequest` did not have a `URL` to encode. - case missingURL - /// JSON serialization failed with an underlying system error during the encoding process. - case jsonEncodingFailed(error: Error) - /// Custom parameter encoding failed due to the associated `Error`. - case customEncodingFailed(error: Error) - } - - /// The underlying reason the `.parameterEncoderFailed` error occurred. - public enum ParameterEncoderFailureReason { - /// Possible missing components. - public enum RequiredComponent { - /// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding. - case url - /// The `HTTPMethod` could not be extracted from the passed `URLRequest`. - case httpMethod(rawValue: String) - } - - /// A `RequiredComponent` was missing during encoding. - case missingRequiredComponent(RequiredComponent) - /// The underlying encoder failed with the associated error. - case encoderFailed(error: Error) - } - - /// The underlying reason the `.responseValidationFailed` error occurred. - public enum ResponseValidationFailureReason { - /// The data file containing the server response did not exist. - case dataFileNil - /// The data file containing the server response at the associated `URL` could not be read. - case dataFileReadFailed(at: URL) - /// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a - /// wildcard type. - case missingContentType(acceptableContentTypes: [String]) - /// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`. - case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) - /// The response status code was not acceptable. - case unacceptableStatusCode(code: Int) - /// Custom response validation failed due to the associated `Error`. - case customValidationFailed(error: Error) - } - - /// The underlying reason the response serialization error occurred. - public enum ResponseSerializationFailureReason { - /// The server response contained no data or the data was zero length. - case inputDataNilOrZeroLength - /// The file containing the server response did not exist. - case inputFileNil - /// The file containing the server response could not be read from the associated `URL`. - case inputFileReadFailed(at: URL) - /// String serialization failed using the provided `String.Encoding`. - case stringSerializationFailed(encoding: String.Encoding) - /// JSON serialization failed with an underlying system error. - case jsonSerializationFailed(error: Error) - /// A `DataDecoder` failed to decode the response due to the associated `Error`. - case decodingFailed(error: Error) - /// A custom response serializer failed due to the associated `Error`. - case customSerializationFailed(error: Error) - /// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type. - case invalidEmptyResponse(type: String) - } - - /// Underlying reason a server trust evaluation error occurred. - public enum ServerTrustFailureReason { - /// The output of a server trust evaluation. - public struct Output { - /// The host for which the evaluation was performed. - public let host: String - /// The `SecTrust` value which was evaluated. - public let trust: SecTrust - /// The `OSStatus` of evaluation operation. - public let status: OSStatus - /// The result of the evaluation operation. - public let result: SecTrustResultType - - /// Creates an `Output` value from the provided values. - init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) { - self.host = host - self.trust = trust - self.status = status - self.result = result - } - } - - /// No `ServerTrustEvaluator` was found for the associated host. - case noRequiredEvaluator(host: String) - /// No certificates were found with which to perform the trust evaluation. - case noCertificatesFound - /// No public keys were found with which to perform the trust evaluation. - case noPublicKeysFound - /// During evaluation, application of the associated `SecPolicy` failed. - case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus) - /// During evaluation, setting the associated anchor certificates failed. - case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate]) - /// During evaluation, creation of the revocation policy failed. - case revocationPolicyCreationFailed - /// `SecTrust` evaluation failed with the associated `Error`, if one was produced. - case trustEvaluationFailed(error: Error?) - /// Default evaluation failed with the associated `Output`. - case defaultEvaluationFailed(output: Output) - /// Host validation failed with the associated `Output`. - case hostValidationFailed(output: Output) - /// Revocation check failed with the associated `Output` and options. - case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options) - /// Certificate pinning failed. - case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate]) - /// Public key pinning failed. - case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey]) - /// Custom server trust evaluation failed due to the associated `Error`. - case customEvaluationFailed(error: Error) - } - - /// The underlying reason the `.urlRequestValidationFailed` - public enum URLRequestValidationFailureReason { - /// URLRequest with GET method had body data. - case bodyDataInGETRequest(Data) - } - - /// `UploadableConvertible` threw an error in `createUploadable()`. - case createUploadableFailed(error: Error) - /// `URLRequestConvertible` threw an error in `asURLRequest()`. - case createURLRequestFailed(error: Error) - /// `SessionDelegate` threw an error while attempting to move downloaded file to destination URL. - case downloadedFileMoveFailed(error: Error, source: URL, destination: URL) - /// `Request` was explicitly cancelled. - case explicitlyCancelled - /// `URLConvertible` type failed to create a valid `URL`. - case invalidURL(url: URLConvertible) - /// Multipart form encoding failed. - case multipartEncodingFailed(reason: MultipartEncodingFailureReason) - /// `ParameterEncoding` threw an error during the encoding process. - case parameterEncodingFailed(reason: ParameterEncodingFailureReason) - /// `ParameterEncoder` threw an error while running the encoder. - case parameterEncoderFailed(reason: ParameterEncoderFailureReason) - /// `RequestAdapter` threw an error during adaptation. - case requestAdaptationFailed(error: Error) - /// `RequestRetrier` threw an error during the request retry process. - case requestRetryFailed(retryError: Error, originalError: Error) - /// Response validation failed. - case responseValidationFailed(reason: ResponseValidationFailureReason) - /// Response serialization failed. - case responseSerializationFailed(reason: ResponseSerializationFailureReason) - /// `ServerTrustEvaluating` instance threw an error during trust evaluation. - case serverTrustEvaluationFailed(reason: ServerTrustFailureReason) - /// `Session` which issued the `Request` was deinitialized, most likely because its reference went out of scope. - case sessionDeinitialized - /// `Session` was explicitly invalidated, possibly with the `Error` produced by the underlying `URLSession`. - case sessionInvalidated(error: Error?) - /// `URLSessionTask` completed with error. - case sessionTaskFailed(error: Error) - /// `URLRequest` failed validation. - case urlRequestValidationFailed(reason: URLRequestValidationFailureReason) -} - -extension Error { - /// Returns the instance cast as an `AFError`. - public var asAFError: AFError? { - self as? AFError - } - - /// Returns the instance cast as an `AFError`. If casting fails, a `fatalError` with the specified `message` is thrown. - public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError { - guard let afError = self as? AFError else { - fatalError(message(), file: file, line: line) - } - return afError - } - - /// Casts the instance as `AFError` or returns `defaultAFError` - func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError { - self as? AFError ?? defaultAFError() - } -} - -// MARK: - Error Booleans - -extension AFError { - /// Returns whether the instance is `.sessionDeinitialized`. - public var isSessionDeinitializedError: Bool { - if case .sessionDeinitialized = self { return true } - return false - } - - /// Returns whether the instance is `.sessionInvalidated`. - public var isSessionInvalidatedError: Bool { - if case .sessionInvalidated = self { return true } - return false - } - - /// Returns whether the instance is `.explicitlyCancelled`. - public var isExplicitlyCancelledError: Bool { - if case .explicitlyCancelled = self { return true } - return false - } - - /// Returns whether the instance is `.invalidURL`. - public var isInvalidURLError: Bool { - if case .invalidURL = self { return true } - return false - } - - /// Returns whether the instance is `.parameterEncodingFailed`. 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 instance is `.parameterEncoderFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isParameterEncoderError: Bool { - if case .parameterEncoderFailed = self { return true } - return false - } - - /// Returns whether the instance is `.multipartEncodingFailed`. 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 instance is `.requestAdaptationFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isRequestAdaptationError: Bool { - if case .requestAdaptationFailed = self { return true } - return false - } - - /// Returns whether the instance is `.responseValidationFailed`. When `true`, the `acceptableContentTypes`, - /// `responseContentType`, `responseCode`, and `underlyingError` properties will contain the associated values. - public var isResponseValidationError: Bool { - if case .responseValidationFailed = self { return true } - return false - } - - /// Returns whether the instance is `.responseSerializationFailed`. When `true`, the `failedStringEncoding` and - /// `underlyingError` properties will contain the associated values. - public var isResponseSerializationError: Bool { - if case .responseSerializationFailed = self { return true } - return false - } - - /// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isServerTrustEvaluationError: Bool { - if case .serverTrustEvaluationFailed = self { return true } - return false - } - - /// Returns whether the instance is `requestRetryFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isRequestRetryError: Bool { - if case .requestRetryFailed = self { return true } - return false - } - - /// Returns whether the instance is `createUploadableFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isCreateUploadableError: Bool { - if case .createUploadableFailed = self { return true } - return false - } - - /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isCreateURLRequestError: Bool { - if case .createURLRequestFailed = self { return true } - return false - } - - /// Returns whether the instance is `downloadedFileMoveFailed`. When `true`, the `destination` and `underlyingError` properties will - /// contain the associated values. - public var isDownloadedFileMoveError: Bool { - if case .downloadedFileMoveFailed = self { return true } - return false - } - - /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will - /// contain the associated value. - public var isSessionTaskError: Bool { - if case .sessionTaskFailed = self { return true } - return false - } -} - -// MARK: - Convenience Properties - -extension AFError { - /// The `URLConvertible` associated with the error. - public var urlConvertible: URLConvertible? { - guard case let .invalidURL(url) = self else { return nil } - return url - } - - /// The `URL` associated with the error. - public var url: URL? { - guard case let .multipartEncodingFailed(reason) = self else { return nil } - return reason.url - } - - /// The underlying `Error` responsible for generating the failure associated with `.sessionInvalidated`, - /// `.parameterEncodingFailed`, `.parameterEncoderFailed`, `.multipartEncodingFailed`, `.requestAdaptationFailed`, - /// `.responseSerializationFailed`, `.requestRetryFailed` errors. - public var underlyingError: Error? { - switch self { - case let .multipartEncodingFailed(reason): - return reason.underlyingError - case let .parameterEncodingFailed(reason): - return reason.underlyingError - case let .parameterEncoderFailed(reason): - return reason.underlyingError - case let .requestAdaptationFailed(error): - return error - case let .requestRetryFailed(retryError, _): - return retryError - case let .responseValidationFailed(reason): - return reason.underlyingError - case let .responseSerializationFailed(reason): - return reason.underlyingError - case let .serverTrustEvaluationFailed(reason): - return reason.underlyingError - case let .sessionInvalidated(error): - return error - case let .createUploadableFailed(error): - return error - case let .createURLRequestFailed(error): - return error - case let .downloadedFileMoveFailed(error, _, _): - return error - case let .sessionTaskFailed(error): - return error - case .explicitlyCancelled, - .invalidURL, - .sessionDeinitialized, - .urlRequestValidationFailed: - return nil - } - } - - /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. - public var acceptableContentTypes: [String]? { - guard case let .responseValidationFailed(reason) = self else { return nil } - return reason.acceptableContentTypes - } - - /// The response `Content-Type` of a `.responseValidationFailed` error. - public var responseContentType: String? { - guard case let .responseValidationFailed(reason) = self else { return nil } - return reason.responseContentType - } - - /// The response code of a `.responseValidationFailed` error. - public var responseCode: Int? { - guard case let .responseValidationFailed(reason) = self else { return nil } - return reason.responseCode - } - - /// The `String.Encoding` associated with a failed `.stringResponse()` call. - public var failedStringEncoding: String.Encoding? { - guard case let .responseSerializationFailed(reason) = self else { return nil } - return reason.failedStringEncoding - } - - /// The `source` URL of a `.downloadedFileMoveFailed` error. - public var sourceURL: URL? { - guard case let .downloadedFileMoveFailed(_, source, _) = self else { return nil } - return source - } - - /// The `destination` URL of a `.downloadedFileMoveFailed` error. - public var destinationURL: URL? { - guard case let .downloadedFileMoveFailed(_, _, destination) = self else { return nil } - return destination - } - - /// The download resume data of any underlying network error. Only produced by `DownloadRequest`s. - public var downloadResumeData: Data? { - (underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data - } -} - -extension AFError.ParameterEncodingFailureReason { - var underlyingError: Error? { - switch self { - case let .jsonEncodingFailed(error), - let .customEncodingFailed(error): - return error - case .missingURL: - return nil - } - } -} - -extension AFError.ParameterEncoderFailureReason { - var underlyingError: Error? { - switch self { - case let .encoderFailed(error): - return error - case .missingRequiredComponent: - return nil - } - } -} - -extension AFError.MultipartEncodingFailureReason { - var url: URL? { - switch self { - case let .bodyPartURLInvalid(url), - let .bodyPartFilenameInvalid(url), - let .bodyPartFileNotReachable(url), - let .bodyPartFileIsDirectory(url), - let .bodyPartFileSizeNotAvailable(url), - let .bodyPartInputStreamCreationFailed(url), - let .outputStreamCreationFailed(url), - let .outputStreamFileAlreadyExists(url), - let .outputStreamURLInvalid(url), - let .bodyPartFileNotReachableWithError(url, _), - let .bodyPartFileSizeQueryFailedWithError(url, _): - return url - case .outputStreamWriteFailed, - .inputStreamReadFailed: - return nil - } - } - - var underlyingError: Error? { - switch self { - case let .bodyPartFileNotReachableWithError(_, error), - let .bodyPartFileSizeQueryFailedWithError(_, error), - let .outputStreamWriteFailed(error), - let .inputStreamReadFailed(error): - return error - case .bodyPartURLInvalid, - .bodyPartFilenameInvalid, - .bodyPartFileNotReachable, - .bodyPartFileIsDirectory, - .bodyPartFileSizeNotAvailable, - .bodyPartInputStreamCreationFailed, - .outputStreamCreationFailed, - .outputStreamFileAlreadyExists, - .outputStreamURLInvalid: - return nil - } - } -} - -extension AFError.ResponseValidationFailureReason { - var acceptableContentTypes: [String]? { - switch self { - case let .missingContentType(types), - let .unacceptableContentType(types, _): - return types - case .dataFileNil, - .dataFileReadFailed, - .unacceptableStatusCode, - .customValidationFailed: - return nil - } - } - - var responseContentType: String? { - switch self { - case let .unacceptableContentType(_, responseType): - return responseType - case .dataFileNil, - .dataFileReadFailed, - .missingContentType, - .unacceptableStatusCode, - .customValidationFailed: - return nil - } - } - - var responseCode: Int? { - switch self { - case let .unacceptableStatusCode(code): - return code - case .dataFileNil, - .dataFileReadFailed, - .missingContentType, - .unacceptableContentType, - .customValidationFailed: - return nil - } - } - - var underlyingError: Error? { - switch self { - case let .customValidationFailed(error): - return error - case .dataFileNil, - .dataFileReadFailed, - .missingContentType, - .unacceptableContentType, - .unacceptableStatusCode: - return nil - } - } -} - -extension AFError.ResponseSerializationFailureReason { - var failedStringEncoding: String.Encoding? { - switch self { - case let .stringSerializationFailed(encoding): - return encoding - case .inputDataNilOrZeroLength, - .inputFileNil, - .inputFileReadFailed(_), - .jsonSerializationFailed(_), - .decodingFailed(_), - .customSerializationFailed(_), - .invalidEmptyResponse: - return nil - } - } - - var underlyingError: Error? { - switch self { - case let .jsonSerializationFailed(error), - let .decodingFailed(error), - let .customSerializationFailed(error): - return error - case .inputDataNilOrZeroLength, - .inputFileNil, - .inputFileReadFailed, - .stringSerializationFailed, - .invalidEmptyResponse: - return nil - } - } -} - -extension AFError.ServerTrustFailureReason { - var output: AFError.ServerTrustFailureReason.Output? { - switch self { - case let .defaultEvaluationFailed(output), - let .hostValidationFailed(output), - let .revocationCheckFailed(output, _): - return output - case .noRequiredEvaluator, - .noCertificatesFound, - .noPublicKeysFound, - .policyApplicationFailed, - .settingAnchorCertificatesFailed, - .revocationPolicyCreationFailed, - .trustEvaluationFailed, - .certificatePinningFailed, - .publicKeyPinningFailed, - .customEvaluationFailed: - return nil - } - } - - var underlyingError: Error? { - switch self { - case let .customEvaluationFailed(error): - return error - case let .trustEvaluationFailed(error): - return error - case .noRequiredEvaluator, - .noCertificatesFound, - .noPublicKeysFound, - .policyApplicationFailed, - .settingAnchorCertificatesFailed, - .revocationPolicyCreationFailed, - .defaultEvaluationFailed, - .hostValidationFailed, - .revocationCheckFailed, - .certificatePinningFailed, - .publicKeyPinningFailed: - return nil - } - } -} - -// MARK: - Error Descriptions - -extension AFError: LocalizedError { - public var errorDescription: String? { - switch self { - case .explicitlyCancelled: - return "Request explicitly cancelled." - case let .invalidURL(url): - return "URL is not valid: \(url)" - case let .parameterEncodingFailed(reason): - return reason.localizedDescription - case let .parameterEncoderFailed(reason): - return reason.localizedDescription - case let .multipartEncodingFailed(reason): - return reason.localizedDescription - case let .requestAdaptationFailed(error): - return "Request adaption failed with error: \(error.localizedDescription)" - case let .responseValidationFailed(reason): - return reason.localizedDescription - case let .responseSerializationFailed(reason): - return reason.localizedDescription - case let .requestRetryFailed(retryError, originalError): - return """ - Request retry failed with retry error: \(retryError.localizedDescription), \ - original error: \(originalError.localizedDescription) - """ - case .sessionDeinitialized: - return """ - Session was invalidated without error, so it was likely deinitialized unexpectedly. \ - Be sure to retain a reference to your Session for the duration of your requests. - """ - case let .sessionInvalidated(error): - return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")" - case let .serverTrustEvaluationFailed(reason): - return "Server trust evaluation failed due to reason: \(reason.localizedDescription)" - case let .urlRequestValidationFailed(reason): - return "URLRequest validation failed due to reason: \(reason.localizedDescription)" - case let .createUploadableFailed(error): - return "Uploadable creation failed with error: \(error.localizedDescription)" - case let .createURLRequestFailed(error): - return "URLRequest creation failed with error: \(error.localizedDescription)" - case let .downloadedFileMoveFailed(error, source, destination): - return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)" - case let .sessionTaskFailed(error): - return "URLSessionTask failed with error: \(error.localizedDescription)" - } - } -} - -extension AFError.ParameterEncodingFailureReason { - var localizedDescription: String { - switch self { - case .missingURL: - return "URL request to encode was missing a URL" - case let .jsonEncodingFailed(error): - return "JSON could not be encoded because of error:\n\(error.localizedDescription)" - case let .customEncodingFailed(error): - return "Custom parameter encoder failed with error: \(error.localizedDescription)" - } - } -} - -extension AFError.ParameterEncoderFailureReason { - var localizedDescription: String { - switch self { - case let .missingRequiredComponent(component): - return "Encoding failed due to a missing request component: \(component)" - case let .encoderFailed(error): - return "The underlying encoder failed with the error: \(error)" - } - } -} - -extension AFError.MultipartEncodingFailureReason { - var localizedDescription: String { - switch self { - case let .bodyPartURLInvalid(url): - return "The URL provided is not a file URL: \(url)" - case let .bodyPartFilenameInvalid(url): - return "The URL provided does not have a valid filename: \(url)" - case let .bodyPartFileNotReachable(url): - return "The URL provided is not reachable: \(url)" - case let .bodyPartFileNotReachableWithError(url, error): - return """ - The system returned an error while checking the provided URL for reachability. - URL: \(url) - Error: \(error) - """ - case let .bodyPartFileIsDirectory(url): - return "The URL provided is a directory: \(url)" - case let .bodyPartFileSizeNotAvailable(url): - return "Could not fetch the file size from the provided URL: \(url)" - case let .bodyPartFileSizeQueryFailedWithError(url, error): - return """ - The system returned an error while attempting to fetch the file size from the provided URL. - URL: \(url) - Error: \(error) - """ - case let .bodyPartInputStreamCreationFailed(url): - return "Failed to create an InputStream for the provided URL: \(url)" - case let .outputStreamCreationFailed(url): - return "Failed to create an OutputStream for URL: \(url)" - case let .outputStreamFileAlreadyExists(url): - return "A file already exists at the provided URL: \(url)" - case let .outputStreamURLInvalid(url): - return "The provided OutputStream URL is invalid: \(url)" - case let .outputStreamWriteFailed(error): - return "OutputStream write failed with error: \(error)" - case let .inputStreamReadFailed(error): - return "InputStream read failed with error: \(error)" - } - } -} - -extension AFError.ResponseSerializationFailureReason { - var localizedDescription: String { - switch self { - 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 let .inputFileReadFailed(url): - return "Response could not be serialized, input file could not be read: \(url)." - case let .stringSerializationFailed(encoding): - return "String could not be serialized with encoding: \(encoding)." - case let .jsonSerializationFailed(error): - return "JSON could not be serialized because of error:\n\(error.localizedDescription)" - case let .invalidEmptyResponse(type): - return """ - Empty response could not be serialized to type: \(type). \ - Use Empty as the expected type for such responses. - """ - case let .decodingFailed(error): - return "Response could not be decoded because of error:\n\(error.localizedDescription)" - case let .customSerializationFailed(error): - return "Custom response serializer failed with 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 let .dataFileReadFailed(url): - return "Response could not be validated, data file could not be read: \(url)." - case let .missingContentType(types): - return """ - Response Content-Type was missing and acceptable content types \ - (\(types.joined(separator: ","))) do not match "*/*". - """ - case let .unacceptableContentType(acceptableTypes, responseType): - return """ - Response Content-Type "\(responseType)" does not match any acceptable types: \ - \(acceptableTypes.joined(separator: ",")). - """ - case let .unacceptableStatusCode(code): - return "Response status code was unacceptable: \(code)." - case let .customValidationFailed(error): - return "Custom response validation failed with error: \(error.localizedDescription)" - } - } -} - -extension AFError.ServerTrustFailureReason { - var localizedDescription: String { - switch self { - case let .noRequiredEvaluator(host): - return "A ServerTrustEvaluating value is required for host \(host) but none was found." - case .noCertificatesFound: - return "No certificates were found or provided for evaluation." - case .noPublicKeysFound: - return "No public keys were found or provided for evaluation." - case .policyApplicationFailed: - return "Attempting to set a SecPolicy failed." - case .settingAnchorCertificatesFailed: - return "Attempting to set the provided certificates as anchor certificates failed." - case .revocationPolicyCreationFailed: - return "Attempting to create a revocation policy failed." - case let .trustEvaluationFailed(error): - return "SecTrust evaluation failed with error: \(error?.localizedDescription ?? "None")" - case let .defaultEvaluationFailed(output): - return "Default evaluation failed for host \(output.host)." - case let .hostValidationFailed(output): - return "Host validation failed for host \(output.host)." - case let .revocationCheckFailed(output, _): - return "Revocation check failed for host \(output.host)." - case let .certificatePinningFailed(host, _, _, _): - return "Certificate pinning failed for host \(host)." - case let .publicKeyPinningFailed(host, _, _, _): - return "Public key pinning failed for host \(host)." - case let .customEvaluationFailed(error): - return "Custom trust evaluation failed with error: \(error.localizedDescription)" - } - } -} - -extension AFError.URLRequestValidationFailureReason { - var localizedDescription: String { - switch self { - case let .bodyDataInGETRequest(data): - return """ - Invalid URLRequest: Requests with GET method cannot have body data: - \(String(decoding: data, as: UTF8.self)) - """ - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift deleted file mode 100644 index bcf43d02..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Alamofire.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// Alamofire.swift -// -// Copyright (c) 2014-2021 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. -// - -/// Reference to `Session.default` for quick bootstrapping and examples. -public let AF = Session.default - -/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. -let version = "5.4.3" diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift deleted file mode 100644 index 280c6de9..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/AlamofireExtended.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// AlamofireExtended.swift -// -// Copyright (c) 2019 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. -// - -/// Type that acts as a generic extension point for all `AlamofireExtended` types. -public struct AlamofireExtension { - /// Stores the type or meta-type of any extended type. - public private(set) var type: ExtendedType - - /// Create an instance from the provided value. - /// - /// - Parameter type: Instance being extended. - public init(_ type: ExtendedType) { - self.type = type - } -} - -/// Protocol describing the `af` extension points for Alamofire extended types. -public protocol AlamofireExtended { - /// Type being extended. - associatedtype ExtendedType - - /// Static Alamofire extension point. - static var af: AlamofireExtension.Type { get set } - /// Instance Alamofire extension point. - var af: AlamofireExtension { get set } -} - -extension AlamofireExtended { - /// Static Alamofire extension point. - public static var af: AlamofireExtension.Type { - get { AlamofireExtension.self } - set {} - } - - /// Instance Alamofire extension point. - public var af: AlamofireExtension { - get { AlamofireExtension(self) } - set {} - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift deleted file mode 100644 index 9d7f8360..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/AuthenticationInterceptor.swift +++ /dev/null @@ -1,404 +0,0 @@ -// -// AuthenticationInterceptor.swift -// -// Copyright (c) 2020 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 `AuthenticationCredential` protocol can be used to authenticate `URLRequest`s. -/// -/// One common example of an `AuthenticationCredential` is an OAuth2 credential containing an access token used to -/// authenticate all requests on behalf of a user. The access token generally has an expiration window of 60 minutes -/// which will then require a refresh of the credential using the refresh token to generate a new access token. -public protocol AuthenticationCredential { - /// Whether the credential requires a refresh. This property should always return `true` when the credential is - /// expired. It is also wise to consider returning `true` when the credential will expire in several seconds or - /// minutes depending on the expiration window of the credential. - /// - /// For example, if the credential is valid for 60 minutes, then it would be wise to return `true` when the - /// credential is only valid for 5 minutes or less. That ensures the credential will not expire as it is passed - /// around backend services. - var requiresRefresh: Bool { get } -} - -// MARK: - - -/// Types adopting the `Authenticator` protocol can be used to authenticate `URLRequest`s with an -/// `AuthenticationCredential` as well as refresh the `AuthenticationCredential` when required. -public protocol Authenticator: AnyObject { - /// The type of credential associated with the `Authenticator` instance. - associatedtype Credential: AuthenticationCredential - - /// Applies the `Credential` to the `URLRequest`. - /// - /// In the case of OAuth2, the access token of the `Credential` would be added to the `URLRequest` as a Bearer - /// token to the `Authorization` header. - /// - /// - Parameters: - /// - credential: The `Credential`. - /// - urlRequest: The `URLRequest`. - func apply(_ credential: Credential, to urlRequest: inout URLRequest) - - /// Refreshes the `Credential` and executes the `completion` closure with the `Result` once complete. - /// - /// Refresh can be called in one of two ways. It can be called before the `Request` is actually executed due to - /// a `requiresRefresh` returning `true` during the adapt portion of the `Request` creation process. It can also - /// be triggered by a failed `Request` where the authentication server denied access due to an expired or - /// invalidated access token. - /// - /// In the case of OAuth2, this method would use the refresh token of the `Credential` to generate a new - /// `Credential` using the authentication service. Once complete, the `completion` closure should be called with - /// the new `Credential`, or the error that occurred. - /// - /// In general, if the refresh call fails with certain status codes from the authentication server (commonly a 401), - /// the refresh token in the `Credential` can no longer be used to generate a valid `Credential`. In these cases, - /// you will need to reauthenticate the user with their username / password. - /// - /// Please note, these are just general examples of common use cases. They are not meant to solve your specific - /// authentication server challenges. Please work with your authentication server team to ensure your - /// `Authenticator` logic matches their expectations. - /// - /// - Parameters: - /// - credential: The `Credential` to refresh. - /// - session: The `Session` requiring the refresh. - /// - completion: The closure to be executed once the refresh is complete. - func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result) -> Void) - - /// Determines whether the `URLRequest` failed due to an authentication error based on the `HTTPURLResponse`. - /// - /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `false` - /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then you - /// will need to work with your authentication server team to understand how to identify when this occurs. - /// - /// In the case of OAuth2, where an authentication server can invalidate credentials, you will need to inspect the - /// `HTTPURLResponse` or possibly the `Error` for when this occurs. This is commonly handled by the authentication - /// server returning a 401 status code and some additional header to indicate an OAuth2 failure occurred. - /// - /// It is very important to understand how your authentication server works to be able to implement this correctly. - /// For example, if your authentication server returns a 401 when an OAuth2 error occurs, and your downstream - /// service also returns a 401 when you are not authorized to perform that operation, how do you know which layer - /// of the backend returned you a 401? You do not want to trigger a refresh unless you know your authentication - /// server is actually the layer rejecting the request. Again, work with your authentication server team to understand - /// how to identify an OAuth2 401 error vs. a downstream 401 error to avoid endless refresh loops. - /// - /// - Parameters: - /// - urlRequest: The `URLRequest`. - /// - response: The `HTTPURLResponse`. - /// - error: The `Error`. - /// - /// - Returns: `true` if the `URLRequest` failed due to an authentication error, `false` otherwise. - func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool - - /// Determines whether the `URLRequest` is authenticated with the `Credential`. - /// - /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `true` - /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then - /// read on. - /// - /// When an authentication server can invalidate credentials, it means that you may have a non-expired credential - /// that appears to be valid, but will be rejected by the authentication server when used. Generally when this - /// happens, a number of requests are all sent when the application is foregrounded, and all of them will be - /// rejected by the authentication server in the order they are received. The first failed request will trigger a - /// refresh internally, which will update the credential, and then retry all the queued requests with the new - /// credential. However, it is possible that some of the original requests will not return from the authentication - /// server until the refresh has completed. This is where this method comes in. - /// - /// When the authentication server rejects a credential, we need to check to make sure we haven't refreshed the - /// credential while the request was in flight. If it has already refreshed, then we don't need to trigger an - /// additional refresh. If it hasn't refreshed, then we need to refresh. - /// - /// Now that it is understood how the result of this method is used in the refresh lifecyle, let's walk through how - /// to implement it. You should return `true` in this method if the `URLRequest` is authenticated in a way that - /// matches the values in the `Credential`. In the case of OAuth2, this would mean that the Bearer token in the - /// `Authorization` header of the `URLRequest` matches the access token in the `Credential`. If it matches, then we - /// know the `Credential` was used to authenticate the `URLRequest` and should return `true`. If the Bearer token - /// did not match the access token, then you should return `false`. - /// - /// - Parameters: - /// - urlRequest: The `URLRequest`. - /// - credential: The `Credential`. - /// - /// - Returns: `true` if the `URLRequest` is authenticated with the `Credential`, `false` otherwise. - func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool -} - -// MARK: - - -/// Represents various authentication failures that occur when using the `AuthenticationInterceptor`. All errors are -/// still vended from Alamofire as `AFError` types. The `AuthenticationError` instances will be embedded within -/// `AFError` `.requestAdaptationFailed` or `.requestRetryFailed` cases. -public enum AuthenticationError: Error { - /// The credential was missing so the request could not be authenticated. - case missingCredential - /// The credential was refreshed too many times within the `RefreshWindow`. - case excessiveRefresh -} - -// MARK: - - -/// The `AuthenticationInterceptor` class manages the queuing and threading complexity of authenticating requests. -/// It relies on an `Authenticator` type to handle the actual `URLRequest` authentication and `Credential` refresh. -public class AuthenticationInterceptor: RequestInterceptor where AuthenticatorType: Authenticator { - // MARK: Typealiases - - /// Type of credential used to authenticate requests. - public typealias Credential = AuthenticatorType.Credential - - // MARK: Helper Types - - /// Type that defines a time window used to identify excessive refresh calls. When enabled, prior to executing a - /// refresh, the `AuthenticationInterceptor` compares the timestamp history of previous refresh calls against the - /// `RefreshWindow`. If more refreshes have occurred within the refresh window than allowed, the refresh is - /// cancelled and an `AuthorizationError.excessiveRefresh` error is thrown. - public struct RefreshWindow { - /// `TimeInterval` defining the duration of the time window before the current time in which the number of - /// refresh attempts is compared against `maximumAttempts`. For example, if `interval` is 30 seconds, then the - /// `RefreshWindow` represents the past 30 seconds. If more attempts occurred in the past 30 seconds than - /// `maximumAttempts`, an `.excessiveRefresh` error will be thrown. - public let interval: TimeInterval - - /// Total refresh attempts allowed within `interval` before throwing an `.excessiveRefresh` error. - public let maximumAttempts: Int - - /// Creates a `RefreshWindow` instance from the specified `interval` and `maximumAttempts`. - /// - /// - Parameters: - /// - interval: `TimeInterval` defining the duration of the time window before the current time. - /// - maximumAttempts: The maximum attempts allowed within the `TimeInterval`. - public init(interval: TimeInterval = 30.0, maximumAttempts: Int = 5) { - self.interval = interval - self.maximumAttempts = maximumAttempts - } - } - - private struct AdaptOperation { - let urlRequest: URLRequest - let session: Session - let completion: (Result) -> Void - } - - private enum AdaptResult { - case adapt(Credential) - case doNotAdapt(AuthenticationError) - case adaptDeferred - } - - private struct MutableState { - var credential: Credential? - - var isRefreshing = false - var refreshTimestamps: [TimeInterval] = [] - var refreshWindow: RefreshWindow? - - var adaptOperations: [AdaptOperation] = [] - var requestsToRetry: [(RetryResult) -> Void] = [] - } - - // MARK: Properties - - /// The `Credential` used to authenticate requests. - public var credential: Credential? { - get { mutableState.credential } - set { mutableState.credential = newValue } - } - - let authenticator: AuthenticatorType - let queue = DispatchQueue(label: "org.alamofire.authentication.inspector") - - @Protected - private var mutableState = MutableState() - - // MARK: Initialization - - /// Creates an `AuthenticationInterceptor` instance from the specified parameters. - /// - /// A `nil` `RefreshWindow` will result in the `AuthenticationInterceptor` not checking for excessive refresh calls. - /// It is recommended to always use a `RefreshWindow` to avoid endless refresh cycles. - /// - /// - Parameters: - /// - authenticator: The `Authenticator` type. - /// - credential: The `Credential` if it exists. `nil` by default. - /// - refreshWindow: The `RefreshWindow` used to identify excessive refresh calls. `RefreshWindow()` by default. - public init(authenticator: AuthenticatorType, - credential: Credential? = nil, - refreshWindow: RefreshWindow? = RefreshWindow()) { - self.authenticator = authenticator - mutableState.credential = credential - mutableState.refreshWindow = refreshWindow - } - - // MARK: Adapt - - public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - let adaptResult: AdaptResult = $mutableState.write { mutableState in - // Queue the adapt operation if a refresh is already in place. - guard !mutableState.isRefreshing else { - let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) - mutableState.adaptOperations.append(operation) - return .adaptDeferred - } - - // Throw missing credential error is the credential is missing. - guard let credential = mutableState.credential else { - let error = AuthenticationError.missingCredential - return .doNotAdapt(error) - } - - // Queue the adapt operation and trigger refresh operation if credential requires refresh. - guard !credential.requiresRefresh else { - let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) - mutableState.adaptOperations.append(operation) - refresh(credential, for: session, insideLock: &mutableState) - return .adaptDeferred - } - - return .adapt(credential) - } - - switch adaptResult { - case let .adapt(credential): - var authenticatedRequest = urlRequest - authenticator.apply(credential, to: &authenticatedRequest) - completion(.success(authenticatedRequest)) - - case let .doNotAdapt(adaptError): - completion(.failure(adaptError)) - - case .adaptDeferred: - // No-op: adapt operation captured during refresh. - break - } - } - - // MARK: Retry - - public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { - // Do not attempt retry if there was not an original request and response from the server. - guard let urlRequest = request.request, let response = request.response else { - completion(.doNotRetry) - return - } - - // Do not attempt retry unless the `Authenticator` verifies failure was due to authentication error (i.e. 401 status code). - guard authenticator.didRequest(urlRequest, with: response, failDueToAuthenticationError: error) else { - completion(.doNotRetry) - return - } - - // Do not attempt retry if there is no credential. - guard let credential = credential else { - let error = AuthenticationError.missingCredential - completion(.doNotRetryWithError(error)) - return - } - - // Retry the request if the `Authenticator` verifies it was authenticated with a previous credential. - guard authenticator.isRequest(urlRequest, authenticatedWith: credential) else { - completion(.retry) - return - } - - $mutableState.write { mutableState in - mutableState.requestsToRetry.append(completion) - - guard !mutableState.isRefreshing else { return } - - refresh(credential, for: session, insideLock: &mutableState) - } - } - - // MARK: Refresh - - private func refresh(_ credential: Credential, for session: Session, insideLock mutableState: inout MutableState) { - guard !isRefreshExcessive(insideLock: &mutableState) else { - let error = AuthenticationError.excessiveRefresh - handleRefreshFailure(error, insideLock: &mutableState) - return - } - - mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime) - mutableState.isRefreshing = true - - // Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously. - queue.async { - self.authenticator.refresh(credential, for: session) { result in - self.$mutableState.write { mutableState in - switch result { - case let .success(credential): - self.handleRefreshSuccess(credential, insideLock: &mutableState) - case let .failure(error): - self.handleRefreshFailure(error, insideLock: &mutableState) - } - } - } - } - } - - private func isRefreshExcessive(insideLock mutableState: inout MutableState) -> Bool { - guard let refreshWindow = mutableState.refreshWindow else { return false } - - let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval - - let refreshAttemptsWithinWindow = mutableState.refreshTimestamps.reduce(into: 0) { attempts, refreshTimestamp in - guard refreshWindowMin <= refreshTimestamp else { return } - attempts += 1 - } - - let isRefreshExcessive = refreshAttemptsWithinWindow >= refreshWindow.maximumAttempts - - return isRefreshExcessive - } - - private func handleRefreshSuccess(_ credential: Credential, insideLock mutableState: inout MutableState) { - mutableState.credential = credential - - let adaptOperations = mutableState.adaptOperations - let requestsToRetry = mutableState.requestsToRetry - - mutableState.adaptOperations.removeAll() - mutableState.requestsToRetry.removeAll() - - mutableState.isRefreshing = false - - // Dispatch to queue to hop out of the mutable state lock - queue.async { - adaptOperations.forEach { self.adapt($0.urlRequest, for: $0.session, completion: $0.completion) } - requestsToRetry.forEach { $0(.retry) } - } - } - - private func handleRefreshFailure(_ error: Error, insideLock mutableState: inout MutableState) { - let adaptOperations = mutableState.adaptOperations - let requestsToRetry = mutableState.requestsToRetry - - mutableState.adaptOperations.removeAll() - mutableState.requestsToRetry.removeAll() - - mutableState.isRefreshing = false - - // Dispatch to queue to hop out of the mutable state lock - queue.async { - adaptOperations.forEach { $0.completion(.failure(error)) } - requestsToRetry.forEach { $0(.doNotRetryWithError(error)) } - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift deleted file mode 100644 index b6e0d4b2..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/CachedResponseHandler.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// CachedResponseHandler.swift -// -// Copyright (c) 2019 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 handles whether the data task should store the HTTP response in the cache. -public protocol CachedResponseHandler { - /// Determines whether the HTTP response should be stored in the cache. - /// - /// The `completion` closure should be passed one of three possible options: - /// - /// 1. The cached response provided by the server (this is the most common use case). - /// 2. A modified version of the cached response (you may want to modify it in some way before caching). - /// 3. A `nil` value to prevent the cached response from being stored in the cache. - /// - /// - Parameters: - /// - task: The data task whose request resulted in the cached response. - /// - response: The cached response to potentially store in the cache. - /// - completion: The closure to execute containing cached response, a modified response, or `nil`. - func dataTask(_ task: URLSessionDataTask, - willCacheResponse response: CachedURLResponse, - completion: @escaping (CachedURLResponse?) -> Void) -} - -// MARK: - - -/// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached -/// response. -public struct ResponseCacher { - /// Defines the behavior of the `ResponseCacher` type. - public enum Behavior { - /// Stores the cached response in the cache. - case cache - /// Prevents the cached response from being stored in the cache. - case doNotCache - /// Modifies the cached response before storing it in the cache. - case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) - } - - /// Returns a `ResponseCacher` with a follow `Behavior`. - public static let cache = ResponseCacher(behavior: .cache) - /// Returns a `ResponseCacher` with a do not follow `Behavior`. - public static let doNotCache = ResponseCacher(behavior: .doNotCache) - - /// The `Behavior` of the `ResponseCacher`. - public let behavior: Behavior - - /// Creates a `ResponseCacher` instance from the `Behavior`. - /// - /// - Parameter behavior: The `Behavior`. - public init(behavior: Behavior) { - self.behavior = behavior - } -} - -extension ResponseCacher: CachedResponseHandler { - public func dataTask(_ task: URLSessionDataTask, - willCacheResponse response: CachedURLResponse, - completion: @escaping (CachedURLResponse?) -> Void) { - switch behavior { - case .cache: - completion(response) - case .doNotCache: - completion(nil) - case let .modify(closure): - let response = closure(task, response) - completion(response) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift deleted file mode 100644 index a1382494..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Combine.swift +++ /dev/null @@ -1,622 +0,0 @@ -// -// Combine.swift -// -// Copyright (c) 2020 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 canImport(Combine) - -import Combine -import Dispatch -import Foundation - -// MARK: - DataRequest / UploadRequest - -/// A Combine `Publisher` that publishes the `DataResponse` of the provided `DataRequest`. -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) -public struct DataResponsePublisher: Publisher { - public typealias Output = DataResponse - public typealias Failure = Never - - private typealias Handler = (@escaping (_ response: DataResponse) -> Void) -> DataRequest - - private let request: DataRequest - private let responseHandler: Handler - - /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. - /// - /// - Parameters: - /// - request: `DataRequest` for which to publish the response. - /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. - /// - serializer: `ResponseSerializer` used to produce the published `DataResponse`. - public init(_ request: DataRequest, queue: DispatchQueue, serializer: Serializer) - where Value == Serializer.SerializedObject { - self.request = request - responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } - } - - /// Creates an instance which will serialize responses using the provided `DataResponseSerializerProtocol`. - /// - /// - Parameters: - /// - request: `DataRequest` for which to publish the response. - /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. - /// - serializer: `DataResponseSerializerProtocol` used to produce the published `DataResponse`. - public init(_ request: DataRequest, - queue: DispatchQueue, - serializer: Serializer) - where Value == Serializer.SerializedObject { - self.request = request - responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } - } - - /// Publishes only the `Result` of the `DataResponse` value. - /// - /// - Returns: The `AnyPublisher` publishing the `Result` value. - public func result() -> AnyPublisher, Never> { - map { $0.result }.eraseToAnyPublisher() - } - - /// Publishes the `Result` of the `DataResponse` as a single `Value` or fail with the `AFError` instance. - /// - /// - Returns: The `AnyPublisher` publishing the stream. - public func value() -> AnyPublisher { - setFailureType(to: AFError.self).flatMap { $0.result.publisher }.eraseToAnyPublisher() - } - - public func receive(subscriber: S) where S: Subscriber, DataResponsePublisher.Failure == S.Failure, DataResponsePublisher.Output == S.Input { - subscriber.receive(subscription: Inner(request: request, - responseHandler: responseHandler, - downstream: subscriber)) - } - - private final class Inner: Subscription, Cancellable - where Downstream.Input == Output { - typealias Failure = Downstream.Failure - - @Protected - private var downstream: Downstream? - private let request: DataRequest - private let responseHandler: Handler - - init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) { - self.request = request - self.responseHandler = responseHandler - self.downstream = downstream - } - - func request(_ demand: Subscribers.Demand) { - assert(demand > 0) - - guard let downstream = downstream else { return } - - self.downstream = nil - responseHandler { response in - _ = downstream.receive(response) - downstream.receive(completion: .finished) - }.resume() - } - - func cancel() { - request.cancel() - downstream = nil - } - } -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) -extension DataResponsePublisher where Value == Data? { - /// Creates an instance which publishes a `DataResponse` value without serialization. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public init(_ request: DataRequest, queue: DispatchQueue) { - self.request = request - responseHandler = { request.response(queue: queue, completionHandler: $0) } - } -} - -extension DataRequest { - /// Creates a `DataResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. - /// - /// - Parameters: - /// - serializer: `ResponseSerializer` used to serialize response `Data`. - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - /// - Returns: The `DataResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DataResponsePublisher - where Serializer.SerializedObject == T { - DataResponsePublisher(self, queue: queue, serializer: serializer) - } - - /// Creates a `DataResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the - /// response. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` - /// by default. - /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by - /// default. - /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of - /// status code. `[.head]` by default. - /// - Returns: The `DataResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishData(queue: DispatchQueue = .main, - preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { - publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - on: queue) - } - - /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the - /// response. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` - /// by default. - /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding - /// will be determined by the server response, falling back to the default HTTP character - /// set, `ISO-8859-1`. - /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by - /// default. - /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of - /// status code. `[.head]` by default. - /// - /// - Returns: The `DataResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishString(queue: DispatchQueue = .main, - preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, - encoding: String.Encoding? = nil, - emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { - publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, - encoding: encoding, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - on: queue) - } - - /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the - /// response. - /// - /// - Parameters: - /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default. - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` - /// by default. - /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. - /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by - /// default. - /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of - /// status code. `[.head]` by default. - /// - /// - Returns: The `DataResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishDecodable(type: T.Type = T.self, - queue: DispatchQueue = .main, - preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, - decoder: DataDecoder = JSONDecoder(), - emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, - emptyResponseMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher { - publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, - decoder: decoder, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyResponseMethods), - on: queue) - } - - /// Creates a `DataResponsePublisher` for this instance which does not serialize the response before publishing. - /// - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - /// - Returns: The `DataResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishUnserialized(queue: DispatchQueue = .main) -> DataResponsePublisher { - DataResponsePublisher(self, queue: queue) - } -} - -// A Combine `Publisher` that publishes a sequence of `Stream` values received by the provided `DataStreamRequest`. -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) -public struct DataStreamPublisher: Publisher { - public typealias Output = DataStreamRequest.Stream - public typealias Failure = Never - - private typealias Handler = (@escaping DataStreamRequest.Handler) -> DataStreamRequest - - private let request: DataStreamRequest - private let streamHandler: Handler - - /// Creates an instance which will serialize responses using the provided `DataStreamSerializer`. - /// - /// - Parameters: - /// - request: `DataStreamRequest` for which to publish the response. - /// - queue: `DispatchQueue` on which the `Stream` values will be published. `.main` by - /// default. - /// - serializer: `DataStreamSerializer` used to produce the published `Stream` values. - public init(_ request: DataStreamRequest, queue: DispatchQueue, serializer: Serializer) - where Value == Serializer.SerializedObject { - self.request = request - streamHandler = { request.responseStream(using: serializer, on: queue, stream: $0) } - } - - /// Publishes only the `Result` of the `DataStreamRequest.Stream`'s `Event`s. - /// - /// - Returns: The `AnyPublisher` publishing the `Result` value. - public func result() -> AnyPublisher, Never> { - compactMap { stream in - switch stream.event { - case let .stream(result): - return result - // If the stream has completed with an error, send the error value downstream as a `.failure`. - case let .complete(completion): - return completion.error.map(Result.failure) - } - } - .eraseToAnyPublisher() - } - - /// Publishes the streamed values of the `DataStreamRequest.Stream` as a sequence of `Value` or fail with the - /// `AFError` instance. - /// - /// - Returns: The `AnyPublisher` publishing the stream. - public func value() -> AnyPublisher { - result().setFailureType(to: AFError.self).flatMap { $0.publisher }.eraseToAnyPublisher() - } - - public func receive(subscriber: S) where S: Subscriber, DataStreamPublisher.Failure == S.Failure, DataStreamPublisher.Output == S.Input { - subscriber.receive(subscription: Inner(request: request, - streamHandler: streamHandler, - downstream: subscriber)) - } - - private final class Inner: Subscription, Cancellable - where Downstream.Input == Output { - typealias Failure = Downstream.Failure - - @Protected - private var downstream: Downstream? - private let request: DataStreamRequest - private let streamHandler: Handler - - init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) { - self.request = request - self.streamHandler = streamHandler - self.downstream = downstream - } - - func request(_ demand: Subscribers.Demand) { - assert(demand > 0) - - guard let downstream = downstream else { return } - - self.downstream = nil - streamHandler { stream in - _ = downstream.receive(stream) - if case .complete = stream.event { - downstream.receive(completion: .finished) - } - }.resume() - } - - func cancel() { - request.cancel() - downstream = nil - } - } -} - -extension DataStreamRequest { - /// Creates a `DataStreamPublisher` for this instance using the given `DataStreamSerializer` and `DispatchQueue`. - /// - /// - Parameters: - /// - serializer: `DataStreamSerializer` used to serialize the streamed `Data`. - /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. - /// - Returns: The `DataStreamPublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishStream(using serializer: Serializer, - on queue: DispatchQueue = .main) -> DataStreamPublisher { - DataStreamPublisher(self, queue: queue, serializer: serializer) - } - - /// Creates a `DataStreamPublisher` for this instance which uses a `PassthroughStreamSerializer` to stream `Data` - /// unserialized. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. - /// - Returns: The `DataStreamPublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishData(queue: DispatchQueue = .main) -> DataStreamPublisher { - publishStream(using: PassthroughStreamSerializer(), on: queue) - } - - /// Creates a `DataStreamPublisher` for this instance which uses a `StringStreamSerializer` to serialize stream - /// `Data` values into `String` values. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. - /// - Returns: The `DataStreamPublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishString(queue: DispatchQueue = .main) -> DataStreamPublisher { - publishStream(using: StringStreamSerializer(), on: queue) - } - - /// Creates a `DataStreamPublisher` for this instance which uses a `DecodableStreamSerializer` with the provided - /// parameters to serialize stream `Data` values into the provided type. - /// - /// - Parameters: - /// - type: `Decodable` type to which to decode stream `Data`. Inferred from the context by default. - /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. - /// - decoder: `DataDecoder` instance used to decode stream `Data`. `JSONDecoder()` by default. - /// - preprocessor: `DataPreprocessor` which filters incoming stream `Data` before serialization. - /// `PassthroughPreprocessor()` by default. - /// - Returns: The `DataStreamPublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishDecodable(type: T.Type = T.self, - queue: DispatchQueue = .main, - decoder: DataDecoder = JSONDecoder(), - preprocessor: DataPreprocessor = PassthroughPreprocessor()) -> DataStreamPublisher { - publishStream(using: DecodableStreamSerializer(decoder: decoder, - dataPreprocessor: preprocessor), - on: queue) - } -} - -/// A Combine `Publisher` that publishes the `DownloadResponse` of the provided `DownloadRequest`. -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) -public struct DownloadResponsePublisher: Publisher { - public typealias Output = DownloadResponse - public typealias Failure = Never - - private typealias Handler = (@escaping (_ response: DownloadResponse) -> Void) -> DownloadRequest - - private let request: DownloadRequest - private let responseHandler: Handler - - /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. - /// - /// - Parameters: - /// - request: `DownloadRequest` for which to publish the response. - /// - queue: `DispatchQueue` on which the `DownloadResponse` value will be published. `.main` by default. - /// - serializer: `ResponseSerializer` used to produce the published `DownloadResponse`. - public init(_ request: DownloadRequest, queue: DispatchQueue, serializer: Serializer) - where Value == Serializer.SerializedObject { - self.request = request - responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } - } - - /// Creates an instance which will serialize responses using the provided `DownloadResponseSerializerProtocol` value. - /// - /// - Parameters: - /// - request: `DownloadRequest` for which to publish the response. - /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. - /// - serializer: `DownloadResponseSerializerProtocol` used to produce the published `DownloadResponse`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public init(_ request: DownloadRequest, - queue: DispatchQueue, - serializer: Serializer) - where Value == Serializer.SerializedObject { - self.request = request - responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } - } - - /// Publishes only the `Result` of the `DownloadResponse` value. - /// - /// - Returns: The `AnyPublisher` publishing the `Result` value. - public func result() -> AnyPublisher, Never> { - map { $0.result }.eraseToAnyPublisher() - } - - /// Publishes the `Result` of the `DownloadResponse` as a single `Value` or fail with the `AFError` instance. - /// - /// - Returns: The `AnyPublisher` publishing the stream. - public func value() -> AnyPublisher { - setFailureType(to: AFError.self).flatMap { $0.result.publisher }.eraseToAnyPublisher() - } - - public func receive(subscriber: S) where S: Subscriber, DownloadResponsePublisher.Failure == S.Failure, DownloadResponsePublisher.Output == S.Input { - subscriber.receive(subscription: Inner(request: request, - responseHandler: responseHandler, - downstream: subscriber)) - } - - private final class Inner: Subscription, Cancellable - where Downstream.Input == Output { - typealias Failure = Downstream.Failure - - @Protected - private var downstream: Downstream? - private let request: DownloadRequest - private let responseHandler: Handler - - init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) { - self.request = request - self.responseHandler = responseHandler - self.downstream = downstream - } - - func request(_ demand: Subscribers.Demand) { - assert(demand > 0) - - guard let downstream = downstream else { return } - - self.downstream = nil - responseHandler { response in - _ = downstream.receive(response) - downstream.receive(completion: .finished) - }.resume() - } - - func cancel() { - request.cancel() - downstream = nil - } - } -} - -extension DownloadRequest { - /// Creates a `DownloadResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. - /// - /// - Parameters: - /// - serializer: `ResponseSerializer` used to serialize the response `Data` from disk. - /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher - where Serializer.SerializedObject == T { - DownloadResponsePublisher(self, queue: queue, serializer: serializer) - } - - /// Creates a `DownloadResponsePublisher` for this instance using the given `DownloadResponseSerializerProtocol` and - /// `DispatchQueue`. - /// - /// - Parameters: - /// - serializer: `DownloadResponseSerializer` used to serialize the response `Data` from disk. - /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishResponse(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher - where Serializer.SerializedObject == T { - DownloadResponsePublisher(self, queue: queue, serializer: serializer) - } - - /// Creates a `DownloadResponsePublisher` for this instance and uses a `URLResponseSerializer` to serialize the - /// response. - /// - /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishURL(queue: DispatchQueue = .main) -> DownloadResponsePublisher { - publishResponse(using: URLResponseSerializer(), on: queue) - } - - /// Creates a `DownloadResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the - /// response. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. - /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` - /// by default. - /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by - /// default. - /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of - /// status code. `[.head]` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishData(queue: DispatchQueue = .main, - preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { - publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - on: queue) - } - - /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the - /// response. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` - /// by default. - /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding - /// will be determined by the server response, falling back to the default HTTP character - /// set, `ISO-8859-1`. - /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by - /// default. - /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of - /// status code. `[.head]` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishString(queue: DispatchQueue = .main, - preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, - encoding: String.Encoding? = nil, - emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { - publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, - encoding: encoding, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - on: queue) - } - - /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the - /// response. - /// - /// - Parameters: - /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default. - /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. - /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` - /// by default. - /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. - /// - emptyResponseCodes: `Set` of HTTP status codes for which empty responses are allowed. `[204, 205]` by - /// default. - /// - emptyRequestMethods: `Set` of `HTTPMethod`s for which empty responses are allowed, regardless of - /// status code. `[.head]` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishDecodable(type: T.Type = T.self, - queue: DispatchQueue = .main, - preprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, - decoder: DataDecoder = JSONDecoder(), - emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, - emptyResponseMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher { - publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, - decoder: decoder, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyResponseMethods), - on: queue) - } -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) -extension DownloadResponsePublisher where Value == URL? { - /// Creates an instance which publishes a `DownloadResponse` value without serialization. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public init(_ request: DownloadRequest, queue: DispatchQueue) { - self.request = request - responseHandler = { request.response(queue: queue, completionHandler: $0) } - } -} - -extension DownloadRequest { - /// Creates a `DownloadResponsePublisher` for this instance which does not serialize the response before publishing. - /// - /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. - /// - /// - Returns: The `DownloadResponsePublisher`. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - public func publishUnserialized(on queue: DispatchQueue = .main) -> DownloadResponsePublisher { - DownloadResponsePublisher(self, queue: queue) - } -} - -#endif diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift deleted file mode 100644 index 10cd273e..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// DispatchQueue+Alamofire.swift -// -// Copyright (c) 2014-2018 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 { - /// Execute the provided closure after a `TimeInterval`. - /// - /// - Parameters: - /// - delay: `TimeInterval` to delay execution. - /// - closure: Closure to execute. - func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { - asyncAfter(deadline: .now() + delay, execute: closure) - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift deleted file mode 100644 index 3b096712..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/EventMonitor.swift +++ /dev/null @@ -1,892 +0,0 @@ -// -// EventMonitor.swift -// -// Copyright (c) 2014-2018 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 - -/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various -/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses. -public protocol EventMonitor { - /// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default. - var queue: DispatchQueue { get } - - // MARK: - URLSession Events - - // MARK: URLSessionDelegate Events - - /// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method. - func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) - - // MARK: URLSessionTaskDelegate Events - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method. - func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method. - func urlSession(_ session: URLSession, - task: URLSessionTask, - didSendBodyData bytesSent: Int64, - totalBytesSent: Int64, - totalBytesExpectedToSend: Int64) - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method. - func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method. - func urlSession(_ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest) - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method. - func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method. - func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) - - /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method. - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) - - // MARK: URLSessionDataDelegate Events - - /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method. - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) - - /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method. - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) - - // MARK: URLSessionDownloadDelegate Events - - /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method. - func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didResumeAtOffset fileOffset: Int64, - expectedTotalBytes: Int64) - - /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method. - func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64) - - /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method. - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) - - // MARK: - Request Events - - /// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the - /// `URLRequest` will be adapted before being issued. - func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) - - /// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails. - func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) - - /// Event called when a `RequestAdapter` adapts the `Request`'s initial `URLRequest`. - func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) - - /// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`. - func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) - - /// Event called when a final `URLRequest` is created for a `Request`. - func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) - - /// Event called when a `URLSessionTask` subclass instance is created for a `Request`. - func request(_ request: Request, didCreateTask task: URLSessionTask) - - /// Event called when a `Request` receives a `URLSessionTaskMetrics` value. - func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) - - /// Event called when a `Request` fails due to an error created by Alamofire. e.g. When certificate pinning fails. - func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) - - /// Event called when a `Request`'s task completes, possibly with an error. A `Request` may receive this event - /// multiple times if it is retried. - func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) - - /// Event called when a `Request` is about to be retried. - func requestIsRetrying(_ request: Request) - - /// Event called when a `Request` finishes and response serializers are being called. - func requestDidFinish(_ request: Request) - - /// Event called when a `Request` receives a `resume` call. - func requestDidResume(_ request: Request) - - /// Event called when a `Request`'s associated `URLSessionTask` is resumed. - func request(_ request: Request, didResumeTask task: URLSessionTask) - - /// Event called when a `Request` receives a `suspend` call. - func requestDidSuspend(_ request: Request) - - /// Event called when a `Request`'s associated `URLSessionTask` is suspended. - func request(_ request: Request, didSuspendTask task: URLSessionTask) - - /// Event called when a `Request` receives a `cancel` call. - func requestDidCancel(_ request: Request) - - /// Event called when a `Request`'s associated `URLSessionTask` is cancelled. - func request(_ request: Request, didCancelTask task: URLSessionTask) - - // MARK: DataRequest Events - - /// Event called when a `DataRequest` calls a `Validation`. - func request(_ request: DataRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - data: Data?, - withResult result: Request.ValidationResult) - - /// Event called when a `DataRequest` creates a `DataResponse` value without calling a `ResponseSerializer`. - func request(_ request: DataRequest, didParseResponse response: DataResponse) - - /// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse`. - func request(_ request: DataRequest, didParseResponse response: DataResponse) - - // MARK: DataStreamRequest Events - - /// Event called when a `DataStreamRequest` calls a `Validation` closure. - /// - /// - Parameters: - /// - request: `DataStreamRequest` which is calling the `Validation`. - /// - urlRequest: `URLRequest` of the request being validated. - /// - response: `HTTPURLResponse` of the request being validated. - /// - result: Produced `ValidationResult`. - func request(_ request: DataStreamRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - withResult result: Request.ValidationResult) - - /// Event called when a `DataStreamSerializer` produces a value from streamed `Data`. - /// - /// - Parameters: - /// - request: `DataStreamRequest` for which the value was serialized. - /// - result: `Result` of the serialization attempt. - func request(_ request: DataStreamRequest, didParseStream result: Result) - - // MARK: UploadRequest Events - - /// Event called when an `UploadRequest` creates its `Uploadable` value, indicating the type of upload it represents. - func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) - - /// Event called when an `UploadRequest` failed to create its `Uploadable` value due to an error. - func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) - - /// Event called when an `UploadRequest` provides the `InputStream` from its `Uploadable` value. This only occurs if - /// the `InputStream` does not wrap a `Data` value or file `URL`. - func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) - - // MARK: DownloadRequest Events - - /// Event called when a `DownloadRequest`'s `URLSessionDownloadTask` finishes and the temporary file has been moved. - func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) - - /// Event called when a `DownloadRequest`'s `Destination` closure is called and creates the destination URL the - /// downloaded file will be moved to. - func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) - - /// Event called when a `DownloadRequest` calls a `Validation`. - func request(_ request: DownloadRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - fileURL: URL?, - withResult result: Request.ValidationResult) - - /// Event called when a `DownloadRequest` creates a `DownloadResponse` without calling a `ResponseSerializer`. - func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) - - /// Event called when a `DownloadRequest` calls a `DownloadResponseSerializer` and creates a generic `DownloadResponse` - func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) -} - -extension EventMonitor { - /// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default. - public var queue: DispatchQueue { .main } - - // MARK: Default Implementations - - public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {} - public func urlSession(_ session: URLSession, - task: URLSessionTask, - didReceive challenge: URLAuthenticationChallenge) {} - public func urlSession(_ session: URLSession, - task: URLSessionTask, - didSendBodyData bytesSent: Int64, - totalBytesSent: Int64, - totalBytesExpectedToSend: Int64) {} - public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {} - public func urlSession(_ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest) {} - public func urlSession(_ session: URLSession, - task: URLSessionTask, - didFinishCollecting metrics: URLSessionTaskMetrics) {} - public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {} - public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {} - public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {} - public func urlSession(_ session: URLSession, - dataTask: URLSessionDataTask, - willCacheResponse proposedResponse: CachedURLResponse) {} - public func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didResumeAtOffset fileOffset: Int64, - expectedTotalBytes: Int64) {} - public func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64) {} - public func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didFinishDownloadingTo location: URL) {} - public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {} - public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {} - public func request(_ request: Request, - didAdaptInitialRequest initialRequest: URLRequest, - to adaptedRequest: URLRequest) {} - public func request(_ request: Request, - didFailToAdaptURLRequest initialRequest: URLRequest, - withError error: AFError) {} - public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {} - public func request(_ request: Request, didCreateTask task: URLSessionTask) {} - public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {} - public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {} - public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {} - public func requestIsRetrying(_ request: Request) {} - public func requestDidFinish(_ request: Request) {} - public func requestDidResume(_ request: Request) {} - public func request(_ request: Request, didResumeTask task: URLSessionTask) {} - public func requestDidSuspend(_ request: Request) {} - public func request(_ request: Request, didSuspendTask task: URLSessionTask) {} - public func requestDidCancel(_ request: Request) {} - public func request(_ request: Request, didCancelTask task: URLSessionTask) {} - public func request(_ request: DataRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - data: Data?, - withResult result: Request.ValidationResult) {} - public func request(_ request: DataRequest, didParseResponse response: DataResponse) {} - public func request(_ request: DataRequest, didParseResponse response: DataResponse) {} - public func request(_ request: DataStreamRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - withResult result: Request.ValidationResult) {} - public func request(_ request: DataStreamRequest, didParseStream result: Result) {} - public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {} - public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {} - public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {} - public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) {} - public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {} - public func request(_ request: DownloadRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - fileURL: URL?, - withResult result: Request.ValidationResult) {} - public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) {} - public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) {} -} - -/// An `EventMonitor` which can contain multiple `EventMonitor`s and calls their methods on their queues. -public final class CompositeEventMonitor: EventMonitor { - public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor", qos: .utility) - - let monitors: [EventMonitor] - - init(monitors: [EventMonitor]) { - self.monitors = monitors - } - - func performEvent(_ event: @escaping (EventMonitor) -> Void) { - queue.async { - for monitor in self.monitors { - monitor.queue.async { event(monitor) } - } - } - } - - public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { - performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) } - } - - public func urlSession(_ session: URLSession, - task: URLSessionTask, - didReceive challenge: URLAuthenticationChallenge) { - performEvent { $0.urlSession(session, task: task, didReceive: challenge) } - } - - public func urlSession(_ session: URLSession, - task: URLSessionTask, - didSendBodyData bytesSent: Int64, - totalBytesSent: Int64, - totalBytesExpectedToSend: Int64) { - performEvent { - $0.urlSession(session, - task: task, - didSendBodyData: bytesSent, - totalBytesSent: totalBytesSent, - totalBytesExpectedToSend: totalBytesExpectedToSend) - } - } - - public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { - performEvent { - $0.urlSession(session, taskNeedsNewBodyStream: task) - } - } - - public func urlSession(_ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest) { - performEvent { - $0.urlSession(session, - task: task, - willPerformHTTPRedirection: response, - newRequest: request) - } - } - - public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { - performEvent { $0.urlSession(session, task: task, didFinishCollecting: metrics) } - } - - public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - performEvent { $0.urlSession(session, task: task, didCompleteWithError: error) } - } - - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { - performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) } - } - - public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) } - } - - public func urlSession(_ session: URLSession, - dataTask: URLSessionDataTask, - willCacheResponse proposedResponse: CachedURLResponse) { - performEvent { $0.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) } - } - - public func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didResumeAtOffset fileOffset: Int64, - expectedTotalBytes: Int64) { - performEvent { - $0.urlSession(session, - downloadTask: downloadTask, - didResumeAtOffset: fileOffset, - expectedTotalBytes: expectedTotalBytes) - } - } - - public func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64) { - performEvent { - $0.urlSession(session, - downloadTask: downloadTask, - didWriteData: bytesWritten, - totalBytesWritten: totalBytesWritten, - totalBytesExpectedToWrite: totalBytesExpectedToWrite) - } - } - - public func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didFinishDownloadingTo location: URL) { - performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) } - } - - public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { - performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) } - } - - public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { - performEvent { $0.request(request, didFailToCreateURLRequestWithError: error) } - } - - public func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { - performEvent { $0.request(request, didAdaptInitialRequest: initialRequest, to: adaptedRequest) } - } - - public func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { - performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) } - } - - public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { - performEvent { $0.request(request, didCreateURLRequest: urlRequest) } - } - - public func request(_ request: Request, didCreateTask task: URLSessionTask) { - performEvent { $0.request(request, didCreateTask: task) } - } - - public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { - performEvent { $0.request(request, didGatherMetrics: metrics) } - } - - public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { - performEvent { $0.request(request, didFailTask: task, earlyWithError: error) } - } - - public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { - performEvent { $0.request(request, didCompleteTask: task, with: error) } - } - - public func requestIsRetrying(_ request: Request) { - performEvent { $0.requestIsRetrying(request) } - } - - public func requestDidFinish(_ request: Request) { - performEvent { $0.requestDidFinish(request) } - } - - public func requestDidResume(_ request: Request) { - performEvent { $0.requestDidResume(request) } - } - - public func request(_ request: Request, didResumeTask task: URLSessionTask) { - performEvent { $0.request(request, didResumeTask: task) } - } - - public func requestDidSuspend(_ request: Request) { - performEvent { $0.requestDidSuspend(request) } - } - - public func request(_ request: Request, didSuspendTask task: URLSessionTask) { - performEvent { $0.request(request, didSuspendTask: task) } - } - - public func requestDidCancel(_ request: Request) { - performEvent { $0.requestDidCancel(request) } - } - - public func request(_ request: Request, didCancelTask task: URLSessionTask) { - performEvent { $0.request(request, didCancelTask: task) } - } - - public func request(_ request: DataRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - data: Data?, - withResult result: Request.ValidationResult) { - performEvent { $0.request(request, - didValidateRequest: urlRequest, - response: response, - data: data, - withResult: result) - } - } - - public func request(_ request: DataRequest, didParseResponse response: DataResponse) { - performEvent { $0.request(request, didParseResponse: response) } - } - - public func request(_ request: DataRequest, didParseResponse response: DataResponse) { - performEvent { $0.request(request, didParseResponse: response) } - } - - public func request(_ request: DataStreamRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - withResult result: Request.ValidationResult) { - performEvent { $0.request(request, - didValidateRequest: urlRequest, - response: response, - withResult: result) - } - } - - public func request(_ request: DataStreamRequest, didParseStream result: Result) { - performEvent { $0.request(request, didParseStream: result) } - } - - public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { - performEvent { $0.request(request, didCreateUploadable: uploadable) } - } - - public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { - performEvent { $0.request(request, didFailToCreateUploadableWithError: error) } - } - - public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { - performEvent { $0.request(request, didProvideInputStream: stream) } - } - - public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) { - performEvent { $0.request(request, didFinishDownloadingUsing: task, with: result) } - } - - public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { - performEvent { $0.request(request, didCreateDestinationURL: url) } - } - - public func request(_ request: DownloadRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - fileURL: URL?, - withResult result: Request.ValidationResult) { - performEvent { $0.request(request, - didValidateRequest: urlRequest, - response: response, - fileURL: fileURL, - withResult: result) } - } - - public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { - performEvent { $0.request(request, didParseResponse: response) } - } - - public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { - performEvent { $0.request(request, didParseResponse: response) } - } -} - -/// `EventMonitor` that allows optional closures to be set to receive events. -open class ClosureEventMonitor: EventMonitor { - /// Closure called on the `urlSession(_:didBecomeInvalidWithError:)` event. - open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? - - /// Closure called on the `urlSession(_:task:didReceive:completionHandler:)`. - open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> Void)? - - /// Closure that receives `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` event. - open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? - - /// Closure called on the `urlSession(_:task:needNewBodyStream:)` event. - open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> Void)? - - /// Closure called on the `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` event. - open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> Void)? - - /// Closure called on the `urlSession(_:task:didFinishCollecting:)` event. - open var taskDidFinishCollectingMetrics: ((URLSession, URLSessionTask, URLSessionTaskMetrics) -> Void)? - - /// Closure called on the `urlSession(_:task:didCompleteWithError:)` event. - open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)? - - /// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event. - open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)? - - /// Closure that receives the `urlSession(_:dataTask:didReceive:)` event. - open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? - - /// Closure called on the `urlSession(_:dataTask:willCacheResponse:completionHandler:)` event. - open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> Void)? - - /// Closure called on the `urlSession(_:downloadTask:didFinishDownloadingTo:)` event. - open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)? - - /// Closure called on the `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` - /// event. - open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? - - /// Closure called on the `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` event. - open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? - - // MARK: - Request Events - - /// Closure called on the `request(_:didCreateInitialURLRequest:)` event. - open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)? - - /// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event. - open var requestDidFailToCreateURLRequestWithError: ((Request, AFError) -> Void)? - - /// Closure called on the `request(_:didAdaptInitialRequest:to:)` event. - open var requestDidAdaptInitialRequestToAdaptedRequest: ((Request, URLRequest, URLRequest) -> Void)? - - /// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event. - open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, AFError) -> Void)? - - /// Closure called on the `request(_:didCreateURLRequest:)` event. - open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)? - - /// Closure called on the `request(_:didCreateTask:)` event. - open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)? - - /// Closure called on the `request(_:didGatherMetrics:)` event. - open var requestDidGatherMetrics: ((Request, URLSessionTaskMetrics) -> Void)? - - /// Closure called on the `request(_:didFailTask:earlyWithError:)` event. - open var requestDidFailTaskEarlyWithError: ((Request, URLSessionTask, AFError) -> Void)? - - /// Closure called on the `request(_:didCompleteTask:with:)` event. - open var requestDidCompleteTaskWithError: ((Request, URLSessionTask, AFError?) -> Void)? - - /// Closure called on the `requestIsRetrying(_:)` event. - open var requestIsRetrying: ((Request) -> Void)? - - /// Closure called on the `requestDidFinish(_:)` event. - open var requestDidFinish: ((Request) -> Void)? - - /// Closure called on the `requestDidResume(_:)` event. - open var requestDidResume: ((Request) -> Void)? - - /// Closure called on the `request(_:didResumeTask:)` event. - open var requestDidResumeTask: ((Request, URLSessionTask) -> Void)? - - /// Closure called on the `requestDidSuspend(_:)` event. - open var requestDidSuspend: ((Request) -> Void)? - - /// Closure called on the `request(_:didSuspendTask:)` event. - open var requestDidSuspendTask: ((Request, URLSessionTask) -> Void)? - - /// Closure called on the `requestDidCancel(_:)` event. - open var requestDidCancel: ((Request) -> Void)? - - /// Closure called on the `request(_:didCancelTask:)` event. - open var requestDidCancelTask: ((Request, URLSessionTask) -> Void)? - - /// Closure called on the `request(_:didValidateRequest:response:data:withResult:)` event. - open var requestDidValidateRequestResponseDataWithResult: ((DataRequest, URLRequest?, HTTPURLResponse, Data?, Request.ValidationResult) -> Void)? - - /// Closure called on the `request(_:didParseResponse:)` event. - open var requestDidParseResponse: ((DataRequest, DataResponse) -> Void)? - - /// Closure called on the `request(_:didValidateRequest:response:withResult:)` event. - open var requestDidValidateRequestResponseWithResult: ((DataStreamRequest, URLRequest?, HTTPURLResponse, Request.ValidationResult) -> Void)? - - /// Closure called on the `request(_:didCreateUploadable:)` event. - open var requestDidCreateUploadable: ((UploadRequest, UploadRequest.Uploadable) -> Void)? - - /// Closure called on the `request(_:didFailToCreateUploadableWithError:)` event. - open var requestDidFailToCreateUploadableWithError: ((UploadRequest, AFError) -> Void)? - - /// Closure called on the `request(_:didProvideInputStream:)` event. - open var requestDidProvideInputStream: ((UploadRequest, InputStream) -> Void)? - - /// Closure called on the `request(_:didFinishDownloadingUsing:with:)` event. - open var requestDidFinishDownloadingUsingTaskWithResult: ((DownloadRequest, URLSessionTask, Result) -> Void)? - - /// Closure called on the `request(_:didCreateDestinationURL:)` event. - open var requestDidCreateDestinationURL: ((DownloadRequest, URL) -> Void)? - - /// Closure called on the `request(_:didValidateRequest:response:temporaryURL:destinationURL:withResult:)` event. - open var requestDidValidateRequestResponseFileURLWithResult: ((DownloadRequest, URLRequest?, HTTPURLResponse, URL?, Request.ValidationResult) -> Void)? - - /// Closure called on the `request(_:didParseResponse:)` event. - open var requestDidParseDownloadResponse: ((DownloadRequest, DownloadResponse) -> Void)? - - public let queue: DispatchQueue - - /// Creates an instance using the provided queue. - /// - /// - Parameter queue: `DispatchQueue` on which events will fired. `.main` by default. - public init(queue: DispatchQueue = .main) { - self.queue = queue - } - - open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { - sessionDidBecomeInvalidWithError?(session, error) - } - - open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) { - taskDidReceiveChallenge?(session, task, challenge) - } - - open func urlSession(_ session: URLSession, - task: URLSessionTask, - didSendBodyData bytesSent: Int64, - totalBytesSent: Int64, - totalBytesExpectedToSend: Int64) { - taskDidSendBodyData?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) - } - - open func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { - taskNeedNewBodyStream?(session, task) - } - - open func urlSession(_ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest) { - taskWillPerformHTTPRedirection?(session, task, response, request) - } - - open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { - taskDidFinishCollectingMetrics?(session, task, metrics) - } - - open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - taskDidComplete?(session, task, error) - } - - open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { - taskIsWaitingForConnectivity?(session, task) - } - - open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - dataTaskDidReceiveData?(session, dataTask, data) - } - - open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) { - dataTaskWillCacheResponse?(session, dataTask, proposedResponse) - } - - open func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didResumeAtOffset fileOffset: Int64, - expectedTotalBytes: Int64) { - downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes) - } - - open func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64) { - downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) - } - - open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { - downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location) - } - - // MARK: Request Events - - open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { - requestDidCreateInitialURLRequest?(request, urlRequest) - } - - open func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { - requestDidFailToCreateURLRequestWithError?(request, error) - } - - open func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { - requestDidAdaptInitialRequestToAdaptedRequest?(request, initialRequest, adaptedRequest) - } - - open func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { - requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error) - } - - open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { - requestDidCreateURLRequest?(request, urlRequest) - } - - open func request(_ request: Request, didCreateTask task: URLSessionTask) { - requestDidCreateTask?(request, task) - } - - open func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { - requestDidGatherMetrics?(request, metrics) - } - - open func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { - requestDidFailTaskEarlyWithError?(request, task, error) - } - - open func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { - requestDidCompleteTaskWithError?(request, task, error) - } - - open func requestIsRetrying(_ request: Request) { - requestIsRetrying?(request) - } - - open func requestDidFinish(_ request: Request) { - requestDidFinish?(request) - } - - open func requestDidResume(_ request: Request) { - requestDidResume?(request) - } - - public func request(_ request: Request, didResumeTask task: URLSessionTask) { - requestDidResumeTask?(request, task) - } - - open func requestDidSuspend(_ request: Request) { - requestDidSuspend?(request) - } - - public func request(_ request: Request, didSuspendTask task: URLSessionTask) { - requestDidSuspendTask?(request, task) - } - - open func requestDidCancel(_ request: Request) { - requestDidCancel?(request) - } - - public func request(_ request: Request, didCancelTask task: URLSessionTask) { - requestDidCancelTask?(request, task) - } - - open func request(_ request: DataRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - data: Data?, - withResult result: Request.ValidationResult) { - requestDidValidateRequestResponseDataWithResult?(request, urlRequest, response, data, result) - } - - open func request(_ request: DataRequest, didParseResponse response: DataResponse) { - requestDidParseResponse?(request, response) - } - - public func request(_ request: DataStreamRequest, didValidateRequest urlRequest: URLRequest?, response: HTTPURLResponse, withResult result: Request.ValidationResult) { - requestDidValidateRequestResponseWithResult?(request, urlRequest, response, result) - } - - open func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { - requestDidCreateUploadable?(request, uploadable) - } - - open func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { - requestDidFailToCreateUploadableWithError?(request, error) - } - - open func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { - requestDidProvideInputStream?(request, stream) - } - - open func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result) { - requestDidFinishDownloadingUsingTaskWithResult?(request, task, result) - } - - open func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { - requestDidCreateDestinationURL?(request, url) - } - - open func request(_ request: DownloadRequest, - didValidateRequest urlRequest: URLRequest?, - response: HTTPURLResponse, - fileURL: URL?, - withResult result: Request.ValidationResult) { - requestDidValidateRequestResponseFileURLWithResult?(request, - urlRequest, - response, - fileURL, - result) - } - - open func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse) { - requestDidParseDownloadResponse?(request, response) - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift deleted file mode 100644 index 7829fc69..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPHeaders.swift +++ /dev/null @@ -1,449 +0,0 @@ -// -// HTTPHeaders.swift -// -// Copyright (c) 2014-2018 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 - -/// An order-preserving and case-insensitive representation of HTTP headers. -public struct HTTPHeaders { - private var headers: [HTTPHeader] = [] - - /// Creates an empty instance. - public init() {} - - /// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last - /// name and value encountered. - public init(_ headers: [HTTPHeader]) { - self.init() - - headers.forEach { update($0) } - } - - /// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name - /// and value encountered. - public init(_ dictionary: [String: String]) { - self.init() - - dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) } - } - - /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. - /// - /// - Parameters: - /// - name: The `HTTPHeader` name. - /// - value: The `HTTPHeader value. - public mutating func add(name: String, value: String) { - update(HTTPHeader(name: name, value: value)) - } - - /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. - /// - /// - Parameter header: The `HTTPHeader` to update or append. - public mutating func add(_ header: HTTPHeader) { - update(header) - } - - /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. - /// - /// - Parameters: - /// - name: The `HTTPHeader` name. - /// - value: The `HTTPHeader value. - public mutating func update(name: String, value: String) { - update(HTTPHeader(name: name, value: value)) - } - - /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. - /// - /// - Parameter header: The `HTTPHeader` to update or append. - public mutating func update(_ header: HTTPHeader) { - guard let index = headers.index(of: header.name) else { - headers.append(header) - return - } - - headers.replaceSubrange(index...index, with: [header]) - } - - /// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance. - /// - /// - Parameter name: The name of the `HTTPHeader` to remove. - public mutating func remove(name: String) { - guard let index = headers.index(of: name) else { return } - - headers.remove(at: index) - } - - /// Sort the current instance by header name, case insensitively. - public mutating func sort() { - headers.sort { $0.name.lowercased() < $1.name.lowercased() } - } - - /// Returns an instance sorted by header name. - /// - /// - Returns: A copy of the current instance sorted by name. - public func sorted() -> HTTPHeaders { - var headers = self - headers.sort() - - return headers - } - - /// Case-insensitively find a header's value by name. - /// - /// - Parameter name: The name of the header to search for, case-insensitively. - /// - /// - Returns: The value of header, if it exists. - public func value(for name: String) -> String? { - guard let index = headers.index(of: name) else { return nil } - - return headers[index].value - } - - /// Case-insensitively access the header with the given name. - /// - /// - Parameter name: The name of the header. - public subscript(_ name: String) -> String? { - get { value(for: name) } - set { - if let value = newValue { - update(name: name, value: value) - } else { - remove(name: name) - } - } - } - - /// The dictionary representation of all headers. - /// - /// This representation does not preserve the current order of the instance. - public var dictionary: [String: String] { - let namesAndValues = headers.map { ($0.name, $0.value) } - - return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last }) - } -} - -extension HTTPHeaders: ExpressibleByDictionaryLiteral { - public init(dictionaryLiteral elements: (String, String)...) { - self.init() - - elements.forEach { update(name: $0.0, value: $0.1) } - } -} - -extension HTTPHeaders: ExpressibleByArrayLiteral { - public init(arrayLiteral elements: HTTPHeader...) { - self.init(elements) - } -} - -extension HTTPHeaders: Sequence { - public func makeIterator() -> IndexingIterator<[HTTPHeader]> { - headers.makeIterator() - } -} - -extension HTTPHeaders: Collection { - public var startIndex: Int { - headers.startIndex - } - - public var endIndex: Int { - headers.endIndex - } - - public subscript(position: Int) -> HTTPHeader { - headers[position] - } - - public func index(after i: Int) -> Int { - headers.index(after: i) - } -} - -extension HTTPHeaders: CustomStringConvertible { - public var description: String { - headers.map { $0.description } - .joined(separator: "\n") - } -} - -// MARK: - HTTPHeader - -/// A representation of a single HTTP header's name / value pair. -public struct HTTPHeader: Hashable { - /// Name of the header. - public let name: String - - /// Value of the header. - public let value: String - - /// Creates an instance from the given `name` and `value`. - /// - /// - Parameters: - /// - name: The name of the header. - /// - value: The value of the header. - public init(name: String, value: String) { - self.name = name - self.value = value - } -} - -extension HTTPHeader: CustomStringConvertible { - public var description: String { - "\(name): \(value)" - } -} - -extension HTTPHeader { - /// Returns an `Accept` header. - /// - /// - Parameter value: The `Accept` value. - /// - Returns: The header. - public static func accept(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Accept", value: value) - } - - /// Returns an `Accept-Charset` header. - /// - /// - Parameter value: The `Accept-Charset` value. - /// - Returns: The header. - public static func acceptCharset(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Accept-Charset", value: value) - } - - /// Returns an `Accept-Language` header. - /// - /// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages. - /// Use `HTTPHeader.defaultAcceptLanguage`. - /// - /// - Parameter value: The `Accept-Language` value. - /// - /// - Returns: The header. - public static func acceptLanguage(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Accept-Language", value: value) - } - - /// Returns an `Accept-Encoding` header. - /// - /// Alamofire offers a default accept encoding value that provides the most common values. Use - /// `HTTPHeader.defaultAcceptEncoding`. - /// - /// - Parameter value: The `Accept-Encoding` value. - /// - /// - Returns: The header - public static func acceptEncoding(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Accept-Encoding", value: value) - } - - /// Returns a `Basic` `Authorization` header using the `username` and `password` provided. - /// - /// - Parameters: - /// - username: The username of the header. - /// - password: The password of the header. - /// - /// - Returns: The header. - public static func authorization(username: String, password: String) -> HTTPHeader { - let credential = Data("\(username):\(password)".utf8).base64EncodedString() - - return authorization("Basic \(credential)") - } - - /// Returns a `Bearer` `Authorization` header using the `bearerToken` provided - /// - /// - Parameter bearerToken: The bearer token. - /// - /// - Returns: The header. - public static func authorization(bearerToken: String) -> HTTPHeader { - authorization("Bearer \(bearerToken)") - } - - /// Returns an `Authorization` header. - /// - /// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use - /// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use - /// `HTTPHeader.authorization(bearerToken:)`. - /// - /// - Parameter value: The `Authorization` value. - /// - /// - Returns: The header. - public static func authorization(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Authorization", value: value) - } - - /// Returns a `Content-Disposition` header. - /// - /// - Parameter value: The `Content-Disposition` value. - /// - /// - Returns: The header. - public static func contentDisposition(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Content-Disposition", value: value) - } - - /// Returns a `Content-Type` header. - /// - /// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not be necessary to manually - /// set this value. - /// - /// - Parameter value: The `Content-Type` value. - /// - /// - Returns: The header. - public static func contentType(_ value: String) -> HTTPHeader { - HTTPHeader(name: "Content-Type", value: value) - } - - /// Returns a `User-Agent` header. - /// - /// - Parameter value: The `User-Agent` value. - /// - /// - Returns: The header. - public static func userAgent(_ value: String) -> HTTPHeader { - HTTPHeader(name: "User-Agent", value: value) - } -} - -extension Array where Element == HTTPHeader { - /// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists. - func index(of name: String) -> Int? { - let lowercasedName = name.lowercased() - return firstIndex { $0.name.lowercased() == lowercasedName } - } -} - -// MARK: - Defaults - -extension HTTPHeaders { - /// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and - /// `User-Agent`. - public static let `default`: HTTPHeaders = [.defaultAcceptEncoding, - .defaultAcceptLanguage, - .defaultUserAgent] -} - -extension HTTPHeader { - /// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS - /// versions. - /// - /// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) . - public static let defaultAcceptEncoding: HTTPHeader = { - let encodings: [String] - if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) { - encodings = ["br", "gzip", "deflate"] - } else { - encodings = ["gzip", "deflate"] - } - - return .acceptEncoding(encodings.qualityEncoded()) - }() - - /// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's - /// `preferredLanguages`. - /// - /// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5). - public static let defaultAcceptLanguage: HTTPHeader = { - .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded()) - }() - - /// Returns Alamofire's default `User-Agent` header. - /// - /// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3). - /// - /// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0` - public static let defaultUserAgent: HTTPHeader = { - let info = Bundle.main.infoDictionary - let executable = (info?[kCFBundleExecutableKey as String] as? String) ?? - (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ?? - "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) - #if targetEnvironment(macCatalyst) - return "macOS(Catalyst)" - #else - return "iOS" - #endif - #elseif os(watchOS) - return "watchOS" - #elseif os(tvOS) - return "tvOS" - #elseif os(macOS) - return "macOS" - #elseif os(Linux) - return "Linux" - #elseif os(Windows) - return "Windows" - #else - return "Unknown" - #endif - }() - - return "\(osName) \(versionString)" - }() - - let alamofireVersion = "Alamofire/\(version)" - - let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)" - - return .userAgent(userAgent) - }() -} - -extension Collection where Element == String { - func qualityEncoded() -> String { - enumerated().map { index, encoding in - let quality = 1.0 - (Double(index) * 0.1) - return "\(encoding);q=\(quality)" - }.joined(separator: ", ") - } -} - -// MARK: - System Type Extensions - -extension URLRequest { - /// Returns `allHTTPHeaderFields` as `HTTPHeaders`. - public var headers: HTTPHeaders { - get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() } - set { allHTTPHeaderFields = newValue.dictionary } - } -} - -extension HTTPURLResponse { - /// Returns `allHeaderFields` as `HTTPHeaders`. - public var headers: HTTPHeaders { - (allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() - } -} - -extension URLSessionConfiguration { - /// Returns `httpAdditionalHeaders` as `HTTPHeaders`. - public var headers: HTTPHeaders { - get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() } - set { httpAdditionalHeaders = newValue.dictionary } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift deleted file mode 100644 index 4867c1e9..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/HTTPMethod.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// HTTPMethod.swift -// -// Copyright (c) 2014-2018 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. -// - -/// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so -/// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. -/// -/// See https://tools.ietf.org/html/rfc7231#section-4.3 -public struct HTTPMethod: RawRepresentable, Equatable, Hashable { - /// `CONNECT` method. - public static let connect = HTTPMethod(rawValue: "CONNECT") - /// `DELETE` method. - public static let delete = HTTPMethod(rawValue: "DELETE") - /// `GET` method. - public static let get = HTTPMethod(rawValue: "GET") - /// `HEAD` method. - public static let head = HTTPMethod(rawValue: "HEAD") - /// `OPTIONS` method. - public static let options = HTTPMethod(rawValue: "OPTIONS") - /// `PATCH` method. - public static let patch = HTTPMethod(rawValue: "PATCH") - /// `POST` method. - public static let post = HTTPMethod(rawValue: "POST") - /// `PUT` method. - public static let put = HTTPMethod(rawValue: "PUT") - /// `TRACE` method. - public static let trace = HTTPMethod(rawValue: "TRACE") - - public let rawValue: String - - public init(rawValue: String) { - self.rawValue = rawValue - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift deleted file mode 100644 index d9cecef5..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartFormData.swift +++ /dev/null @@ -1,553 +0,0 @@ -// -// MultipartFormData.swift -// -// Copyright (c) 2014-2018 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 - - enum EncodingCharacters { - static let crlf = "\r\n" - } - - enum BoundaryGenerator { - enum BoundaryType { - case initial, encapsulated, final - } - - static func randomBoundary() -> String { - let first = UInt32.random(in: UInt32.min...UInt32.max) - let second = UInt32.random(in: UInt32.min...UInt32.max) - - return String(format: "alamofire.boundary.%08x%08x", first, second) - } - - 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 Data(boundaryText.utf8) - } - } - - 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 - - /// Default memory threshold used when encoding `MultipartFormData`, in bytes. - public static let encodingMemoryThreshold: UInt64 = 10_000_000 - - /// 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 { bodyParts.reduce(0) { $0 + $1.bodyContentLength } } - - /// The boundary used to separate the body parts in the encoded form data. - public let boundary: String - - let fileManager: FileManager - - private var bodyParts: [BodyPart] - private var bodyPartError: AFError? - private let streamBufferSize: Int - - // MARK: - Lifecycle - - /// Creates an instance. - /// - /// - Parameters: - /// - fileManager: `FileManager` to use for file operations, if needed. - /// - boundary: Boundary `String` used to separate body parts. - public init(fileManager: FileManager = .default, boundary: String? = nil) { - self.fileManager = fileManager - self.boundary = boundary ?? BoundaryGenerator.randomBoundary() - 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 - // - streamBufferSize = 1024 - } - - // MARK: - Body Parts - - /// Creates a body part from the data and appends it to the instance. - /// - /// 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 - /// - /// - Parameters: - /// - data: `Data` to encoding into the instance. - /// - name: Name to associate with the `Data` in the `Content-Disposition` HTTP header. - /// - fileName: Filename to associate with the `Data` in the `Content-Disposition` HTTP header. - /// - mimeType: MIME type to associate with the data in the `Content-Type` HTTP header. - public func append(_ data: Data, withName name: String, fileName: String? = nil, mimeType: String? = nil) { - 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 instance. - /// - /// 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. - /// - /// - Parameters: - /// - fileURL: `URL` of the file whose content will be encoded into the instance. - /// - name: 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 instance. - /// - /// 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 - /// - /// - Parameters: - /// - fileURL: `URL` of the file whose content will be encoded into the instance. - /// - name: Name to associate with the file content in the `Content-Disposition` HTTP header. - /// - fileName: Filename to associate with the file content in the `Content-Disposition` HTTP header. - /// - mimeType: 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.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.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 instance. - /// - /// 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 - /// - /// - Parameters: - /// - stream: `InputStream` to encode into the instance. - /// - length: Length, in bytes, of the stream. - /// - name: Name to associate with the stream content in the `Content-Disposition` HTTP header. - /// - fileName: Filename to associate with the stream content in the `Content-Disposition` HTTP header. - /// - mimeType: 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 stream, length, and headers and appends it to the instance. - /// - /// The body part data will be encoded using the following format: - /// - /// - HTTP headers - /// - Encoded stream data - /// - Multipart form boundary - /// - /// - Parameters: - /// - stream: `InputStream` to encode into the instance. - /// - length: Length, in bytes, of the stream. - /// - headers: `HTTPHeaders` 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 appended body parts into a single `Data` value. - /// - /// - Note: 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 `writeEncodedData(to:))` method. - /// - /// - Returns: The encoded `Data`, if encoding is successful. - /// - Throws: An `AFError` if encoding encounters an error. - 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 all appended body parts to 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: File `URL` to which to write the form data. - /// - Throws: An `AFError` if encoding encounters an error. - public func writeEncodedData(to fileURL: URL) throws { - if let bodyPartError = bodyPartError { - throw bodyPartError - } - - if fileManager.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() } - - bodyParts.first?.hasInitialBoundary = true - bodyParts.last?.hasFinalBoundary = true - - for bodyPart in 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 { - let headerText = bodyPart.headers.map { "\($0.name): \($0.value)\(EncodingCharacters.crlf)" } - .joined() - + EncodingCharacters.crlf - - return Data(headerText.utf8) - } - - 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 - } - } - - guard UInt64(encoded.count) == bodyPart.bodyContentLength else { - let error = AFError.UnexpectedInputStreamLength(bytesExpected: bodyPart.bodyContentLength, - bytesRead: UInt64(encoded.count)) - throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) - } - - 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) -> HTTPHeaders { - var disposition = "form-data; name=\"\(name)\"" - if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" } - - var headers: HTTPHeaders = [.contentDisposition(disposition)] - if let mimeType = mimeType { headers.add(.contentType(mimeType)) } - - return headers - } - - // MARK: - Private - Boundary Encoding - - private func initialBoundaryData() -> Data { - BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary) - } - - private func encapsulatedBoundaryData() -> Data { - BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary) - } - - private func finalBoundaryData() -> Data { - 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) - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift deleted file mode 100644 index 606bd33e..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/MultipartUpload.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// MultipartUpload.swift -// -// Copyright (c) 2014-2018 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 - -/// Internal type which encapsulates a `MultipartFormData` upload. -final class MultipartUpload { - lazy var result = Result { try build() } - - @Protected - private(set) var multipartFormData: MultipartFormData - let encodingMemoryThreshold: UInt64 - let request: URLRequestConvertible - let fileManager: FileManager - - init(encodingMemoryThreshold: UInt64, - request: URLRequestConvertible, - multipartFormData: MultipartFormData) { - self.encodingMemoryThreshold = encodingMemoryThreshold - self.request = request - fileManager = multipartFormData.fileManager - self.multipartFormData = multipartFormData - } - - func build() throws -> UploadRequest.Uploadable { - let uploadable: UploadRequest.Uploadable - if multipartFormData.contentLength < encodingMemoryThreshold { - let data = try multipartFormData.encode() - - uploadable = .data(data) - } else { - let tempDirectoryURL = fileManager.temporaryDirectory - let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") - let fileName = UUID().uuidString - let fileURL = directoryURL.appendingPathComponent(fileName) - - try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) - - do { - try multipartFormData.writeEncodedData(to: fileURL) - } catch { - // Cleanup after attempted write if it fails. - try? fileManager.removeItem(at: fileURL) - throw error - } - - uploadable = .file(fileURL, shouldRemove: true) - } - - return uploadable - } -} - -extension MultipartUpload: UploadConvertible { - func asURLRequest() throws -> URLRequest { - var urlRequest = try request.asURLRequest() - - $multipartFormData.read { multipartFormData in - urlRequest.headers.add(.contentType(multipartFormData.contentType)) - } - - return urlRequest - } - - func createUploadable() throws -> UploadRequest.Uploadable { - try result.get() - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift deleted file mode 100644 index b97b62fa..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/NetworkReachabilityManager.swift +++ /dev/null @@ -1,267 +0,0 @@ -// -// NetworkReachabilityManager.swift -// -// Copyright (c) 2014-2018 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) || os(Linux)) - -import Foundation -import SystemConfiguration - -/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both cellular 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. -open class NetworkReachabilityManager { - /// Defines the various states of network reachability. - public enum NetworkReachabilityStatus { - /// It is unknown whether the network is reachable. - case unknown - /// The network is not reachable. - case notReachable - /// The network is reachable on the associated `ConnectionType`. - case reachable(ConnectionType) - - init(_ flags: SCNetworkReachabilityFlags) { - guard flags.isActuallyReachable else { self = .notReachable; return } - - var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) - - if flags.isCellular { networkStatus = .reachable(.cellular) } - - self = networkStatus - } - - /// Defines the various connection types detected by reachability flags. - public enum ConnectionType { - /// The connection type is either over Ethernet or WiFi. - case ethernetOrWiFi - /// The connection type is a cellular connection. - case cellular - } - } - - /// A closure executed when the network reachability status changes. The closure takes a single argument: the - /// network reachability status. - public typealias Listener = (NetworkReachabilityStatus) -> Void - - /// Default `NetworkReachabilityManager` for the zero address and a `listenerQueue` of `.main`. - public static let `default` = NetworkReachabilityManager() - - // MARK: - Properties - - /// Whether the network is currently reachable. - open var isReachable: Bool { isReachableOnCellular || isReachableOnEthernetOrWiFi } - - /// Whether the network is currently reachable over the cellular interface. - /// - /// - Note: Using this property to decide whether to make a high or low bandwidth request is not recommended. - /// Instead, set the `allowsCellularAccess` on any `URLRequest`s being issued. - /// - open var isReachableOnCellular: Bool { status == .reachable(.cellular) } - - /// Whether the network is currently reachable over Ethernet or WiFi interface. - open var isReachableOnEthernetOrWiFi: Bool { status == .reachable(.ethernetOrWiFi) } - - /// `DispatchQueue` on which reachability will update. - public let reachabilityQueue = DispatchQueue(label: "org.alamofire.reachabilityQueue") - - /// Flags of the current reachability type, if any. - open var flags: SCNetworkReachabilityFlags? { - var flags = SCNetworkReachabilityFlags() - - return (SCNetworkReachabilityGetFlags(reachability, &flags)) ? flags : nil - } - - /// The current network reachability status. - open var status: NetworkReachabilityStatus { - flags.map(NetworkReachabilityStatus.init) ?? .unknown - } - - /// Mutable state storage. - struct MutableState { - /// A closure executed when the network reachability status changes. - var listener: Listener? - /// `DispatchQueue` on which listeners will be called. - var listenerQueue: DispatchQueue? - /// Previously calculated status. - var previousStatus: NetworkReachabilityStatus? - } - - /// `SCNetworkReachability` instance providing notifications. - private let reachability: SCNetworkReachability - - /// Protected storage for mutable state. - @Protected - private var mutableState = MutableState() - - // MARK: - Initialization - - /// Creates an instance with the specified host. - /// - /// - Note: The `host` value must *not* contain a scheme, just the hostname. - /// - /// - Parameters: - /// - host: Host used to evaluate network reachability. Must *not* include the scheme (e.g. `https`). - public convenience init?(host: String) { - guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } - - self.init(reachability: reachability) - } - - /// Creates an 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. - public convenience init?() { - var zero = sockaddr() - zero.sa_len = UInt8(MemoryLayout.size) - zero.sa_family = sa_family_t(AF_INET) - - guard let reachability = SCNetworkReachabilityCreateWithAddress(nil, &zero) else { return nil } - - self.init(reachability: reachability) - } - - private init(reachability: SCNetworkReachability) { - self.reachability = reachability - } - - deinit { - stopListening() - } - - // MARK: - Listening - - /// Starts listening for changes in network reachability status. - /// - /// - Note: Stops and removes any existing listener. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which to call the `listener` closure. `.main` by default. - /// - listener: `Listener` closure called when reachability changes. - /// - /// - Returns: `true` if listening was started successfully, `false` otherwise. - @discardableResult - open func startListening(onQueue queue: DispatchQueue = .main, - onUpdatePerforming listener: @escaping Listener) -> Bool { - stopListening() - - $mutableState.write { state in - state.listenerQueue = queue - state.listener = listener - } - - var context = SCNetworkReachabilityContext(version: 0, - info: Unmanaged.passUnretained(self).toOpaque(), - retain: nil, - release: nil, - copyDescription: nil) - let callback: SCNetworkReachabilityCallBack = { _, flags, info in - guard let info = info else { return } - - let instance = Unmanaged.fromOpaque(info).takeUnretainedValue() - instance.notifyListener(flags) - } - - let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue) - let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context) - - // Manually call listener to give initial state, since the framework may not. - if let currentFlags = flags { - reachabilityQueue.async { - self.notifyListener(currentFlags) - } - } - - return callbackAdded && queueAdded - } - - /// Stops listening for changes in network reachability status. - open func stopListening() { - SCNetworkReachabilitySetCallback(reachability, nil, nil) - SCNetworkReachabilitySetDispatchQueue(reachability, nil) - $mutableState.write { state in - state.listener = nil - state.listenerQueue = nil - state.previousStatus = nil - } - } - - // MARK: - Internal - Listener Notification - - /// Calls the `listener` closure of the `listenerQueue` if the computed status hasn't changed. - /// - /// - Note: Should only be called from the `reachabilityQueue`. - /// - /// - Parameter flags: `SCNetworkReachabilityFlags` to use to calculate the status. - func notifyListener(_ flags: SCNetworkReachabilityFlags) { - let newStatus = NetworkReachabilityStatus(flags) - - $mutableState.write { state in - guard state.previousStatus != newStatus else { return } - - state.previousStatus = newStatus - - let listener = state.listener - state.listenerQueue?.async { listener?(newStatus) } - } - } -} - -// MARK: - - -extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} - -extension SCNetworkReachabilityFlags { - var isReachable: Bool { contains(.reachable) } - var isConnectionRequired: Bool { contains(.connectionRequired) } - var canConnectAutomatically: Bool { contains(.connectionOnDemand) || contains(.connectionOnTraffic) } - var canConnectWithoutUserInteraction: Bool { canConnectAutomatically && !contains(.interventionRequired) } - var isActuallyReachable: Bool { isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) } - var isCellular: Bool { - #if os(iOS) || os(tvOS) - return contains(.isWWAN) - #else - return false - #endif - } - - /// Human readable `String` for all states, to help with debugging. - var readableDescription: String { - let W = isCellular ? "W" : "-" - let R = isReachable ? "R" : "-" - let c = isConnectionRequired ? "c" : "-" - let t = contains(.transientConnection) ? "t" : "-" - let i = contains(.interventionRequired) ? "i" : "-" - let C = contains(.connectionOnTraffic) ? "C" : "-" - let D = contains(.connectionOnDemand) ? "D" : "-" - let l = contains(.isLocalAddress) ? "l" : "-" - let d = contains(.isDirect) ? "d" : "-" - let a = contains(.connectionAutomatic) ? "a" : "-" - - return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)\(a)" - } -} -#endif diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift deleted file mode 100644 index 66434b6e..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Notifications.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// Notifications.swift -// -// Copyright (c) 2014-2018 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 { - /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`. - public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume") - /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`. - public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend") - /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`. - public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel") - /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`. - public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish") - - /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`. - public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask") - /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`. - public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask") - /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`. - public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask") - /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`. - public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask") -} - -// MARK: - - -extension Notification { - /// The `Request` contained by the instance's `userInfo`, `nil` otherwise. - public var request: Request? { - userInfo?[String.requestKey] as? Request - } - - /// Convenience initializer for a `Notification` containing a `Request` payload. - /// - /// - Parameters: - /// - name: The name of the notification. - /// - request: The `Request` payload. - init(name: Notification.Name, request: Request) { - self.init(name: name, object: nil, userInfo: [String.requestKey: request]) - } -} - -extension NotificationCenter { - /// Convenience function for posting notifications with `Request` payloads. - /// - /// - Parameters: - /// - name: The name of the notification. - /// - request: The `Request` payload. - func postNotification(named name: Notification.Name, with request: Request) { - let notification = Notification(name: name, request: request) - post(notification) - } -} - -extension String { - /// User info dictionary key representing the `Request` associated with the notification. - fileprivate static let requestKey = "org.alamofire.notification.key.request" -} - -/// `EventMonitor` that provides Alamofire's notifications. -public final class AlamofireNotifications: EventMonitor { - public func requestDidResume(_ request: Request) { - NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request) - } - - public func requestDidSuspend(_ request: Request) { - NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request) - } - - public func requestDidCancel(_ request: Request) { - NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request) - } - - public func requestDidFinish(_ request: Request) { - NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request) - } - - public func request(_ request: Request, didResumeTask task: URLSessionTask) { - NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request) - } - - public func request(_ request: Request, didSuspendTask task: URLSessionTask) { - NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request) - } - - public func request(_ request: Request, didCancelTask task: URLSessionTask) { - NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request) - } - - public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { - NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request) - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift deleted file mode 100644 index b06a0ccc..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/OperationQueue+Alamofire.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// OperationQueue+Alamofire.swift -// -// Copyright (c) 2014-2018 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 OperationQueue { - /// Creates an instance using the provided parameters. - /// - /// - Parameters: - /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. - /// - maxConcurrentOperationCount: Maximum concurrent operations. - /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. - /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. - /// - name: Name for the queue. `nil` by default. - /// - startSuspended: Whether the queue starts suspended. `false` by default. - convenience init(qualityOfService: QualityOfService = .default, - maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, - underlyingQueue: DispatchQueue? = nil, - name: String? = nil, - startSuspended: Bool = false) { - self.init() - self.qualityOfService = qualityOfService - self.maxConcurrentOperationCount = maxConcurrentOperationCount - self.underlyingQueue = underlyingQueue - self.name = name - isSuspended = startSuspended - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift deleted file mode 100644 index f4facbee..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoder.swift +++ /dev/null @@ -1,184 +0,0 @@ -// -// ParameterEncoder.swift -// -// Copyright (c) 2014-2018 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 encode any `Encodable` type into a `URLRequest`. -public protocol ParameterEncoder { - /// Encode the provided `Encodable` parameters into `request`. - /// - /// - Parameters: - /// - parameters: The `Encodable` parameter value. - /// - request: The `URLRequest` into which to encode the parameters. - /// - /// - Returns: A `URLRequest` with the result of the encoding. - /// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of - /// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`. - func encode(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest -} - -/// A `ParameterEncoder` that encodes types as JSON body data. -/// -/// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`. -open class JSONParameterEncoder: ParameterEncoder { - /// Returns an encoder with default parameters. - public static var `default`: JSONParameterEncoder { JSONParameterEncoder() } - - /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`. - public static var prettyPrinted: JSONParameterEncoder { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - - return JSONParameterEncoder(encoder: encoder) - } - - /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`. - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - public static var sortedKeys: JSONParameterEncoder { - let encoder = JSONEncoder() - encoder.outputFormatting = .sortedKeys - - return JSONParameterEncoder(encoder: encoder) - } - - /// `JSONEncoder` used to encode parameters. - public let encoder: JSONEncoder - - /// Creates an instance with the provided `JSONEncoder`. - /// - /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default. - public init(encoder: JSONEncoder = JSONEncoder()) { - self.encoder = encoder - } - - open func encode(_ parameters: Parameters?, - into request: URLRequest) throws -> URLRequest { - guard let parameters = parameters else { return request } - - var request = request - - do { - let data = try encoder.encode(parameters) - request.httpBody = data - if request.headers["Content-Type"] == nil { - request.headers.update(.contentType("application/json")) - } - } catch { - throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) - } - - return request - } -} - -/// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending -/// on the `Destination` set. -/// -/// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to -/// `application/x-www-form-urlencoded; charset=utf-8`. -/// -/// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer. -open class URLEncodedFormParameterEncoder: ParameterEncoder { - /// Defines where the URL-encoded string should be set for each `URLRequest`. - public enum Destination { - /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request. - /// Sets it to the `httpBody` for all other methods. - case methodDependent - /// Applies the encoded query string to any existing query string from the `URLRequest`. - case queryString - /// Applies the encoded query string to the `httpBody` of the `URLRequest`. - case httpBody - - /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`. - /// - /// - Parameter method: The `HTTPMethod`. - /// - /// - Returns: Whether the URL-encoded string should be applied to a `URL`. - func encodesParametersInURL(for method: HTTPMethod) -> Bool { - switch self { - case .methodDependent: return [.get, .head, .delete].contains(method) - case .queryString: return true - case .httpBody: return false - } - } - } - - /// Returns an encoder with default parameters. - public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() } - - /// The `URLEncodedFormEncoder` to use. - public let encoder: URLEncodedFormEncoder - - /// The `Destination` for the URL-encoded string. - public let destination: Destination - - /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value. - /// - /// - Parameters: - /// - encoder: The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default. - /// - destination: The `Destination`. `.methodDependent` by default. - public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) { - self.encoder = encoder - self.destination = destination - } - - open func encode(_ parameters: Parameters?, - into request: URLRequest) throws -> URLRequest { - guard let parameters = parameters else { return request } - - var request = request - - guard let url = request.url else { - throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) - } - - guard let method = request.method else { - let rawValue = request.method?.rawValue ?? "nil" - throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue))) - } - - if destination.encodesParametersInURL(for: method), - var components = URLComponents(url: url, resolvingAgainstBaseURL: false) { - let query: String = try Result { try encoder.encode(parameters) } - .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() - let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands() - components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString - - guard let newURL = components.url else { - throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) - } - - request.url = newURL - } else { - if request.headers["Content-Type"] == nil { - request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) - } - - request.httpBody = try Result { try encoder.encode(parameters) } - .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() - } - - return request - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift deleted file mode 100644 index 6e72604c..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/ParameterEncoding.swift +++ /dev/null @@ -1,317 +0,0 @@ -// -// ParameterEncoding.swift -// -// Copyright (c) 2014-2018 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 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 `URLRequest` by encoding parameters and applying them on the passed request. - /// - /// - Parameters: - /// - urlRequest: `URLRequestConvertible` value onto which parameters will be encoded. - /// - parameters: `Parameters` to encode onto the request. - /// - /// - Returns: The encoded `URLRequest`. - /// - Throws: Any `Error` produced during parameter encoding. - 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`. -/// -/// There is no published specification for how to encode collection types. By default 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`) is used. Optionally, `ArrayEncoding` can be used to omit the -/// square brackets appended to array keys. -/// -/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode -/// `true` as 1 and `false` as 0. -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. - public enum Destination { - /// 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. - case methodDependent - /// Sets or appends encoded query string result to existing query string. - case queryString - /// Sets encoded query string result as the HTTP body of the URL request. - case httpBody - - func encodesParametersInURL(for method: HTTPMethod) -> Bool { - switch self { - case .methodDependent: return [.get, .head, .delete].contains(method) - case .queryString: return true - case .httpBody: return false - } - } - } - - /// Configures how `Array` parameters are encoded. - public enum ArrayEncoding { - /// An empty set of square brackets is appended to the key for every value. This is the default behavior. - case brackets - /// No brackets are appended. The key is encoded as is. - case noBrackets - - func encode(key: String) -> String { - switch self { - case .brackets: - return "\(key)[]" - case .noBrackets: - return key - } - } - } - - /// Configures how `Bool` parameters are encoded. - public enum BoolEncoding { - /// Encode `true` as `1` and `false` as `0`. This is the default behavior. - case numeric - /// Encode `true` and `false` as string literals. - case literal - - func encode(value: Bool) -> String { - switch self { - case .numeric: - return value ? "1" : "0" - case .literal: - return value ? "true" : "false" - } - } - } - - // MARK: Properties - - /// Returns a default `URLEncoding` instance with a `.methodDependent` destination. - public static var `default`: URLEncoding { URLEncoding() } - - /// Returns a `URLEncoding` instance with a `.queryString` destination. - public static var queryString: URLEncoding { URLEncoding(destination: .queryString) } - - /// Returns a `URLEncoding` instance with an `.httpBody` destination. - public static var httpBody: URLEncoding { URLEncoding(destination: .httpBody) } - - /// The destination defining where the encoded query string is to be applied to the URL request. - public let destination: Destination - - /// The encoding to use for `Array` parameters. - public let arrayEncoding: ArrayEncoding - - /// The encoding to use for `Bool` parameters. - public let boolEncoding: BoolEncoding - - // MARK: Initialization - - /// Creates an instance using the specified parameters. - /// - /// - Parameters: - /// - destination: `Destination` defining where the encoded query string will be applied. `.methodDependent` by - /// default. - /// - arrayEncoding: `ArrayEncoding` to use. `.brackets` by default. - /// - boolEncoding: `BoolEncoding` to use. `.numeric` by default. - public init(destination: Destination = .methodDependent, - arrayEncoding: ArrayEncoding = .brackets, - boolEncoding: BoolEncoding = .numeric) { - self.destination = destination - self.arrayEncoding = arrayEncoding - self.boolEncoding = boolEncoding - } - - // MARK: Encoding - - 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 = urlRequest.method, destination.encodesParametersInURL(for: 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.headers["Content-Type"] == nil { - urlRequest.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) - } - - urlRequest.httpBody = Data(query(parameters).utf8) - } - - return urlRequest - } - - /// Creates a percent-escaped, URL encoded query string components from the given key-value pair recursively. - /// - /// - Parameters: - /// - key: Key of the query component. - /// - value: 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)] = [] - switch value { - case let dictionary as [String: Any]: - for (nestedKey, value) in dictionary { - components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) - } - case let array as [Any]: - for value in array { - components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value) - } - case let number as NSNumber: - if number.isBool { - components.append((escape(key), escape(boolEncoding.encode(value: number.boolValue)))) - } else { - components.append((escape(key), escape("\(number)"))) - } - case let bool as Bool: - components.append((escape(key), escape(boolEncoding.encode(value: bool)))) - default: - components.append((escape(key), escape("\(value)"))) - } - return components - } - - /// Creates a percent-escaped string following RFC 3986 for a query string key or value. - /// - /// - Parameter string: `String` to be percent-escaped. - /// - /// - Returns: The percent-escaped `String`. - public func escape(_ string: String) -> String { - string.addingPercentEncoding(withAllowedCharacters: .afURLQueryAllowed) ?? string - } - - private func query(_ parameters: [String: Any]) -> 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: "&") - } -} - -// 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 { JSONEncoding() } - - /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. - public static var prettyPrinted: JSONEncoding { JSONEncoding(options: .prettyPrinted) } - - /// The options for writing the parameters as JSON data. - public let options: JSONSerialization.WritingOptions - - // MARK: Initialization - - /// Creates an instance using the specified `WritingOptions`. - /// - /// - Parameter options: `JSONSerialization.WritingOptions` to use. - public init(options: JSONSerialization.WritingOptions = []) { - self.options = options - } - - // MARK: Encoding - - 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.headers["Content-Type"] == nil { - urlRequest.headers.update(.contentType("application/json")) - } - - urlRequest.httpBody = data - } catch { - throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) - } - - return urlRequest - } - - /// Encodes any JSON compatible object into a `URLRequest`. - /// - /// - Parameters: - /// - urlRequest: `URLRequestConvertible` value into which the object will be encoded. - /// - jsonObject: `Any` value (must be JSON compatible` to be encoded into the `URLRequest`. `nil` by default. - /// - /// - Returns: The encoded `URLRequest`. - /// - Throws: Any `Error` produced during encoding. - 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.headers["Content-Type"] == nil { - urlRequest.headers.update(.contentType("application/json")) - } - - urlRequest.httpBody = data - } catch { - throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) - } - - return urlRequest - } -} - -// MARK: - - -extension NSNumber { - fileprivate var isBool: Bool { - // Use Obj-C type encoding to check whether the underlying type is a `Bool`, as it's guaranteed as part of - // swift-corelibs-foundation, per [this discussion on the Swift forums](https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553/22). - String(cString: objCType) == "c" - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift deleted file mode 100644 index 6288205e..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Protected.swift +++ /dev/null @@ -1,224 +0,0 @@ -// -// Protected.swift -// -// Copyright (c) 2014-2020 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 - -private protocol Lock { - func lock() - func unlock() -} - -extension Lock { - /// Executes a closure returning a value while acquiring the lock. - /// - /// - Parameter closure: The closure to run. - /// - /// - Returns: The value the closure generated. - func around(_ closure: () -> T) -> T { - lock(); defer { unlock() } - return closure() - } - - /// Execute a closure while acquiring the lock. - /// - /// - Parameter closure: The closure to run. - func around(_ closure: () -> Void) { - lock(); defer { unlock() } - closure() - } -} - -#if os(Linux) -/// A `pthread_mutex_t` wrapper. -final class MutexLock: Lock { - private var mutex: UnsafeMutablePointer - - init() { - mutex = .allocate(capacity: 1) - - var attr = pthread_mutexattr_t() - pthread_mutexattr_init(&attr) - pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK)) - - let error = pthread_mutex_init(mutex, &attr) - precondition(error == 0, "Failed to create pthread_mutex") - } - - deinit { - let error = pthread_mutex_destroy(mutex) - precondition(error == 0, "Failed to destroy pthread_mutex") - } - - fileprivate func lock() { - let error = pthread_mutex_lock(mutex) - precondition(error == 0, "Failed to lock pthread_mutex") - } - - fileprivate func unlock() { - let error = pthread_mutex_unlock(mutex) - precondition(error == 0, "Failed to unlock pthread_mutex") - } -} -#endif - -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) -/// An `os_unfair_lock` wrapper. -final class UnfairLock: Lock { - private let unfairLock: os_unfair_lock_t - - init() { - unfairLock = .allocate(capacity: 1) - unfairLock.initialize(to: os_unfair_lock()) - } - - deinit { - unfairLock.deinitialize(count: 1) - unfairLock.deallocate() - } - - fileprivate func lock() { - os_unfair_lock_lock(unfairLock) - } - - fileprivate func unlock() { - os_unfair_lock_unlock(unfairLock) - } -} -#endif - -/// A thread-safe wrapper around a value. -@propertyWrapper -@dynamicMemberLookup -final class Protected { - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - private let lock = UnfairLock() - #elseif os(Linux) - private let lock = MutexLock() - #endif - private var value: T - - init(_ value: T) { - self.value = value - } - - /// The contained value. Unsafe for anything more than direct read or write. - var wrappedValue: T { - get { lock.around { value } } - set { lock.around { value = newValue } } - } - - var projectedValue: Protected { self } - - init(wrappedValue: T) { - value = wrappedValue - } - - /// Synchronously read or transform the contained value. - /// - /// - Parameter closure: The closure to execute. - /// - /// - Returns: The return value of the closure passed. - func read(_ closure: (T) -> U) -> U { - lock.around { closure(self.value) } - } - - /// Synchronously modify the protected value. - /// - /// - Parameter closure: The closure to execute. - /// - /// - Returns: The modified value. - @discardableResult - func write(_ closure: (inout T) -> U) -> U { - lock.around { closure(&self.value) } - } - - subscript(dynamicMember keyPath: WritableKeyPath) -> Property { - get { lock.around { value[keyPath: keyPath] } } - set { lock.around { value[keyPath: keyPath] = newValue } } - } -} - -extension Protected where T: RangeReplaceableCollection { - /// Adds a new element to the end of this protected collection. - /// - /// - Parameter newElement: The `Element` to append. - func append(_ newElement: T.Element) { - write { (ward: inout T) in - ward.append(newElement) - } - } - - /// Adds the elements of a sequence to the end of this protected collection. - /// - /// - Parameter newElements: The `Sequence` to append. - func append(contentsOf newElements: S) where S.Element == T.Element { - write { (ward: inout T) in - ward.append(contentsOf: newElements) - } - } - - /// Add the elements of a collection to the end of the protected collection. - /// - /// - Parameter newElements: The `Collection` to append. - func append(contentsOf newElements: C) where C.Element == T.Element { - write { (ward: inout T) in - ward.append(contentsOf: newElements) - } - } -} - -extension Protected where T == Data? { - /// Adds the contents of a `Data` value to the end of the protected `Data`. - /// - /// - Parameter data: The `Data` to be appended. - func append(_ data: Data) { - write { (ward: inout T) in - ward?.append(data) - } - } -} - -extension Protected where T == Request.MutableState { - /// Attempts to transition to the passed `State`. - /// - /// - Parameter state: The `State` to attempt transition to. - /// - /// - Returns: Whether the transition occurred. - func attemptToTransitionTo(_ state: Request.State) -> Bool { - lock.around { - guard value.state.canTransitionTo(state) else { return false } - - value.state = state - - return true - } - } - - /// Perform a closure while locked with the provided `Request.State`. - /// - /// - Parameter perform: The closure to perform while locked. - func withState(perform: (Request.State) -> Void) { - lock.around { perform(value.state) } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift deleted file mode 100644 index b6c069ca..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/RedirectHandler.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// RedirectHandler.swift -// -// Copyright (c) 2014-2018 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 handles how an HTTP redirect response from a remote server should be redirected to the new request. -public protocol RedirectHandler { - /// Determines how the HTTP redirect response should be redirected to the new request. - /// - /// The `completion` closure should be passed one of three possible options: - /// - /// 1. The new request specified by the redirect (this is the most common use case). - /// 2. A modified version of the new request (you may want to route it somewhere else). - /// 3. A `nil` value to deny the redirect request and return the body of the redirect response. - /// - /// - Parameters: - /// - task: The `URLSessionTask` whose request resulted in a redirect. - /// - request: The `URLRequest` to the new location specified by the redirect response. - /// - response: The `HTTPURLResponse` containing the server's response to the original request. - /// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`. - func task(_ task: URLSessionTask, - willBeRedirectedTo request: URLRequest, - for response: HTTPURLResponse, - completion: @escaping (URLRequest?) -> Void) -} - -// MARK: - - -/// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect. -public struct Redirector { - /// Defines the behavior of the `Redirector` type. - public enum Behavior { - /// Follow the redirect as defined in the response. - case follow - /// Do not follow the redirect defined in the response. - case doNotFollow - /// Modify the redirect request defined in the response. - case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) - } - - /// Returns a `Redirector` with a `.follow` `Behavior`. - public static let follow = Redirector(behavior: .follow) - /// Returns a `Redirector` with a `.doNotFollow` `Behavior`. - public static let doNotFollow = Redirector(behavior: .doNotFollow) - - /// The `Behavior` of the `Redirector`. - public let behavior: Behavior - - /// Creates a `Redirector` instance from the `Behavior`. - /// - /// - Parameter behavior: The `Behavior`. - public init(behavior: Behavior) { - self.behavior = behavior - } -} - -// MARK: - - -extension Redirector: RedirectHandler { - public func task(_ task: URLSessionTask, - willBeRedirectedTo request: URLRequest, - for response: HTTPURLResponse, - completion: @escaping (URLRequest?) -> Void) { - switch behavior { - case .follow: - completion(request) - case .doNotFollow: - completion(nil) - case let .modify(closure): - let request = closure(task, request, response) - completion(request) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift deleted file mode 100644 index 849f8784..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Request.swift +++ /dev/null @@ -1,1882 +0,0 @@ -// -// Request.swift -// -// Copyright (c) 2014-2020 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 - -/// `Request` is the common superclass of all Alamofire request types and provides common state, delegate, and callback -/// handling. -public class Request { - /// State of the `Request`, with managed transitions between states set when calling `resume()`, `suspend()`, or - /// `cancel()` on the `Request`. - public enum State { - /// Initial state of the `Request`. - case initialized - /// `State` set when `resume()` is called. Any tasks created for the `Request` will have `resume()` called on - /// them in this state. - case resumed - /// `State` set when `suspend()` is called. Any tasks created for the `Request` will have `suspend()` called on - /// them in this state. - case suspended - /// `State` set when `cancel()` is called. Any tasks created for the `Request` will have `cancel()` called on - /// them. Unlike `resumed` or `suspended`, once in the `cancelled` state, the `Request` can no longer transition - /// to any other state. - case cancelled - /// `State` set when all response serialization completion closures have been cleared on the `Request` and - /// enqueued on their respective queues. - case finished - - /// Determines whether `self` can be transitioned to the provided `State`. - func canTransitionTo(_ state: State) -> Bool { - switch (self, state) { - case (.initialized, _): - return true - case (_, .initialized), (.cancelled, _), (.finished, _): - return false - case (.resumed, .cancelled), (.suspended, .cancelled), (.resumed, .suspended), (.suspended, .resumed): - return true - case (.suspended, .suspended), (.resumed, .resumed): - return false - case (_, .finished): - return true - } - } - } - - // MARK: - Initial State - - /// `UUID` providing a unique identifier for the `Request`, used in the `Hashable` and `Equatable` conformances. - public let id: UUID - /// The serial queue for all internal async actions. - public let underlyingQueue: DispatchQueue - /// The queue used for all serialization actions. By default it's a serial queue that targets `underlyingQueue`. - public let serializationQueue: DispatchQueue - /// `EventMonitor` used for event callbacks. - public let eventMonitor: EventMonitor? - /// The `Request`'s interceptor. - public let interceptor: RequestInterceptor? - /// The `Request`'s delegate. - public private(set) weak var delegate: RequestDelegate? - - // MARK: - Mutable State - - /// Type encapsulating all mutable state that may need to be accessed from anything other than the `underlyingQueue`. - struct MutableState { - /// State of the `Request`. - var state: State = .initialized - /// `ProgressHandler` and `DispatchQueue` provided for upload progress callbacks. - var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? - /// `ProgressHandler` and `DispatchQueue` provided for download progress callbacks. - var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? - /// `RedirectHandler` provided for to handle request redirection. - var redirectHandler: RedirectHandler? - /// `CachedResponseHandler` provided to handle response caching. - var cachedResponseHandler: CachedResponseHandler? - /// Queue and closure called when the `Request` is able to create a cURL description of itself. - var cURLHandler: (queue: DispatchQueue, handler: (String) -> Void)? - /// Queue and closure called when the `Request` creates a `URLRequest`. - var urlRequestHandler: (queue: DispatchQueue, handler: (URLRequest) -> Void)? - /// Queue and closure called when the `Request` creates a `URLSessionTask`. - var urlSessionTaskHandler: (queue: DispatchQueue, handler: (URLSessionTask) -> Void)? - /// Response serialization closures that handle response parsing. - var responseSerializers: [() -> Void] = [] - /// Response serialization completion closures executed once all response serializers are complete. - var responseSerializerCompletions: [() -> Void] = [] - /// Whether response serializer processing is finished. - var responseSerializerProcessingFinished = false - /// `URLCredential` used for authentication challenges. - var credential: URLCredential? - /// All `URLRequest`s created by Alamofire on behalf of the `Request`. - var requests: [URLRequest] = [] - /// All `URLSessionTask`s created by Alamofire on behalf of the `Request`. - var tasks: [URLSessionTask] = [] - /// All `URLSessionTaskMetrics` values gathered by Alamofire on behalf of the `Request`. Should correspond - /// exactly the the `tasks` created. - var metrics: [URLSessionTaskMetrics] = [] - /// Number of times any retriers provided retried the `Request`. - var retryCount = 0 - /// Final `AFError` for the `Request`, whether from various internal Alamofire calls or as a result of a `task`. - var error: AFError? - /// Whether the instance has had `finish()` called and is running the serializers. Should be replaced with a - /// representation in the state machine in the future. - var isFinishing = false - } - - /// Protected `MutableState` value that provides thread-safe access to state values. - @Protected - fileprivate var mutableState = MutableState() - - /// `State` of the `Request`. - public var state: State { mutableState.state } - /// Returns whether `state` is `.initialized`. - public var isInitialized: Bool { state == .initialized } - /// Returns whether `state is `.resumed`. - public var isResumed: Bool { state == .resumed } - /// Returns whether `state` is `.suspended`. - public var isSuspended: Bool { state == .suspended } - /// Returns whether `state` is `.cancelled`. - public var isCancelled: Bool { state == .cancelled } - /// Returns whether `state` is `.finished`. - public var isFinished: Bool { state == .finished } - - // MARK: Progress - - /// Closure type executed when monitoring the upload or download progress of a request. - public typealias ProgressHandler = (Progress) -> Void - - /// `Progress` of the upload of the body of the executed `URLRequest`. Reset to `0` if the `Request` is retried. - public let uploadProgress = Progress(totalUnitCount: 0) - /// `Progress` of the download of any response data. Reset to `0` if the `Request` is retried. - public let downloadProgress = Progress(totalUnitCount: 0) - /// `ProgressHandler` called when `uploadProgress` is updated, on the provided `DispatchQueue`. - private var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? { - get { mutableState.uploadProgressHandler } - set { mutableState.uploadProgressHandler = newValue } - } - - /// `ProgressHandler` called when `downloadProgress` is updated, on the provided `DispatchQueue`. - fileprivate var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? { - get { mutableState.downloadProgressHandler } - set { mutableState.downloadProgressHandler = newValue } - } - - // MARK: Redirect Handling - - /// `RedirectHandler` set on the instance. - public private(set) var redirectHandler: RedirectHandler? { - get { mutableState.redirectHandler } - set { mutableState.redirectHandler = newValue } - } - - // MARK: Cached Response Handling - - /// `CachedResponseHandler` set on the instance. - public private(set) var cachedResponseHandler: CachedResponseHandler? { - get { mutableState.cachedResponseHandler } - set { mutableState.cachedResponseHandler = newValue } - } - - // MARK: URLCredential - - /// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods. - public private(set) var credential: URLCredential? { - get { mutableState.credential } - set { mutableState.credential = newValue } - } - - // MARK: Validators - - /// `Validator` callback closures that store the validation calls enqueued. - @Protected - fileprivate var validators: [() -> Void] = [] - - // MARK: URLRequests - - /// All `URLRequests` created on behalf of the `Request`, including original and adapted requests. - public var requests: [URLRequest] { mutableState.requests } - /// First `URLRequest` created on behalf of the `Request`. May not be the first one actually executed. - public var firstRequest: URLRequest? { requests.first } - /// Last `URLRequest` created on behalf of the `Request`. - public var lastRequest: URLRequest? { requests.last } - /// Current `URLRequest` created on behalf of the `Request`. - public var request: URLRequest? { lastRequest } - - /// `URLRequest`s from all of the `URLSessionTask`s executed on behalf of the `Request`. May be different from - /// `requests` due to `URLSession` manipulation. - public var performedRequests: [URLRequest] { $mutableState.read { $0.tasks.compactMap { $0.currentRequest } } } - - // MARK: HTTPURLResponse - - /// `HTTPURLResponse` received from the server, if any. If the `Request` was retried, this is the response of the - /// last `URLSessionTask`. - public var response: HTTPURLResponse? { lastTask?.response as? HTTPURLResponse } - - // MARK: Tasks - - /// All `URLSessionTask`s created on behalf of the `Request`. - public var tasks: [URLSessionTask] { mutableState.tasks } - /// First `URLSessionTask` created on behalf of the `Request`. - public var firstTask: URLSessionTask? { tasks.first } - /// Last `URLSessionTask` crated on behalf of the `Request`. - public var lastTask: URLSessionTask? { tasks.last } - /// Current `URLSessionTask` created on behalf of the `Request`. - public var task: URLSessionTask? { lastTask } - - // MARK: Metrics - - /// All `URLSessionTaskMetrics` gathered on behalf of the `Request`. Should correspond to the `tasks` created. - public var allMetrics: [URLSessionTaskMetrics] { mutableState.metrics } - /// First `URLSessionTaskMetrics` gathered on behalf of the `Request`. - public var firstMetrics: URLSessionTaskMetrics? { allMetrics.first } - /// Last `URLSessionTaskMetrics` gathered on behalf of the `Request`. - public var lastMetrics: URLSessionTaskMetrics? { allMetrics.last } - /// Current `URLSessionTaskMetrics` gathered on behalf of the `Request`. - public var metrics: URLSessionTaskMetrics? { lastMetrics } - - // MARK: Retry Count - - /// Number of times the `Request` has been retried. - public var retryCount: Int { mutableState.retryCount } - - // MARK: Error - - /// `Error` returned from Alamofire internally, from the network request directly, or any validators executed. - public fileprivate(set) var error: AFError? { - get { mutableState.error } - set { mutableState.error = newValue } - } - - /// Default initializer for the `Request` superclass. - /// - /// - Parameters: - /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. - /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. - /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets - /// `underlyingQueue`, but can be passed another queue from a `Session`. - /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. - /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. - /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. - init(id: UUID = UUID(), - underlyingQueue: DispatchQueue, - serializationQueue: DispatchQueue, - eventMonitor: EventMonitor?, - interceptor: RequestInterceptor?, - delegate: RequestDelegate) { - self.id = id - self.underlyingQueue = underlyingQueue - self.serializationQueue = serializationQueue - self.eventMonitor = eventMonitor - self.interceptor = interceptor - self.delegate = delegate - } - - // MARK: - Internal Event API - - // All API must be called from underlyingQueue. - - /// Called when an initial `URLRequest` has been created on behalf of the instance. If a `RequestAdapter` is active, - /// the `URLRequest` will be adapted before being issued. - /// - /// - Parameter request: The `URLRequest` created. - func didCreateInitialURLRequest(_ request: URLRequest) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - $mutableState.write { $0.requests.append(request) } - - eventMonitor?.request(self, didCreateInitialURLRequest: request) - } - - /// Called when initial `URLRequest` creation has failed, typically through a `URLRequestConvertible`. - /// - /// - Note: Triggers retry. - /// - /// - Parameter error: `AFError` thrown from the failed creation. - func didFailToCreateURLRequest(with error: AFError) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - self.error = error - - eventMonitor?.request(self, didFailToCreateURLRequestWithError: error) - - callCURLHandlerIfNecessary() - - retryOrFinish(error: error) - } - - /// Called when a `RequestAdapter` has successfully adapted a `URLRequest`. - /// - /// - Parameters: - /// - initialRequest: The `URLRequest` that was adapted. - /// - adaptedRequest: The `URLRequest` returned by the `RequestAdapter`. - func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - $mutableState.write { $0.requests.append(adaptedRequest) } - - eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest) - } - - /// Called when a `RequestAdapter` fails to adapt a `URLRequest`. - /// - /// - Note: Triggers retry. - /// - /// - Parameters: - /// - request: The `URLRequest` the adapter was called with. - /// - error: The `AFError` returned by the `RequestAdapter`. - func didFailToAdaptURLRequest(_ request: URLRequest, withError error: AFError) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - self.error = error - - eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error) - - callCURLHandlerIfNecessary() - - retryOrFinish(error: error) - } - - /// Final `URLRequest` has been created for the instance. - /// - /// - Parameter request: The `URLRequest` created. - func didCreateURLRequest(_ request: URLRequest) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - $mutableState.read { state in - state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) } - } - - eventMonitor?.request(self, didCreateURLRequest: request) - - callCURLHandlerIfNecessary() - } - - /// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`. - private func callCURLHandlerIfNecessary() { - $mutableState.write { mutableState in - guard let cURLHandler = mutableState.cURLHandler else { return } - - cURLHandler.queue.async { cURLHandler.handler(self.cURLDescription()) } - - mutableState.cURLHandler = nil - } - } - - /// Called when a `URLSessionTask` is created on behalf of the instance. - /// - /// - Parameter task: The `URLSessionTask` created. - func didCreateTask(_ task: URLSessionTask) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - $mutableState.write { state in - state.tasks.append(task) - - guard let urlSessionTaskHandler = state.urlSessionTaskHandler else { return } - - urlSessionTaskHandler.queue.async { urlSessionTaskHandler.handler(task) } - } - - eventMonitor?.request(self, didCreateTask: task) - } - - /// Called when resumption is completed. - func didResume() { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - eventMonitor?.requestDidResume(self) - } - - /// Called when a `URLSessionTask` is resumed on behalf of the instance. - /// - /// - Parameter task: The `URLSessionTask` resumed. - func didResumeTask(_ task: URLSessionTask) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - eventMonitor?.request(self, didResumeTask: task) - } - - /// Called when suspension is completed. - func didSuspend() { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - eventMonitor?.requestDidSuspend(self) - } - - /// Called when a `URLSessionTask` is suspended on behalf of the instance. - /// - /// - Parameter task: The `URLSessionTask` suspended. - func didSuspendTask(_ task: URLSessionTask) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - eventMonitor?.request(self, didSuspendTask: task) - } - - /// Called when cancellation is completed, sets `error` to `AFError.explicitlyCancelled`. - func didCancel() { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - error = error ?? AFError.explicitlyCancelled - - eventMonitor?.requestDidCancel(self) - } - - /// Called when a `URLSessionTask` is cancelled on behalf of the instance. - /// - /// - Parameter task: The `URLSessionTask` cancelled. - func didCancelTask(_ task: URLSessionTask) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - eventMonitor?.request(self, didCancelTask: task) - } - - /// Called when a `URLSessionTaskMetrics` value is gathered on behalf of the instance. - /// - /// - Parameter metrics: The `URLSessionTaskMetrics` gathered. - func didGatherMetrics(_ metrics: URLSessionTaskMetrics) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - $mutableState.write { $0.metrics.append(metrics) } - - eventMonitor?.request(self, didGatherMetrics: metrics) - } - - /// Called when a `URLSessionTask` fails before it is finished, typically during certificate pinning. - /// - /// - Parameters: - /// - task: The `URLSessionTask` which failed. - /// - error: The early failure `AFError`. - func didFailTask(_ task: URLSessionTask, earlyWithError error: AFError) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - self.error = error - - // Task will still complete, so didCompleteTask(_:with:) will handle retry. - eventMonitor?.request(self, didFailTask: task, earlyWithError: error) - } - - /// Called when a `URLSessionTask` completes. All tasks will eventually call this method. - /// - /// - Note: Response validation is synchronously triggered in this step. - /// - /// - Parameters: - /// - task: The `URLSessionTask` which completed. - /// - error: The `AFError` `task` may have completed with. If `error` has already been set on the instance, this - /// value is ignored. - func didCompleteTask(_ task: URLSessionTask, with error: AFError?) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - self.error = self.error ?? error - - validators.forEach { $0() } - - eventMonitor?.request(self, didCompleteTask: task, with: error) - - retryOrFinish(error: self.error) - } - - /// Called when the `RequestDelegate` is going to retry this `Request`. Calls `reset()`. - func prepareForRetry() { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - $mutableState.write { $0.retryCount += 1 } - - reset() - - eventMonitor?.requestIsRetrying(self) - } - - /// Called to determine whether retry will be triggered for the particular error, or whether the instance should - /// call `finish()`. - /// - /// - Parameter error: The possible `AFError` which may trigger retry. - func retryOrFinish(error: AFError?) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - guard let error = error, let delegate = delegate else { finish(); return } - - delegate.retryResult(for: self, dueTo: error) { retryResult in - switch retryResult { - case .doNotRetry: - self.finish() - case let .doNotRetryWithError(retryError): - self.finish(error: retryError.asAFError(orFailWith: "Received retryError was not already AFError")) - case .retry, .retryWithDelay: - delegate.retryRequest(self, withDelay: retryResult.delay) - } - } - } - - /// Finishes this `Request` and starts the response serializers. - /// - /// - Parameter error: The possible `Error` with which the instance will finish. - func finish(error: AFError? = nil) { - dispatchPrecondition(condition: .onQueue(underlyingQueue)) - - guard !mutableState.isFinishing else { return } - - mutableState.isFinishing = true - - if let error = error { self.error = error } - - // Start response handlers - processNextResponseSerializer() - - eventMonitor?.requestDidFinish(self) - } - - /// Appends the response serialization closure to the instance. - /// - /// - Note: This method will also `resume` the instance if `delegate.startImmediately` returns `true`. - /// - /// - Parameter closure: The closure containing the response serialization call. - func appendResponseSerializer(_ closure: @escaping () -> Void) { - $mutableState.write { mutableState in - mutableState.responseSerializers.append(closure) - - if mutableState.state == .finished { - mutableState.state = .resumed - } - - if mutableState.responseSerializerProcessingFinished { - underlyingQueue.async { self.processNextResponseSerializer() } - } - - if mutableState.state.canTransitionTo(.resumed) { - underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } } - } - } - } - - /// Returns the next response serializer closure to execute if there's one left. - /// - /// - Returns: The next response serialization closure, if there is one. - func nextResponseSerializer() -> (() -> Void)? { - var responseSerializer: (() -> Void)? - - $mutableState.write { mutableState in - let responseSerializerIndex = mutableState.responseSerializerCompletions.count - - if responseSerializerIndex < mutableState.responseSerializers.count { - responseSerializer = mutableState.responseSerializers[responseSerializerIndex] - } - } - - return responseSerializer - } - - /// Processes the next response serializer and calls all completions if response serialization is complete. - func processNextResponseSerializer() { - guard let responseSerializer = nextResponseSerializer() else { - // Execute all response serializer completions and clear them - var completions: [() -> Void] = [] - - $mutableState.write { mutableState in - completions = mutableState.responseSerializerCompletions - - // Clear out all response serializers and response serializer completions in mutable state since the - // request is complete. It's important to do this prior to calling the completion closures in case - // the completions call back into the request triggering a re-processing of the response serializers. - // An example of how this can happen is by calling cancel inside a response completion closure. - mutableState.responseSerializers.removeAll() - mutableState.responseSerializerCompletions.removeAll() - - if mutableState.state.canTransitionTo(.finished) { - mutableState.state = .finished - } - - mutableState.responseSerializerProcessingFinished = true - mutableState.isFinishing = false - } - - completions.forEach { $0() } - - // Cleanup the request - cleanup() - - return - } - - serializationQueue.async { responseSerializer() } - } - - /// Notifies the `Request` that the response serializer is complete. - /// - /// - Parameter completion: The completion handler provided with the response serializer, called when all serializers - /// are complete. - func responseSerializerDidComplete(completion: @escaping () -> Void) { - $mutableState.write { $0.responseSerializerCompletions.append(completion) } - processNextResponseSerializer() - } - - /// Resets all task and response serializer related state for retry. - func reset() { - error = nil - - uploadProgress.totalUnitCount = 0 - uploadProgress.completedUnitCount = 0 - downloadProgress.totalUnitCount = 0 - downloadProgress.completedUnitCount = 0 - - $mutableState.write { state in - state.isFinishing = false - state.responseSerializerCompletions = [] - } - } - - /// Called when updating the upload progress. - /// - /// - Parameters: - /// - totalBytesSent: Total bytes sent so far. - /// - totalBytesExpectedToSend: Total bytes expected to send. - func updateUploadProgress(totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { - uploadProgress.totalUnitCount = totalBytesExpectedToSend - uploadProgress.completedUnitCount = totalBytesSent - - uploadProgressHandler?.queue.async { self.uploadProgressHandler?.handler(self.uploadProgress) } - } - - /// Perform a closure on the current `state` while locked. - /// - /// - Parameter perform: The closure to perform. - func withState(perform: (State) -> Void) { - $mutableState.withState(perform: perform) - } - - // MARK: Task Creation - - /// Called when creating a `URLSessionTask` for this `Request`. Subclasses must override. - /// - /// - Parameters: - /// - request: `URLRequest` to use to create the `URLSessionTask`. - /// - session: `URLSession` which creates the `URLSessionTask`. - /// - /// - Returns: The `URLSessionTask` created. - func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { - fatalError("Subclasses must override.") - } - - // MARK: - Public API - - // These APIs are callable from any queue. - - // MARK: State - - /// Cancels the instance. Once cancelled, a `Request` can no longer be resumed or suspended. - /// - /// - Returns: The instance. - @discardableResult - public func cancel() -> Self { - $mutableState.write { mutableState in - guard mutableState.state.canTransitionTo(.cancelled) else { return } - - mutableState.state = .cancelled - - underlyingQueue.async { self.didCancel() } - - guard let task = mutableState.tasks.last, task.state != .completed else { - underlyingQueue.async { self.finish() } - return - } - - // Resume to ensure metrics are gathered. - task.resume() - task.cancel() - underlyingQueue.async { self.didCancelTask(task) } - } - - return self - } - - /// Suspends the instance. - /// - /// - Returns: The instance. - @discardableResult - public func suspend() -> Self { - $mutableState.write { mutableState in - guard mutableState.state.canTransitionTo(.suspended) else { return } - - mutableState.state = .suspended - - underlyingQueue.async { self.didSuspend() } - - guard let task = mutableState.tasks.last, task.state != .completed else { return } - - task.suspend() - underlyingQueue.async { self.didSuspendTask(task) } - } - - return self - } - - /// Resumes the instance. - /// - /// - Returns: The instance. - @discardableResult - public func resume() -> Self { - $mutableState.write { mutableState in - guard mutableState.state.canTransitionTo(.resumed) else { return } - - mutableState.state = .resumed - - underlyingQueue.async { self.didResume() } - - guard let task = mutableState.tasks.last, task.state != .completed else { return } - - task.resume() - underlyingQueue.async { self.didResumeTask(task) } - } - - return self - } - - // MARK: - Closure API - - /// Associates a credential using the provided values with the instance. - /// - /// - Parameters: - /// - username: The username. - /// - password: The password. - /// - persistence: The `URLCredential.Persistence` for the created `URLCredential`. `.forSession` by default. - /// - /// - Returns: The instance. - @discardableResult - public func authenticate(username: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self { - let credential = URLCredential(user: username, password: password, persistence: persistence) - - return authenticate(with: credential) - } - - /// Associates the provided credential with the instance. - /// - /// - Parameter credential: The `URLCredential`. - /// - /// - Returns: The instance. - @discardableResult - public func authenticate(with credential: URLCredential) -> Self { - mutableState.credential = credential - - return self - } - - /// Sets a closure to be called periodically during the lifecycle of the instance as data is read from the server. - /// - /// - Note: Only the last closure provided is used. - /// - /// - Parameters: - /// - queue: The `DispatchQueue` to execute the closure on. `.main` by default. - /// - closure: The closure to be executed periodically as data is read from the server. - /// - /// - Returns: The instance. - @discardableResult - public func downloadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { - mutableState.downloadProgressHandler = (handler: closure, queue: queue) - - return self - } - - /// Sets a closure to be called periodically during the lifecycle of the instance as data is sent to the server. - /// - /// - Note: Only the last closure provided is used. - /// - /// - Parameters: - /// - queue: The `DispatchQueue` to execute the closure on. `.main` by default. - /// - closure: The closure to be executed periodically as data is sent to the server. - /// - /// - Returns: The instance. - @discardableResult - public func uploadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { - mutableState.uploadProgressHandler = (handler: closure, queue: queue) - - return self - } - - // MARK: Redirects - - /// Sets the redirect handler for the instance which will be used if a redirect response is encountered. - /// - /// - Note: Attempting to set the redirect handler more than once is a logic error and will crash. - /// - /// - Parameter handler: The `RedirectHandler`. - /// - /// - Returns: The instance. - @discardableResult - public func redirect(using handler: RedirectHandler) -> Self { - $mutableState.write { mutableState in - precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.") - mutableState.redirectHandler = handler - } - - return self - } - - // MARK: Cached Responses - - /// Sets the cached response handler for the `Request` which will be used when attempting to cache a response. - /// - /// - Note: Attempting to set the cache handler more than once is a logic error and will crash. - /// - /// - Parameter handler: The `CachedResponseHandler`. - /// - /// - Returns: The instance. - @discardableResult - public func cacheResponse(using handler: CachedResponseHandler) -> Self { - $mutableState.write { mutableState in - precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.") - mutableState.cachedResponseHandler = handler - } - - return self - } - - // MARK: - Lifetime APIs - - /// Sets a handler to be called when the cURL description of the request is available. - /// - /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which `handler` will be called. - /// - handler: Closure to be called when the cURL description is available. - /// - /// - Returns: The instance. - @discardableResult - public func cURLDescription(on queue: DispatchQueue, calling handler: @escaping (String) -> Void) -> Self { - $mutableState.write { mutableState in - if mutableState.requests.last != nil { - queue.async { handler(self.cURLDescription()) } - } else { - mutableState.cURLHandler = (queue, handler) - } - } - - return self - } - - /// Sets a handler to be called when the cURL description of the request is available. - /// - /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called. - /// - /// - Parameter handler: Closure to be called when the cURL description is available. Called on the instance's - /// `underlyingQueue` by default. - /// - /// - Returns: The instance. - @discardableResult - public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self { - $mutableState.write { mutableState in - if mutableState.requests.last != nil { - underlyingQueue.async { handler(self.cURLDescription()) } - } else { - mutableState.cURLHandler = (underlyingQueue, handler) - } - } - - return self - } - - /// Sets a closure to called whenever Alamofire creates a `URLRequest` for this instance. - /// - /// - Note: This closure will be called multiple times if the instance adapts incoming `URLRequest`s or is retried. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which `handler` will be called. `.main` by default. - /// - handler: Closure to be called when a `URLRequest` is available. - /// - /// - Returns: The instance. - @discardableResult - public func onURLRequestCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLRequest) -> Void) -> Self { - $mutableState.write { state in - if let request = state.requests.last { - queue.async { handler(request) } - } - - state.urlRequestHandler = (queue, handler) - } - - return self - } - - /// Sets a closure to be called whenever the instance creates a `URLSessionTask`. - /// - /// - Note: This API should only be used to provide `URLSessionTask`s to existing API, like `NSFileProvider`. It - /// **SHOULD NOT** be used to interact with tasks directly, as that may be break Alamofire features. - /// Additionally, this closure may be called multiple times if the instance is retried. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which `handler` will be called. `.main` by default. - /// - handler: Closure to be called when the `URLSessionTask` is available. - /// - /// - Returns: The instance. - @discardableResult - public func onURLSessionTaskCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLSessionTask) -> Void) -> Self { - $mutableState.write { state in - if let task = state.tasks.last { - queue.async { handler(task) } - } - - state.urlSessionTaskHandler = (queue, handler) - } - - return self - } - - // MARK: Cleanup - - /// Final cleanup step executed when the instance finishes response serialization. - func cleanup() { - delegate?.cleanup(after: self) - // No-op: override in subclass - } -} - -// MARK: - Protocol Conformances - -extension Request: Equatable { - public static func ==(lhs: Request, rhs: Request) -> Bool { - lhs.id == rhs.id - } -} - -extension Request: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(id) - } -} - -extension Request: CustomStringConvertible { - /// A textual representation of this instance, including the `HTTPMethod` and `URL` if the `URLRequest` has been - /// created, as well as the response status code, if a response has been received. - public var description: String { - guard let request = performedRequests.last ?? lastRequest, - let url = request.url, - let method = request.httpMethod else { return "No request created yet." } - - let requestDescription = "\(method) \(url.absoluteString)" - - return response.map { "\(requestDescription) (\($0.statusCode))" } ?? requestDescription - } -} - -extension Request { - /// cURL representation of the instance. - /// - /// - Returns: The cURL equivalent of the instance. - public func cURLDescription() -> String { - guard - let request = lastRequest, - let url = request.url, - let host = url.host, - let method = request.httpMethod else { return "$ curl command could not be created" } - - var components = ["$ curl -v"] - - components.append("-X \(method)") - - if let credentialStorage = delegate?.sessionConfiguration.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 = credential, let user = credential.user, let password = credential.password { - components.append("-u \(user):\(password)") - } - } - } - - if let configuration = delegate?.sessionConfiguration, configuration.httpShouldSetCookies { - if - let cookieStorage = configuration.httpCookieStorage, - let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty { - let allCookies = cookies.map { "\($0.name)=\($0.value)" }.joined(separator: ";") - - components.append("-b \"\(allCookies)\"") - } - } - - var headers = HTTPHeaders() - - if let sessionHeaders = delegate?.sessionConfiguration.headers { - for header in sessionHeaders where header.name != "Cookie" { - headers[header.name] = header.value - } - } - - for header in request.headers where header.name != "Cookie" { - headers[header.name] = header.value - } - - for header in headers { - let escapedValue = header.value.replacingOccurrences(of: "\"", with: "\\\"") - components.append("-H \"\(header.name): \(escapedValue)\"") - } - - if let httpBodyData = request.httpBody { - let httpBody = String(decoding: httpBodyData, as: UTF8.self) - var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"") - escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"") - - components.append("-d \"\(escapedBody)\"") - } - - components.append("\"\(url.absoluteString)\"") - - return components.joined(separator: " \\\n\t") - } -} - -/// Protocol abstraction for `Request`'s communication back to the `SessionDelegate`. -public protocol RequestDelegate: AnyObject { - /// `URLSessionConfiguration` used to create the underlying `URLSessionTask`s. - var sessionConfiguration: URLSessionConfiguration { get } - - /// Determines whether the `Request` should automatically call `resume()` when adding the first response handler. - var startImmediately: Bool { get } - - /// Notifies the delegate the `Request` has reached a point where it needs cleanup. - /// - /// - Parameter request: The `Request` to cleanup after. - func cleanup(after request: Request) - - /// Asynchronously ask the delegate whether a `Request` will be retried. - /// - /// - Parameters: - /// - request: `Request` which failed. - /// - error: `Error` which produced the failure. - /// - completion: Closure taking the `RetryResult` for evaluation. - func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void) - - /// Asynchronously retry the `Request`. - /// - /// - Parameters: - /// - request: `Request` which will be retried. - /// - timeDelay: `TimeInterval` after which the retry will be triggered. - func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?) -} - -// MARK: - Subclasses - -// MARK: - DataRequest - -/// `Request` subclass which handles in-memory `Data` download using `URLSessionDataTask`. -public class DataRequest: Request { - /// `URLRequestConvertible` value used to create `URLRequest`s for this instance. - public let convertible: URLRequestConvertible - /// `Data` read from the server so far. - public var data: Data? { mutableData } - - /// Protected storage for the `Data` read by the instance. - @Protected - private var mutableData: Data? = nil - - /// Creates a `DataRequest` using the provided parameters. - /// - /// - Parameters: - /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. - /// - convertible: `URLRequestConvertible` value used to create `URLRequest`s for this instance. - /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. - /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets - /// `underlyingQueue`, but can be passed another queue from a `Session`. - /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. - /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. - /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. - init(id: UUID = UUID(), - convertible: URLRequestConvertible, - underlyingQueue: DispatchQueue, - serializationQueue: DispatchQueue, - eventMonitor: EventMonitor?, - interceptor: RequestInterceptor?, - delegate: RequestDelegate) { - self.convertible = convertible - - super.init(id: id, - underlyingQueue: underlyingQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: delegate) - } - - override func reset() { - super.reset() - - mutableData = nil - } - - /// Called when `Data` is received by this instance. - /// - /// - Note: Also calls `updateDownloadProgress`. - /// - /// - Parameter data: The `Data` received. - func didReceive(data: Data) { - if self.data == nil { - mutableData = data - } else { - $mutableData.write { $0?.append(data) } - } - - updateDownloadProgress() - } - - override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { - let copiedRequest = request - return session.dataTask(with: copiedRequest) - } - - /// Called to updated the `downloadProgress` of the instance. - func updateDownloadProgress() { - let totalBytesReceived = Int64(data?.count ?? 0) - let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown - - downloadProgress.totalUnitCount = totalBytesExpected - downloadProgress.completedUnitCount = totalBytesReceived - - downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) } - } - - /// Validates the request, using the specified closure. - /// - /// - Note: If validation fails, subsequent calls to response handlers will have an associated error. - /// - /// - Parameter validation: `Validation` closure used to validate the response. - /// - /// - Returns: The instance. - @discardableResult - public func validate(_ validation: @escaping Validation) -> Self { - let validator: () -> Void = { [unowned self] in - guard self.error == nil, let response = self.response else { return } - - let result = validation(self.request, response, self.data) - - if case let .failure(error) = result { self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) } - - self.eventMonitor?.request(self, - didValidateRequest: self.request, - response: response, - data: self.data, - withResult: result) - } - - $validators.write { $0.append(validator) } - - return self - } -} - -// MARK: - DataStreamRequest - -/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure. -public final class DataStreamRequest: Request { - /// Closure type handling `DataStreamRequest.Stream` values. - public typealias Handler = (Stream) throws -> Void - - /// Type encapsulating an `Event` as it flows through the stream, as well as a `CancellationToken` which can be used - /// to stop the stream at any time. - public struct Stream { - /// Latest `Event` from the stream. - public let event: Event - /// Token used to cancel the stream. - public let token: CancellationToken - - /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`. - public func cancel() { - token.cancel() - } - } - - /// Type representing an event flowing through the stream. Contains either the `Result` of processing streamed - /// `Data` or the completion of the stream. - public enum Event { - /// Output produced every time the instance receives additional `Data`. The associated value contains the - /// `Result` of processing the incoming `Data`. - case stream(Result) - /// Output produced when the instance has completed, whether due to stream end, cancellation, or an error. - /// Associated `Completion` value contains the final state. - case complete(Completion) - } - - /// Value containing the state of a `DataStreamRequest` when the stream was completed. - public struct Completion { - /// Last `URLRequest` issued by the instance. - public let request: URLRequest? - /// Last `HTTPURLResponse` received by the instance. - public let response: HTTPURLResponse? - /// Last `URLSessionTaskMetrics` produced for the instance. - public let metrics: URLSessionTaskMetrics? - /// `AFError` produced for the instance, if any. - public let error: AFError? - } - - /// Type used to cancel an ongoing stream. - public struct CancellationToken { - weak var request: DataStreamRequest? - - init(_ request: DataStreamRequest) { - self.request = request - } - - /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`. - public func cancel() { - request?.cancel() - } - } - - /// `URLRequestConvertible` value used to create `URLRequest`s for this instance. - public let convertible: URLRequestConvertible - /// Whether or not the instance will be cancelled if stream parsing encounters an error. - public let automaticallyCancelOnStreamError: Bool - - /// Internal mutable state specific to this type. - struct StreamMutableState { - /// `OutputStream` bound to the `InputStream` produced by `asInputStream`, if it has been called. - var outputStream: OutputStream? - /// Stream closures called as `Data` is received. - var streams: [(_ data: Data) -> Void] = [] - /// Number of currently executing streams. Used to ensure completions are only fired after all streams are - /// enqueued. - var numberOfExecutingStreams = 0 - /// Completion calls enqueued while streams are still executing. - var enqueuedCompletionEvents: [() -> Void] = [] - } - - @Protected - var streamMutableState = StreamMutableState() - - /// Creates a `DataStreamRequest` using the provided parameters. - /// - /// - Parameters: - /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` - /// by default. - /// - convertible: `URLRequestConvertible` value used to create `URLRequest`s for this - /// instance. - /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance will be cancelled when an `Error` - /// is thrown while serializing stream `Data`. - /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. - /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default - /// targets - /// `underlyingQueue`, but can be passed another queue from a `Session`. - /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. - /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. - /// - delegate: `RequestDelegate` that provides an interface to actions not performed by - /// the `Request`. - init(id: UUID = UUID(), - convertible: URLRequestConvertible, - automaticallyCancelOnStreamError: Bool, - underlyingQueue: DispatchQueue, - serializationQueue: DispatchQueue, - eventMonitor: EventMonitor?, - interceptor: RequestInterceptor?, - delegate: RequestDelegate) { - self.convertible = convertible - self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError - - super.init(id: id, - underlyingQueue: underlyingQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: delegate) - } - - override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { - let copiedRequest = request - return session.dataTask(with: copiedRequest) - } - - override func finish(error: AFError? = nil) { - $streamMutableState.write { state in - state.outputStream?.close() - } - - super.finish(error: error) - } - - func didReceive(data: Data) { - $streamMutableState.write { state in - if let stream = state.outputStream { - underlyingQueue.async { - var bytes = Array(data) - stream.write(&bytes, maxLength: bytes.count) - } - } - state.numberOfExecutingStreams += state.streams.count - let localState = state - underlyingQueue.async { localState.streams.forEach { $0(data) } } - } - } - - /// Validates the `URLRequest` and `HTTPURLResponse` received for the instance using the provided `Validation` closure. - /// - /// - Parameter validation: `Validation` closure used to validate the request and response. - /// - /// - Returns: The `DataStreamRequest`. - @discardableResult - public func validate(_ validation: @escaping Validation) -> Self { - let validator: () -> Void = { [unowned self] in - guard self.error == nil, let response = self.response else { return } - - let result = validation(self.request, response) - - if case let .failure(error) = result { - self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) - } - - self.eventMonitor?.request(self, - didValidateRequest: self.request, - response: response, - withResult: result) - } - - $validators.write { $0.append(validator) } - - return self - } - - /// Produces an `InputStream` that receives the `Data` received by the instance. - /// - /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`. - /// Additionally, this method will automatically call `resume()` on the instance, regardless of whether or - /// not the creating session has `startRequestsImmediately` set to `true`. - /// - /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`. - /// - /// - Returns: The `InputStream` bound to the internal `OutboundStream`. - public func asInputStream(bufferSize: Int = 1024) -> InputStream? { - defer { resume() } - - var inputStream: InputStream? - $streamMutableState.write { state in - Foundation.Stream.getBoundStreams(withBufferSize: bufferSize, - inputStream: &inputStream, - outputStream: &state.outputStream) - state.outputStream?.open() - } - - return inputStream - } - - func capturingError(from closure: () throws -> Void) { - do { - try closure() - } catch { - self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) - cancel() - } - } - - func appendStreamCompletion(on queue: DispatchQueue, - stream: @escaping Handler) { - appendResponseSerializer { - self.underlyingQueue.async { - self.responseSerializerDidComplete { - self.$streamMutableState.write { state in - guard state.numberOfExecutingStreams == 0 else { - state.enqueuedCompletionEvents.append { - self.enqueueCompletion(on: queue, stream: stream) - } - - return - } - - self.enqueueCompletion(on: queue, stream: stream) - } - } - } - } - } - - func enqueueCompletion(on queue: DispatchQueue, - stream: @escaping Handler) { - queue.async { - do { - let completion = Completion(request: self.request, - response: self.response, - metrics: self.metrics, - error: self.error) - try stream(.init(event: .complete(completion), token: .init(self))) - } catch { - // Ignore error, as errors on Completion can't be handled anyway. - } - } - } -} - -extension DataStreamRequest.Stream { - /// Incoming `Result` values from `Event.stream`. - public var result: Result? { - guard case let .stream(result) = event else { return nil } - - return result - } - - /// `Success` value of the instance, if any. - public var value: Success? { - guard case let .success(value) = result else { return nil } - - return value - } - - /// `Failure` value of the instance, if any. - public var error: Failure? { - guard case let .failure(error) = result else { return nil } - - return error - } - - /// `Completion` value of the instance, if any. - public var completion: DataStreamRequest.Completion? { - guard case let .complete(completion) = event else { return nil } - - return completion - } -} - -// MARK: - DownloadRequest - -/// `Request` subclass which downloads `Data` to a file on disk using `URLSessionDownloadTask`. -public class DownloadRequest: Request { - /// A set of options to be executed prior to moving a downloaded file from the temporary `URL` to the destination - /// `URL`. - public struct Options: OptionSet { - /// Specifies that intermediate directories for the destination URL should be created. - public static let createIntermediateDirectories = Options(rawValue: 1 << 0) - /// Specifies that any previous file at the destination `URL` should be removed. - public static let removePreviousFile = Options(rawValue: 1 << 1) - - public let rawValue: Int - - public init(rawValue: Int) { - self.rawValue = rawValue - } - } - - // MARK: Destination - - /// A closure executed once a `DownloadRequest` 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 `HTTPURLResponse`, and returns two values: the file URL where the temporary file should be moved and - /// the options defining how the file should be moved. - /// - /// - Note: Downloads from a local `file://` `URL`s do not use the `Destination` closure, as those downloads do not - /// return an `HTTPURLResponse`. Instead the file is merely moved within the temporary directory. - public typealias Destination = (_ temporaryURL: URL, - _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options) - - /// 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. - /// - /// - Parameters: - /// - directory: The search path directory. `.documentDirectory` by default. - /// - domain: The search path domain mask. `.userDomainMask` by default. - /// - options: `DownloadRequest.Options` used when moving the downloaded file to its destination. None by - /// default. - /// - Returns: The `Destination` closure. - public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory, - in domain: FileManager.SearchPathDomainMask = .userDomainMask, - options: Options = []) -> Destination { - { temporaryURL, response in - let directoryURLs = FileManager.default.urls(for: directory, in: domain) - let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL - - return (url, options) - } - } - - /// Default `Destination` used by Alamofire to ensure all downloads persist. This `Destination` prepends - /// `Alamofire_` to the automatically generated download name and moves it within the temporary directory. Files - /// with this destination must be additionally moved if they should survive the system reclamation of temporary - /// space. - static let defaultDestination: Destination = { url, _ in - (defaultDestinationURL(url), []) - } - - /// Default `URL` creation closure. Creates a `URL` in the temporary directory with `Alamofire_` prepended to the - /// provided file name. - static let defaultDestinationURL: (URL) -> URL = { url in - let filename = "Alamofire_\(url.lastPathComponent)" - let destination = url.deletingLastPathComponent().appendingPathComponent(filename) - - return destination - } - - // MARK: Downloadable - - /// Type describing the source used to create the underlying `URLSessionDownloadTask`. - public enum Downloadable { - /// Download should be started from the `URLRequest` produced by the associated `URLRequestConvertible` value. - case request(URLRequestConvertible) - /// Download should be started from the associated resume `Data` value. - case resumeData(Data) - } - - // MARK: Mutable State - - /// Type containing all mutable state for `DownloadRequest` instances. - private struct DownloadRequestMutableState { - /// Possible resume `Data` produced when cancelling the instance. - var resumeData: Data? - /// `URL` to which `Data` is being downloaded. - var fileURL: URL? - } - - /// Protected mutable state specific to `DownloadRequest`. - @Protected - private var mutableDownloadState = DownloadRequestMutableState() - - /// If the download is resumable and is eventually cancelled or fails, this value may be used to resume the download - /// using the `download(resumingWith data:)` API. - /// - /// - Note: For more information about `resumeData`, see [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel). - public var resumeData: Data? { mutableDownloadState.resumeData ?? error?.downloadResumeData } - /// If the download is successful, the `URL` where the file was downloaded. - public var fileURL: URL? { mutableDownloadState.fileURL } - - // MARK: Initial State - - /// `Downloadable` value used for this instance. - public let downloadable: Downloadable - /// The `Destination` to which the downloaded file is moved. - let destination: Destination - - /// Creates a `DownloadRequest` using the provided parameters. - /// - /// - Parameters: - /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. - /// - downloadable: `Downloadable` value used to create `URLSessionDownloadTasks` for the instance. - /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. - /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets - /// `underlyingQueue`, but can be passed another queue from a `Session`. - /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. - /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. - /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request` - /// - destination: `Destination` closure used to move the downloaded file to its final location. - init(id: UUID = UUID(), - downloadable: Downloadable, - underlyingQueue: DispatchQueue, - serializationQueue: DispatchQueue, - eventMonitor: EventMonitor?, - interceptor: RequestInterceptor?, - delegate: RequestDelegate, - destination: @escaping Destination) { - self.downloadable = downloadable - self.destination = destination - - super.init(id: id, - underlyingQueue: underlyingQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: delegate) - } - - override func reset() { - super.reset() - - $mutableDownloadState.write { - $0.resumeData = nil - $0.fileURL = nil - } - } - - /// Called when a download has finished. - /// - /// - Parameters: - /// - task: `URLSessionTask` that finished the download. - /// - result: `Result` of the automatic move to `destination`. - func didFinishDownloading(using task: URLSessionTask, with result: Result) { - eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result) - - switch result { - case let .success(url): mutableDownloadState.fileURL = url - case let .failure(error): self.error = error - } - } - - /// Updates the `downloadProgress` using the provided values. - /// - /// - Parameters: - /// - bytesWritten: Total bytes written so far. - /// - totalBytesExpectedToWrite: Total bytes expected to write. - func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) { - downloadProgress.totalUnitCount = totalBytesExpectedToWrite - downloadProgress.completedUnitCount += bytesWritten - - downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) } - } - - override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { - session.downloadTask(with: request) - } - - /// Creates a `URLSessionTask` from the provided resume data. - /// - /// - Parameters: - /// - data: `Data` used to resume the download. - /// - session: `URLSession` used to create the `URLSessionTask`. - /// - /// - Returns: The `URLSessionTask` created. - public func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask { - session.downloadTask(withResumeData: data) - } - - /// Cancels the instance. Once cancelled, a `DownloadRequest` can no longer be resumed or suspended. - /// - /// - Note: This method will NOT produce resume data. If you wish to cancel and produce resume data, use - /// `cancel(producingResumeData:)` or `cancel(byProducingResumeData:)`. - /// - /// - Returns: The instance. - @discardableResult - override public func cancel() -> Self { - cancel(producingResumeData: false) - } - - /// Cancels the instance, optionally producing resume data. Once cancelled, a `DownloadRequest` can no longer be - /// resumed or suspended. - /// - /// - Note: If `producingResumeData` is `true`, the `resumeData` property will be populated with any resume data, if - /// available. - /// - /// - Returns: The instance. - @discardableResult - public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self { - cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil) - } - - /// Cancels the instance while producing resume data. Once cancelled, a `DownloadRequest` can no longer be resumed - /// or suspended. - /// - /// - Note: The resume data passed to the completion handler will also be available on the instance's `resumeData` - /// property. - /// - /// - Parameter completionHandler: The completion handler that is called when the download has been successfully - /// cancelled. It is not guaranteed to be called on a particular queue, so you may - /// want use an appropriate queue to perform your work. - /// - /// - Returns: The instance. - @discardableResult - public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void) -> Self { - cancel(optionallyProducingResumeData: completionHandler) - } - - /// Internal implementation of cancellation that optionally takes a resume data handler. If no handler is passed, - /// cancellation is performed without producing resume data. - /// - /// - Parameter completionHandler: Optional resume data handler. - /// - /// - Returns: The instance. - private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self { - $mutableState.write { mutableState in - guard mutableState.state.canTransitionTo(.cancelled) else { return } - - mutableState.state = .cancelled - - underlyingQueue.async { self.didCancel() } - - guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state != .completed else { - underlyingQueue.async { self.finish() } - return - } - - if let completionHandler = completionHandler { - // Resume to ensure metrics are gathered. - task.resume() - task.cancel { resumeData in - self.mutableDownloadState.resumeData = resumeData - self.underlyingQueue.async { self.didCancelTask(task) } - completionHandler(resumeData) - } - } else { - // Resume to ensure metrics are gathered. - task.resume() - task.cancel(byProducingResumeData: { _ in }) - self.underlyingQueue.async { self.didCancelTask(task) } - } - } - - return self - } - - /// Validates the request, using the specified closure. - /// - /// - Note: If validation fails, subsequent calls to response handlers will have an associated error. - /// - /// - Parameter validation: `Validation` closure to validate the response. - /// - /// - Returns: The instance. - @discardableResult - public func validate(_ validation: @escaping Validation) -> Self { - let validator: () -> Void = { [unowned self] in - guard self.error == nil, let response = self.response else { return } - - let result = validation(self.request, response, self.fileURL) - - if case let .failure(error) = result { - self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) - } - - self.eventMonitor?.request(self, - didValidateRequest: self.request, - response: response, - fileURL: self.fileURL, - withResult: result) - } - - $validators.write { $0.append(validator) } - - return self - } -} - -// MARK: - UploadRequest - -/// `DataRequest` subclass which handles `Data` upload from memory, file, or stream using `URLSessionUploadTask`. -public class UploadRequest: DataRequest { - /// Type describing the origin of the upload, whether `Data`, file, or stream. - public enum Uploadable { - /// Upload from the provided `Data` value. - case data(Data) - /// Upload from the provided file `URL`, as well as a `Bool` determining whether the source file should be - /// automatically removed once uploaded. - case file(URL, shouldRemove: Bool) - /// Upload from the provided `InputStream`. - case stream(InputStream) - } - - // MARK: Initial State - - /// The `UploadableConvertible` value used to produce the `Uploadable` value for this instance. - public let upload: UploadableConvertible - - /// `FileManager` used to perform cleanup tasks, including the removal of multipart form encoded payloads written - /// to disk. - public let fileManager: FileManager - - // MARK: Mutable State - - /// `Uploadable` value used by the instance. - public var uploadable: Uploadable? - - /// Creates an `UploadRequest` using the provided parameters. - /// - /// - Parameters: - /// - id: `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default. - /// - convertible: `UploadConvertible` value used to determine the type of upload to be performed. - /// - underlyingQueue: `DispatchQueue` on which all internal `Request` work is performed. - /// - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets - /// `underlyingQueue`, but can be passed another queue from a `Session`. - /// - eventMonitor: `EventMonitor` called for event callbacks from internal `Request` actions. - /// - interceptor: `RequestInterceptor` used throughout the request lifecycle. - /// - delegate: `RequestDelegate` that provides an interface to actions not performed by the `Request`. - init(id: UUID = UUID(), - convertible: UploadConvertible, - underlyingQueue: DispatchQueue, - serializationQueue: DispatchQueue, - eventMonitor: EventMonitor?, - interceptor: RequestInterceptor?, - fileManager: FileManager, - delegate: RequestDelegate) { - upload = convertible - self.fileManager = fileManager - - super.init(id: id, - convertible: convertible, - underlyingQueue: underlyingQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: delegate) - } - - /// Called when the `Uploadable` value has been created from the `UploadConvertible`. - /// - /// - Parameter uploadable: The `Uploadable` that was created. - func didCreateUploadable(_ uploadable: Uploadable) { - self.uploadable = uploadable - - eventMonitor?.request(self, didCreateUploadable: uploadable) - } - - /// Called when the `Uploadable` value could not be created. - /// - /// - Parameter error: `AFError` produced by the failure. - func didFailToCreateUploadable(with error: AFError) { - self.error = error - - eventMonitor?.request(self, didFailToCreateUploadableWithError: error) - - retryOrFinish(error: error) - } - - override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask { - guard let uploadable = uploadable else { - fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.") - } - - switch uploadable { - case let .data(data): return session.uploadTask(with: request, from: data) - case let .file(url, _): return session.uploadTask(with: request, fromFile: url) - case .stream: return session.uploadTask(withStreamedRequest: request) - } - } - - override func reset() { - // Uploadable must be recreated on every retry. - uploadable = nil - - super.reset() - } - - /// Produces the `InputStream` from `uploadable`, if it can. - /// - /// - Note: Calling this method with a non-`.stream` `Uploadable` is a logic error and will crash. - /// - /// - Returns: The `InputStream`. - func inputStream() -> InputStream { - guard let uploadable = uploadable else { - fatalError("Attempting to access the input stream but the uploadable doesn't exist.") - } - - guard case let .stream(stream) = uploadable else { - fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.") - } - - eventMonitor?.request(self, didProvideInputStream: stream) - - return stream - } - - override public func cleanup() { - defer { super.cleanup() } - - guard - let uploadable = self.uploadable, - case let .file(url, shouldRemove) = uploadable, - shouldRemove - else { return } - - try? fileManager.removeItem(at: url) - } -} - -/// A type that can produce an `UploadRequest.Uploadable` value. -public protocol UploadableConvertible { - /// Produces an `UploadRequest.Uploadable` value from the instance. - /// - /// - Returns: The `UploadRequest.Uploadable`. - /// - Throws: Any `Error` produced during creation. - func createUploadable() throws -> UploadRequest.Uploadable -} - -extension UploadRequest.Uploadable: UploadableConvertible { - public func createUploadable() throws -> UploadRequest.Uploadable { - self - } -} - -/// A type that can be converted to an upload, whether from an `UploadRequest.Uploadable` or `URLRequestConvertible`. -public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift deleted file mode 100644 index 1912e9c1..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestInterceptor.swift +++ /dev/null @@ -1,244 +0,0 @@ -// -// RequestInterceptor.swift -// -// Copyright (c) 2019 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 and calls the completion handler with the Result. - /// - /// - Parameters: - /// - urlRequest: The `URLRequest` to adapt. - /// - session: The `Session` that will execute the `URLRequest`. - /// - completion: The completion handler that must be called when adaptation is complete. - func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) -} - -// MARK: - - -/// Outcome of determination whether retry is necessary. -public enum RetryResult { - /// Retry should be attempted immediately. - case retry - /// Retry should be attempted after the associated `TimeInterval`. - case retryWithDelay(TimeInterval) - /// Do not retry. - case doNotRetry - /// Do not retry due to the associated `Error`. - case doNotRetryWithError(Error) -} - -extension RetryResult { - var retryRequired: Bool { - switch self { - case .retry, .retryWithDelay: return true - default: return false - } - } - - var delay: TimeInterval? { - switch self { - case let .retryWithDelay(delay): return delay - default: return nil - } - } - - var error: Error? { - guard case let .doNotRetryWithError(error) = self else { return nil } - return error - } -} - -/// 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. - /// - /// - Parameters: - /// - request: `Request` that failed due to the provided `Error`. - /// - session: `Session` that produced the `Request`. - /// - error: `Error` encountered while executing the `Request`. - /// - completion: Completion closure to be executed when a retry decision has been determined. - func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) -} - -// MARK: - - -/// Type that provides both `RequestAdapter` and `RequestRetrier` functionality. -public protocol RequestInterceptor: RequestAdapter, RequestRetrier {} - -extension RequestInterceptor { - public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - completion(.success(urlRequest)) - } - - public func retry(_ request: Request, - for session: Session, - dueTo error: Error, - completion: @escaping (RetryResult) -> Void) { - completion(.doNotRetry) - } -} - -/// `RequestAdapter` closure definition. -public typealias AdaptHandler = (URLRequest, Session, _ completion: @escaping (Result) -> Void) -> Void -/// `RequestRetrier` closure definition. -public typealias RetryHandler = (Request, Session, Error, _ completion: @escaping (RetryResult) -> Void) -> Void - -// MARK: - - -/// Closure-based `RequestAdapter`. -open class Adapter: RequestInterceptor { - private let adaptHandler: AdaptHandler - - /// Creates an instance using the provided closure. - /// - /// - Parameter adaptHandler: `AdaptHandler` closure to be executed when handling request adaptation. - public init(_ adaptHandler: @escaping AdaptHandler) { - self.adaptHandler = adaptHandler - } - - open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - adaptHandler(urlRequest, session, completion) - } -} - -// MARK: - - -/// Closure-based `RequestRetrier`. -open class Retrier: RequestInterceptor { - private let retryHandler: RetryHandler - - /// Creates an instance using the provided closure. - /// - /// - Parameter retryHandler: `RetryHandler` closure to be executed when handling request retry. - public init(_ retryHandler: @escaping RetryHandler) { - self.retryHandler = retryHandler - } - - open func retry(_ request: Request, - for session: Session, - dueTo error: Error, - completion: @escaping (RetryResult) -> Void) { - retryHandler(request, session, error, completion) - } -} - -// MARK: - - -/// `RequestInterceptor` which can use multiple `RequestAdapter` and `RequestRetrier` values. -open class Interceptor: RequestInterceptor { - /// All `RequestAdapter`s associated with the instance. These adapters will be run until one fails. - public let adapters: [RequestAdapter] - /// All `RequestRetrier`s associated with the instance. These retriers will be run one at a time until one triggers retry. - public let retriers: [RequestRetrier] - - /// Creates an instance from `AdaptHandler` and `RetryHandler` closures. - /// - /// - Parameters: - /// - adaptHandler: `AdaptHandler` closure to be used. - /// - retryHandler: `RetryHandler` closure to be used. - public init(adaptHandler: @escaping AdaptHandler, retryHandler: @escaping RetryHandler) { - adapters = [Adapter(adaptHandler)] - retriers = [Retrier(retryHandler)] - } - - /// Creates an instance from `RequestAdapter` and `RequestRetrier` values. - /// - /// - Parameters: - /// - adapter: `RequestAdapter` value to be used. - /// - retrier: `RequestRetrier` value to be used. - public init(adapter: RequestAdapter, retrier: RequestRetrier) { - adapters = [adapter] - retriers = [retrier] - } - - /// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values. - /// - /// - Parameters: - /// - adapters: `RequestAdapter` values to be used. - /// - retriers: `RequestRetrier` values to be used. - /// - interceptors: `RequestInterceptor`s to be used. - public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = [], interceptors: [RequestInterceptor] = []) { - self.adapters = adapters + interceptors - self.retriers = retriers + interceptors - } - - open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - adapt(urlRequest, for: session, using: adapters, completion: completion) - } - - private func adapt(_ urlRequest: URLRequest, - for session: Session, - using adapters: [RequestAdapter], - completion: @escaping (Result) -> Void) { - var pendingAdapters = adapters - - guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return } - - let adapter = pendingAdapters.removeFirst() - - adapter.adapt(urlRequest, for: session) { result in - switch result { - case let .success(urlRequest): - self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion) - case .failure: - completion(result) - } - } - } - - open func retry(_ request: Request, - for session: Session, - dueTo error: Error, - completion: @escaping (RetryResult) -> Void) { - retry(request, for: session, dueTo: error, using: retriers, completion: completion) - } - - private func retry(_ request: Request, - for session: Session, - dueTo error: Error, - using retriers: [RequestRetrier], - completion: @escaping (RetryResult) -> Void) { - var pendingRetriers = retriers - - guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return } - - let retrier = pendingRetriers.removeFirst() - - retrier.retry(request, for: session, dueTo: error) { result in - switch result { - case .retry, .retryWithDelay, .doNotRetryWithError: - completion(result) - case .doNotRetry: - // Only continue to the next retrier if retry was not triggered and no error was encountered - self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion) - } - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift deleted file mode 100644 index 85b58f37..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/RequestTaskMap.swift +++ /dev/null @@ -1,149 +0,0 @@ -// -// RequestTaskMap.swift -// -// Copyright (c) 2014-2018 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 maintains a two way, one to one map of `URLSessionTask`s to `Request`s. -struct RequestTaskMap { - private typealias Events = (completed: Bool, metricsGathered: Bool) - - private var tasksToRequests: [URLSessionTask: Request] - private var requestsToTasks: [Request: URLSessionTask] - private var taskEvents: [URLSessionTask: Events] - - var requests: [Request] { - Array(tasksToRequests.values) - } - - init(tasksToRequests: [URLSessionTask: Request] = [:], - requestsToTasks: [Request: URLSessionTask] = [:], - taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) { - self.tasksToRequests = tasksToRequests - self.requestsToTasks = requestsToTasks - self.taskEvents = taskEvents - } - - subscript(_ request: Request) -> URLSessionTask? { - get { requestsToTasks[request] } - set { - guard let newValue = newValue else { - guard let task = requestsToTasks[request] else { - fatalError("RequestTaskMap consistency error: no task corresponding to request found.") - } - - requestsToTasks.removeValue(forKey: request) - tasksToRequests.removeValue(forKey: task) - taskEvents.removeValue(forKey: task) - - return - } - - requestsToTasks[request] = newValue - tasksToRequests[newValue] = request - taskEvents[newValue] = (completed: false, metricsGathered: false) - } - } - - subscript(_ task: URLSessionTask) -> Request? { - get { tasksToRequests[task] } - set { - guard let newValue = newValue else { - guard let request = tasksToRequests[task] else { - fatalError("RequestTaskMap consistency error: no request corresponding to task found.") - } - - tasksToRequests.removeValue(forKey: task) - requestsToTasks.removeValue(forKey: request) - taskEvents.removeValue(forKey: task) - - return - } - - tasksToRequests[task] = newValue - requestsToTasks[newValue] = task - taskEvents[task] = (completed: false, metricsGathered: false) - } - } - - var count: Int { - precondition(tasksToRequests.count == requestsToTasks.count, - "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)") - - return tasksToRequests.count - } - - var eventCount: Int { - precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)") - - return taskEvents.count - } - - var isEmpty: Bool { - precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty, - "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)") - - return tasksToRequests.isEmpty - } - - var isEventsEmpty: Bool { - precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)") - - return taskEvents.isEmpty - } - - mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool { - guard let events = taskEvents[task] else { - fatalError("RequestTaskMap consistency error: no events corresponding to task found.") - } - - switch (events.completed, events.metricsGathered) { - case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.") - case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false - case (true, false): self[task] = nil; return true - } - } - - mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool { - guard let events = taskEvents[task] else { - fatalError("RequestTaskMap consistency error: no events corresponding to task found.") - } - - switch (events.completed, events.metricsGathered) { - case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.") - #if os(Linux) // Linux doesn't gather metrics, so unconditionally remove the reference and return true. - default: self[task] = nil; return true - #else - case (false, false): - if #available(macOS 10.12, iOS 10, watchOS 7, tvOS 10, *) { - taskEvents[task] = (completed: true, metricsGathered: false); return false - } else { - // watchOS < 7 doesn't gather metrics, so unconditionally remove the reference and return true. - self[task] = nil; return true - } - case (false, true): - self[task] = nil; return true - #endif - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift deleted file mode 100644 index 92e9c44e..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Response.swift +++ /dev/null @@ -1,454 +0,0 @@ -// -// Response.swift -// -// Copyright (c) 2014-2018 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 - -/// Default type of `DataResponse` returned by Alamofire, with an `AFError` `Failure` type. -public typealias AFDataResponse = DataResponse -/// Default type of `DownloadResponse` returned by Alamofire, with an `AFError` `Failure` type. -public typealias AFDownloadResponse = DownloadResponse - -/// Type used to store all values associated with a serialized response of a `DataRequest` or `UploadRequest`. -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 final metrics of the response. - /// - /// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.` - /// - public let metrics: URLSessionTaskMetrics? - - /// The time taken to serialize the response. - public let serializationDuration: TimeInterval - - /// The result of response serialization. - public let result: Result - - /// Returns the associated value of the result if it is a success, `nil` otherwise. - public var value: Success? { result.success } - - /// Returns the associated error value if the result if it is a failure, `nil` otherwise. - public var error: Failure? { result.failure } - - /// Creates a `DataResponse` instance with the specified parameters derived from the response serialization. - /// - /// - Parameters: - /// - request: The `URLRequest` sent to the server. - /// - response: The `HTTPURLResponse` from the server. - /// - data: The `Data` returned by the server. - /// - metrics: The `URLSessionTaskMetrics` of the `DataRequest` or `UploadRequest`. - /// - serializationDuration: The duration taken by serialization. - /// - result: The `Result` of response serialization. - public init(request: URLRequest?, - response: HTTPURLResponse?, - data: Data?, - metrics: URLSessionTaskMetrics?, - serializationDuration: TimeInterval, - result: Result) { - self.request = request - self.response = response - self.data = data - self.metrics = metrics - self.serializationDuration = serializationDuration - self.result = result - } -} - -// 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 { - "\(result)" - } - - /// The debug textual representation used when written to an output stream, which includes (if available) a summary - /// of the `URLRequest`, the request's headers and body (if decodable as a `String` below 100KB); the - /// `HTTPURLResponse`'s status code, headers, and body; the duration of the network and serialization actions; and - /// the `Result` of serialization. - public var debugDescription: String { - guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" } - - let requestDescription = DebugDescription.description(of: urlRequest) - - let responseDescription = response.map { response in - let responseBodyDescription = DebugDescription.description(for: data, headers: response.headers) - - return """ - \(DebugDescription.description(of: response)) - \(responseBodyDescription.indentingNewlines()) - """ - } ?? "[Response]: None" - - let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None" - - return """ - \(requestDescription) - \(responseDescription) - [Network Duration]: \(networkDuration) - [Serialization Duration]: \(serializationDuration)s - [Result]: \(result) - """ - } -} - -// 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: (Success) -> NewSuccess) -> DataResponse { - DataResponse(request: request, - response: response, - data: data, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.map(transform)) - } - - /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result - /// value as a parameter. - /// - /// Use the `tryMap` method with a closure that may throw an error. For example: - /// - /// let possibleData: DataResponse = ... - /// let possibleObject = possibleData.tryMap { - /// 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 tryMap(_ transform: (Success) throws -> NewSuccess) -> DataResponse { - DataResponse(request: request, - response: response, - data: data, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.tryMap(transform)) - } - - /// Evaluates the specified closure when the `DataResponse` 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: DataResponse = ... - /// let withMyError = possibleData.mapError { MyError.error($0) } - /// - /// - Parameter transform: A closure that takes the error of the instance. - /// - /// - Returns: A `DataResponse` instance containing the result of the transform. - public func mapError(_ transform: (Failure) -> NewFailure) -> DataResponse { - DataResponse(request: request, - response: response, - data: data, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.mapError(transform)) - } - - /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter. - /// - /// Use the `tryMapError` function with a closure that may throw an error. For example: - /// - /// let possibleData: DataResponse = ... - /// let possibleObject = possibleData.tryMapError { - /// try someFailableFunction(taking: $0) - /// } - /// - /// - Parameter transform: A throwing closure that takes the error of the instance. - /// - /// - Returns: A `DataResponse` instance containing the result of the transform. - public func tryMapError(_ transform: (Failure) throws -> NewFailure) -> DataResponse { - DataResponse(request: request, - response: response, - data: data, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.tryMapError(transform)) - } -} - -// 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 final destination URL of the data returned from the server after it is moved. - public let fileURL: URL? - - /// The resume data generated if the request was cancelled. - public let resumeData: Data? - - /// The final metrics of the response. - /// - /// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.` - /// - public let metrics: URLSessionTaskMetrics? - - /// The time taken to serialize the response. - public let serializationDuration: TimeInterval - - /// The result of response serialization. - public let result: Result - - /// Returns the associated value of the result if it is a success, `nil` otherwise. - public var value: Success? { result.success } - - /// Returns the associated error value if the result if it is a failure, `nil` otherwise. - public var error: Failure? { result.failure } - - /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. - /// - /// - Parameters: - /// - request: The `URLRequest` sent to the server. - /// - response: The `HTTPURLResponse` from the server. - /// - 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. - /// - metrics: The `URLSessionTaskMetrics` of the `DownloadRequest`. - /// - serializationDuration: The duration taken by serialization. - /// - result: The `Result` of response serialization. - public init(request: URLRequest?, - response: HTTPURLResponse?, - fileURL: URL?, - resumeData: Data?, - metrics: URLSessionTaskMetrics?, - serializationDuration: TimeInterval, - result: Result) { - self.request = request - self.response = response - self.fileURL = fileURL - self.resumeData = resumeData - self.metrics = metrics - self.serializationDuration = serializationDuration - self.result = result - } -} - -// 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 { - "\(result)" - } - - /// 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 durations of the network and serialization - /// actions, and the response serialization result. - public var debugDescription: String { - guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" } - - let requestDescription = DebugDescription.description(of: urlRequest) - let responseDescription = response.map(DebugDescription.description(of:)) ?? "[Response]: None" - let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None" - let resumeDataDescription = resumeData.map { "\($0)" } ?? "None" - - return """ - \(requestDescription) - \(responseDescription) - [File URL]: \(fileURL?.path ?? "None") - [Resume Data]: \(resumeDataDescription) - [Network Duration]: \(networkDuration) - [Serialization Duration]: \(serializationDuration)s - [Result]: \(result) - """ - } -} - -// 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: (Success) -> NewSuccess) -> DownloadResponse { - DownloadResponse(request: request, - response: response, - fileURL: fileURL, - resumeData: resumeData, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.map(transform)) - } - - /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped - /// result value as a parameter. - /// - /// Use the `tryMap` method with a closure that may throw an error. For example: - /// - /// let possibleData: DownloadResponse = ... - /// let possibleObject = possibleData.tryMap { - /// 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 tryMap(_ transform: (Success) throws -> NewSuccess) -> DownloadResponse { - DownloadResponse(request: request, - response: response, - fileURL: fileURL, - resumeData: resumeData, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.tryMap(transform)) - } - - /// Evaluates the specified closure when the `DownloadResponse` 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: DownloadResponse = ... - /// let withMyError = possibleData.mapError { MyError.error($0) } - /// - /// - Parameter transform: A closure that takes the error of the instance. - /// - /// - Returns: A `DownloadResponse` instance containing the result of the transform. - public func mapError(_ transform: (Failure) -> NewFailure) -> DownloadResponse { - DownloadResponse(request: request, - response: response, - fileURL: fileURL, - resumeData: resumeData, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.mapError(transform)) - } - - /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. - /// - /// Use the `tryMapError` function with a closure that may throw an error. For example: - /// - /// let possibleData: DownloadResponse = ... - /// let possibleObject = possibleData.tryMapError { - /// try someFailableFunction(taking: $0) - /// } - /// - /// - Parameter transform: A throwing closure that takes the error of the instance. - /// - /// - Returns: A `DownloadResponse` instance containing the result of the transform. - public func tryMapError(_ transform: (Failure) throws -> NewFailure) -> DownloadResponse { - DownloadResponse(request: request, - response: response, - fileURL: fileURL, - resumeData: resumeData, - metrics: metrics, - serializationDuration: serializationDuration, - result: result.tryMapError(transform)) - } -} - -private enum DebugDescription { - static func description(of request: URLRequest) -> String { - let requestSummary = "\(request.httpMethod!) \(request)" - let requestHeadersDescription = DebugDescription.description(for: request.headers) - let requestBodyDescription = DebugDescription.description(for: request.httpBody, headers: request.headers) - - return """ - [Request]: \(requestSummary) - \(requestHeadersDescription.indentingNewlines()) - \(requestBodyDescription.indentingNewlines()) - """ - } - - static func description(of response: HTTPURLResponse) -> String { - """ - [Response]: - [Status Code]: \(response.statusCode) - \(DebugDescription.description(for: response.headers).indentingNewlines()) - """ - } - - static func description(for headers: HTTPHeaders) -> String { - guard !headers.isEmpty else { return "[Headers]: None" } - - let headerDescription = "\(headers.sorted())".indentingNewlines() - return """ - [Headers]: - \(headerDescription) - """ - } - - static func description(for data: Data?, - headers: HTTPHeaders, - allowingPrintableTypes printableTypes: [String] = ["json", "xml", "text"], - maximumLength: Int = 100_000) -> String { - guard let data = data, !data.isEmpty else { return "[Body]: None" } - - guard - data.count <= maximumLength, - printableTypes.compactMap({ headers["Content-Type"]?.contains($0) }).contains(true) - else { return "[Body]: \(data.count) bytes" } - - return """ - [Body]: - \(String(decoding: data, as: UTF8.self) - .trimmingCharacters(in: .whitespacesAndNewlines) - .indentingNewlines()) - """ - } -} - -extension String { - fileprivate func indentingNewlines(by spaceCount: Int = 4) -> String { - let spaces = String(repeating: " ", count: spaceCount) - return replacingOccurrences(of: "\n", with: "\n\(spaces)") - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift deleted file mode 100644 index 1b77016d..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/ResponseSerialization.swift +++ /dev/null @@ -1,1116 +0,0 @@ -// -// ResponseSerialization.swift -// -// Copyright (c) 2014-2018 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 - -// MARK: Protocols - -/// The type to which all data response serializers must conform in order to serialize a response. -public protocol DataResponseSerializerProtocol { - /// The type of serialized object to be created. - associatedtype SerializedObject - - /// Serialize the response `Data` into the provided type.. - /// - /// - Parameters: - /// - request: `URLRequest` which was used to perform the request, if any. - /// - response: `HTTPURLResponse` received from the server, if any. - /// - data: `Data` returned from the server, if any. - /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. - /// - /// - Returns: The `SerializedObject`. - /// - Throws: Any `Error` produced during serialization. - func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> SerializedObject -} - -/// The type to which all download response serializers must conform in order to serialize a response. -public protocol DownloadResponseSerializerProtocol { - /// The type of serialized object to be created. - associatedtype SerializedObject - - /// Serialize the downloaded response `Data` from disk into the provided type.. - /// - /// - Parameters: - /// - request: `URLRequest` which was used to perform the request, if any. - /// - response: `HTTPURLResponse` received from the server, if any. - /// - fileURL: File `URL` to which the response data was downloaded. - /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. - /// - /// - Returns: The `SerializedObject`. - /// - Throws: Any `Error` produced during serialization. - func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> SerializedObject -} - -/// A serializer that can handle both data and download responses. -public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol { - /// `DataPreprocessor` used to prepare incoming `Data` for serialization. - var dataPreprocessor: DataPreprocessor { get } - /// `HTTPMethod`s for which empty response bodies are considered appropriate. - var emptyRequestMethods: Set { get } - /// HTTP response codes for which empty response bodies are considered appropriate. - var emptyResponseCodes: Set { get } -} - -/// Type used to preprocess `Data` before it handled by a serializer. -public protocol DataPreprocessor { - /// Process `Data` before it's handled by a serializer. - /// - Parameter data: The raw `Data` to process. - func preprocess(_ data: Data) throws -> Data -} - -/// `DataPreprocessor` that returns passed `Data` without any transform. -public struct PassthroughPreprocessor: DataPreprocessor { - public init() {} - - public func preprocess(_ data: Data) throws -> Data { data } -} - -/// `DataPreprocessor` that trims Google's typical `)]}',\n` XSSI JSON header. -public struct GoogleXSSIPreprocessor: DataPreprocessor { - public init() {} - - public func preprocess(_ data: Data) throws -> Data { - (data.prefix(6) == Data(")]}',\n".utf8)) ? data.dropFirst(6) : data - } -} - -extension ResponseSerializer { - /// Default `DataPreprocessor`. `PassthroughPreprocessor` by default. - public static var defaultDataPreprocessor: DataPreprocessor { PassthroughPreprocessor() } - /// Default `HTTPMethod`s for which empty response bodies are considered appropriate. `[.head]` by default. - public static var defaultEmptyRequestMethods: Set { [.head] } - /// HTTP response codes for which empty response bodies are considered appropriate. `[204, 205]` by default. - public static var defaultEmptyResponseCodes: Set { [204, 205] } - - public var dataPreprocessor: DataPreprocessor { Self.defaultDataPreprocessor } - public var emptyRequestMethods: Set { Self.defaultEmptyRequestMethods } - public var emptyResponseCodes: Set { Self.defaultEmptyResponseCodes } - - /// Determines whether the `request` allows empty response bodies, if `request` exists. - /// - /// - Parameter request: `URLRequest` to evaluate. - /// - /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `request` was `nil`. - public func requestAllowsEmptyResponseData(_ request: URLRequest?) -> Bool? { - request.flatMap { $0.httpMethod } - .flatMap(HTTPMethod.init) - .map { emptyRequestMethods.contains($0) } - } - - /// Determines whether the `response` allows empty response bodies, if `response` exists`. - /// - /// - Parameter response: `HTTPURLResponse` to evaluate. - /// - /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `response` was `nil`. - public func responseAllowsEmptyResponseData(_ response: HTTPURLResponse?) -> Bool? { - response.flatMap { $0.statusCode } - .map { emptyResponseCodes.contains($0) } - } - - /// Determines whether `request` and `response` allow empty response bodies. - /// - /// - Parameters: - /// - request: `URLRequest` to evaluate. - /// - response: `HTTPURLResponse` to evaluate. - /// - /// - Returns: `true` if `request` or `response` allow empty bodies, `false` otherwise. - public func emptyResponseAllowed(forRequest request: URLRequest?, response: HTTPURLResponse?) -> Bool { - (requestAllowsEmptyResponseData(request) == true) || (responseAllowsEmptyResponseData(response) == true) - } -} - -/// By default, any serializer declared to conform to both types will get file serialization for free, as it just feeds -/// the data read from disk into the data response serializer. -extension DownloadResponseSerializerProtocol where Self: DataResponseSerializerProtocol { - public func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> Self.SerializedObject { - guard error == nil else { throw error! } - - guard let fileURL = fileURL else { - throw AFError.responseSerializationFailed(reason: .inputFileNil) - } - - let data: Data - do { - data = try Data(contentsOf: fileURL) - } catch { - throw AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)) - } - - do { - return try serialize(request: request, response: response, data: data, error: error) - } catch { - throw error - } - } -} - -// MARK: - Default - -extension DataRequest { - /// Adds a handler to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - completionHandler: The code to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { - appendResponseSerializer { - // Start work that should be on the serialization queue. - let result = AFResult(value: self.data, error: self.error) - // End work that should be on the serialization queue. - - self.underlyingQueue.async { - let response = DataResponse(request: self.request, - response: self.response, - data: self.data, - metrics: self.metrics, - serializationDuration: 0, - result: result) - - self.eventMonitor?.request(self, didParseResponse: response) - - self.responseSerializerDidComplete { queue.async { completionHandler(response) } } - } - } - - return self - } - - /// Adds a handler to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default - /// - responseSerializer: The response serializer responsible for serializing the request, response, and data. - /// - completionHandler: The code to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func response(queue: DispatchQueue = .main, - responseSerializer: Serializer, - completionHandler: @escaping (AFDataResponse) -> Void) - -> Self { - appendResponseSerializer { - // Start work that should be on the serialization queue. - let start = ProcessInfo.processInfo.systemUptime - let result: AFResult = Result { - try responseSerializer.serialize(request: self.request, - response: self.response, - data: self.data, - error: self.error) - }.mapError { error in - error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) - } - - let end = ProcessInfo.processInfo.systemUptime - // End work that should be on the serialization queue. - - self.underlyingQueue.async { - let response = DataResponse(request: self.request, - response: self.response, - data: self.data, - metrics: self.metrics, - serializationDuration: end - start, - result: result) - - self.eventMonitor?.request(self, didParseResponse: response) - - guard let serializerError = result.failure, let delegate = self.delegate else { - self.responseSerializerDidComplete { queue.async { completionHandler(response) } } - return - } - - delegate.retryResult(for: self, dueTo: serializerError) { retryResult in - var didComplete: (() -> Void)? - - defer { - if let didComplete = didComplete { - self.responseSerializerDidComplete { queue.async { didComplete() } } - } - } - - switch retryResult { - case .doNotRetry: - didComplete = { completionHandler(response) } - - case let .doNotRetryWithError(retryError): - let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) - - let response = DataResponse(request: self.request, - response: self.response, - data: self.data, - metrics: self.metrics, - serializationDuration: end - start, - result: result) - - didComplete = { completionHandler(response) } - - case .retry, .retryWithDelay: - delegate.retryRequest(self, withDelay: retryResult.delay) - } - } - } - } - - return self - } -} - -extension DownloadRequest { - /// Adds a handler to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - completionHandler: The code to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func response(queue: DispatchQueue = .main, - completionHandler: @escaping (AFDownloadResponse) -> Void) - -> Self { - appendResponseSerializer { - // Start work that should be on the serialization queue. - let result = AFResult(value: self.fileURL, error: self.error) - // End work that should be on the serialization queue. - - self.underlyingQueue.async { - let response = DownloadResponse(request: self.request, - response: self.response, - fileURL: self.fileURL, - resumeData: self.resumeData, - metrics: self.metrics, - serializationDuration: 0, - result: result) - - self.eventMonitor?.request(self, didParseResponse: response) - - self.responseSerializerDidComplete { queue.async { completionHandler(response) } } - } - } - - return self - } - - /// Adds a handler to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - responseSerializer: The response serializer responsible for serializing the request, response, and data - /// contained in the destination `URL`. - /// - completionHandler: The code to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func response(queue: DispatchQueue = .main, - responseSerializer: Serializer, - completionHandler: @escaping (AFDownloadResponse) -> Void) - -> Self { - appendResponseSerializer { - // Start work that should be on the serialization queue. - let start = ProcessInfo.processInfo.systemUptime - let result: AFResult = Result { - try responseSerializer.serializeDownload(request: self.request, - response: self.response, - fileURL: self.fileURL, - error: self.error) - }.mapError { error in - error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) - } - let end = ProcessInfo.processInfo.systemUptime - // End work that should be on the serialization queue. - - self.underlyingQueue.async { - let response = DownloadResponse(request: self.request, - response: self.response, - fileURL: self.fileURL, - resumeData: self.resumeData, - metrics: self.metrics, - serializationDuration: end - start, - result: result) - - self.eventMonitor?.request(self, didParseResponse: response) - - guard let serializerError = result.failure, let delegate = self.delegate else { - self.responseSerializerDidComplete { queue.async { completionHandler(response) } } - return - } - - delegate.retryResult(for: self, dueTo: serializerError) { retryResult in - var didComplete: (() -> Void)? - - defer { - if let didComplete = didComplete { - self.responseSerializerDidComplete { queue.async { didComplete() } } - } - } - - switch retryResult { - case .doNotRetry: - didComplete = { completionHandler(response) } - - case let .doNotRetryWithError(retryError): - let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) - - let response = DownloadResponse(request: self.request, - response: self.response, - fileURL: self.fileURL, - resumeData: self.resumeData, - metrics: self.metrics, - serializationDuration: end - start, - result: result) - - didComplete = { completionHandler(response) } - - case .retry, .retryWithDelay: - delegate.retryRequest(self, withDelay: retryResult.delay) - } - } - } - } - - return self - } -} - -// MARK: - URL - -/// A `DownloadResponseSerializerProtocol` that performs only `Error` checking and ensures that a downloaded `fileURL` -/// is present. -public struct URLResponseSerializer: DownloadResponseSerializerProtocol { - /// Creates an instance. - public init() {} - - public func serializeDownload(request: URLRequest?, - response: HTTPURLResponse?, - fileURL: URL?, - error: Error?) throws -> URL { - guard error == nil else { throw error! } - - guard let url = fileURL else { - throw AFError.responseSerializationFailed(reason: .inputFileNil) - } - - return url - } -} - -extension DownloadRequest { - /// Adds a handler using a `URLResponseSerializer` to be called once the request is finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is called. `.main` by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseURL(queue: DispatchQueue = .main, - completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { - response(queue: queue, responseSerializer: URLResponseSerializer(), completionHandler: completionHandler) - } -} - -// MARK: - Data - -/// A `ResponseSerializer` that performs minimal response checking and returns any response `Data` as-is. By default, a -/// request returning `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the -/// response has an HTTP status code valid for empty responses, then an empty `Data` value is returned. -public final class DataResponseSerializer: ResponseSerializer { - public let dataPreprocessor: DataPreprocessor - public let emptyResponseCodes: Set - public let emptyRequestMethods: Set - - /// Creates an instance using the provided values. - /// - /// - Parameters: - /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. - /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. - /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. - public init(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) { - self.dataPreprocessor = dataPreprocessor - self.emptyResponseCodes = emptyResponseCodes - self.emptyRequestMethods = emptyRequestMethods - } - - public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Data { - guard error == nil else { throw error! } - - guard var data = data, !data.isEmpty else { - guard emptyResponseAllowed(forRequest: request, response: response) else { - throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) - } - - return Data() - } - - data = try dataPreprocessor.preprocess(data) - - return data - } -} - -extension DataRequest { - /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is called. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseData(queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, - completionHandler: @escaping (AFDataResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - completionHandler: completionHandler) - } -} - -extension DownloadRequest { - /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is called. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseData(queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, - completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - completionHandler: completionHandler) - } -} - -// MARK: - String - -/// A `ResponseSerializer` that decodes the response data as a `String`. By default, a request returning `nil` or no -/// data is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code -/// valid for empty responses, then an empty `String` is returned. -public final class StringResponseSerializer: ResponseSerializer { - public let dataPreprocessor: DataPreprocessor - /// Optional string encoding used to validate the response. - public let encoding: String.Encoding? - public let emptyResponseCodes: Set - public let emptyRequestMethods: Set - - /// Creates an instance with the provided values. - /// - /// - Parameters: - /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. - /// - encoding: A string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. - /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. - public init(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, - encoding: String.Encoding? = nil, - emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) { - self.dataPreprocessor = dataPreprocessor - self.encoding = encoding - self.emptyResponseCodes = emptyResponseCodes - self.emptyRequestMethods = emptyRequestMethods - } - - public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> String { - guard error == nil else { throw error! } - - guard var data = data, !data.isEmpty else { - guard emptyResponseAllowed(forRequest: request, response: response) else { - throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) - } - - return "" - } - - data = try dataPreprocessor.preprocess(data) - - var convertedEncoding = encoding - - if let encodingName = response?.textEncodingName, convertedEncoding == nil { - convertedEncoding = String.Encoding(ianaCharsetName: encodingName) - } - - let actualEncoding = convertedEncoding ?? .isoLatin1 - - guard let string = String(data: data, encoding: actualEncoding) else { - throw AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)) - } - - return string - } -} - -extension DataRequest { - /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseString(queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, - encoding: String.Encoding? = nil, - emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, - completionHandler: @escaping (AFDataResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, - encoding: encoding, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - completionHandler: completionHandler) - } -} - -extension DownloadRequest { - /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseString(queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, - encoding: String.Encoding? = nil, - emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, - completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, - encoding: encoding, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - completionHandler: completionHandler) - } -} - -// MARK: - JSON - -/// A `ResponseSerializer` that decodes the response data using `JSONSerialization`. By default, a request returning -/// `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the response has an -/// HTTP status code valid for empty responses, then an `NSNull` value is returned. -public final class JSONResponseSerializer: ResponseSerializer { - public let dataPreprocessor: DataPreprocessor - public let emptyResponseCodes: Set - public let emptyRequestMethods: Set - /// `JSONSerialization.ReadingOptions` used when serializing a response. - public let options: JSONSerialization.ReadingOptions - - /// Creates an instance with the provided values. - /// - /// - Parameters: - /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. - /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. - /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. - /// - options: The options to use. `.allowFragments` by default. - public init(dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, - options: JSONSerialization.ReadingOptions = .allowFragments) { - self.dataPreprocessor = dataPreprocessor - self.emptyResponseCodes = emptyResponseCodes - self.emptyRequestMethods = emptyRequestMethods - self.options = options - } - - public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any { - guard error == nil else { throw error! } - - guard var data = data, !data.isEmpty else { - guard emptyResponseAllowed(forRequest: request, response: response) else { - throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) - } - - return NSNull() - } - - data = try dataPreprocessor.preprocess(data) - - do { - return try JSONSerialization.jsonObject(with: data, options: options) - } catch { - throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)) - } - } -} - -extension DataRequest { - /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` - /// by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseJSON(queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, - options: JSONSerialization.ReadingOptions = .allowFragments, - completionHandler: @escaping (AFDataResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods, - options: options), - completionHandler: completionHandler) - } -} - -extension DownloadRequest { - /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` - /// by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseJSON(queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, - emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, - options: JSONSerialization.ReadingOptions = .allowFragments, - completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods, - options: options), - completionHandler: completionHandler) - } -} - -// MARK: - Empty - -/// Protocol representing an empty response. Use `T.emptyValue()` to get an instance. -public protocol EmptyResponse { - /// Empty value for the conforming type. - /// - /// - Returns: Value of `Self` to use for empty values. - static func emptyValue() -> Self -} - -/// Type representing an empty value. Use `Empty.value` to get the static instance. -public struct Empty: Codable { - /// Static `Empty` instance used for all `Empty` responses. - public static let value = Empty() -} - -extension Empty: EmptyResponse { - public static func emptyValue() -> Empty { - value - } -} - -// MARK: - DataDecoder Protocol - -/// Any type which can decode `Data` into a `Decodable` type. -public protocol DataDecoder { - /// Decode `Data` into the provided type. - /// - /// - Parameters: - /// - type: The `Type` to be decoded. - /// - data: The `Data` to be decoded. - /// - /// - Returns: The decoded value of type `D`. - /// - Throws: Any error that occurs during decode. - func decode(_ type: D.Type, from data: Data) throws -> D -} - -/// `JSONDecoder` automatically conforms to `DataDecoder`. -extension JSONDecoder: DataDecoder {} -/// `PropertyListDecoder` automatically conforms to `DataDecoder`. -extension PropertyListDecoder: DataDecoder {} - -// MARK: - Decodable - -/// A `ResponseSerializer` that decodes the response data as a generic value using any type that conforms to -/// `DataDecoder`. By default, this is an instance of `JSONDecoder`. Additionally, a request returning `nil` or no data -/// is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code valid -/// for empty responses then an empty value will be returned. If the decoded type conforms to `EmptyResponse`, the -/// type's `emptyValue()` will be returned. If the decoded type is `Empty`, the `.value` instance is returned. If the -/// decoded type *does not* conform to `EmptyResponse` and isn't `Empty`, an error will be produced. -public final class DecodableResponseSerializer: ResponseSerializer { - public let dataPreprocessor: DataPreprocessor - /// The `DataDecoder` instance used to decode responses. - public let decoder: DataDecoder - public let emptyResponseCodes: Set - public let emptyRequestMethods: Set - - /// Creates an instance using the values provided. - /// - /// - Parameters: - /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. - /// - decoder: The `DataDecoder`. `JSONDecoder()` by default. - /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. - /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. - public init(dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, - decoder: DataDecoder = JSONDecoder(), - emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) { - self.dataPreprocessor = dataPreprocessor - self.decoder = decoder - self.emptyResponseCodes = emptyResponseCodes - self.emptyRequestMethods = emptyRequestMethods - } - - public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T { - guard error == nil else { throw error! } - - guard var data = data, !data.isEmpty else { - guard emptyResponseAllowed(forRequest: request, response: response) else { - throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) - } - - guard let emptyResponseType = T.self as? EmptyResponse.Type, let emptyValue = emptyResponseType.emptyValue() as? T else { - throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) - } - - return emptyValue - } - - data = try dataPreprocessor.preprocess(data) - - do { - return try decoder.decode(T.self, from: data) - } catch { - throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) - } - } -} - -extension DataRequest { - /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - type: `Decodable` type to decode from response data. - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. - /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` - /// by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseDecodable(of type: T.Type = T.self, - queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, - decoder: DataDecoder = JSONDecoder(), - emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, - completionHandler: @escaping (AFDataResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, - decoder: decoder, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - completionHandler: completionHandler) - } -} - -extension DownloadRequest { - /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. - /// - /// - Parameters: - /// - type: `Decodable` type to decode from response data. - /// - queue: The queue on which the completion handler is dispatched. `.main` by default. - /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the - /// `completionHandler`. `PassthroughPreprocessor()` by default. - /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. - /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined - /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. - /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. - /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. - /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` - /// by default. - /// - completionHandler: A closure to be executed once the request has finished. - /// - /// - Returns: The request. - @discardableResult - public func responseDecodable(of type: T.Type = T.self, - queue: DispatchQueue = .main, - dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, - decoder: DataDecoder = JSONDecoder(), - emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, - emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, - completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { - response(queue: queue, - responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, - decoder: decoder, - emptyResponseCodes: emptyResponseCodes, - emptyRequestMethods: emptyRequestMethods), - completionHandler: completionHandler) - } -} - -// MARK: - DataStreamRequest - -/// A type which can serialize incoming `Data`. -public protocol DataStreamSerializer { - /// Type produced from the serialized `Data`. - associatedtype SerializedObject - - /// Serializes incoming `Data` into a `SerializedObject` value. - /// - /// - Parameter data: `Data` to be serialized. - /// - /// - Throws: Any error produced during serialization. - func serialize(_ data: Data) throws -> SerializedObject -} - -/// `DataStreamSerializer` which uses the provided `DataPreprocessor` and `DataDecoder` to serialize the incoming `Data`. -public struct DecodableStreamSerializer: DataStreamSerializer { - /// `DataDecoder` used to decode incoming `Data`. - public let decoder: DataDecoder - /// `DataPreprocessor` incoming `Data` is passed through before being passed to the `DataDecoder`. - public let dataPreprocessor: DataPreprocessor - - /// Creates an instance with the provided `DataDecoder` and `DataPreprocessor`. - /// - Parameters: - /// - decoder: ` DataDecoder` used to decode incoming `Data`. - /// - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the `decoder`. - public init(decoder: DataDecoder = JSONDecoder(), dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) { - self.decoder = decoder - self.dataPreprocessor = dataPreprocessor - } - - public func serialize(_ data: Data) throws -> T { - let processedData = try dataPreprocessor.preprocess(data) - do { - return try decoder.decode(T.self, from: processedData) - } catch { - throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) - } - } -} - -/// `DataStreamSerializer` which performs no serialization on incoming `Data`. -public struct PassthroughStreamSerializer: DataStreamSerializer { - public func serialize(_ data: Data) throws -> Data { data } -} - -/// `DataStreamSerializer` which serializes incoming stream `Data` into `UTF8`-decoded `String` values. -public struct StringStreamSerializer: DataStreamSerializer { - public func serialize(_ data: Data) throws -> String { - String(decoding: data, as: UTF8.self) - } -} - -extension DataStreamRequest { - /// Adds a `StreamHandler` which performs no parsing on incoming `Data`. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. - /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. - /// - /// - Returns: The `DataStreamRequest`. - @discardableResult - public func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler) -> Self { - let parser = { [unowned self] (data: Data) in - queue.async { - self.capturingError { - try stream(.init(event: .stream(.success(data)), token: .init(self))) - } - - self.updateAndCompleteIfPossible() - } - } - - $streamMutableState.write { $0.streams.append(parser) } - appendStreamCompletion(on: queue, stream: stream) - - return self - } - - /// Adds a `StreamHandler` which uses the provided `DataStreamSerializer` to process incoming `Data`. - /// - /// - Parameters: - /// - serializer: `DataStreamSerializer` used to process incoming `Data`. Its work is done on the `serializationQueue`. - /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. - /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. - /// - /// - Returns: The `DataStreamRequest`. - @discardableResult - public func responseStream(using serializer: Serializer, - on queue: DispatchQueue = .main, - stream: @escaping Handler) -> Self { - let parser = { [unowned self] (data: Data) in - self.serializationQueue.async { - // Start work on serialization queue. - let result = Result { try serializer.serialize(data) } - .mapError { $0.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: $0))) } - // End work on serialization queue. - self.underlyingQueue.async { - self.eventMonitor?.request(self, didParseStream: result) - - if result.isFailure, self.automaticallyCancelOnStreamError { - self.cancel() - } - - queue.async { - self.capturingError { - try stream(.init(event: .stream(result), token: .init(self))) - } - - self.updateAndCompleteIfPossible() - } - } - } - } - - $streamMutableState.write { $0.streams.append(parser) } - appendStreamCompletion(on: queue, stream: stream) - - return self - } - - /// Adds a `StreamHandler` which parses incoming `Data` as a UTF8 `String`. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. - /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. - /// - /// - Returns: The `DataStreamRequest`. - @discardableResult - public func responseStreamString(on queue: DispatchQueue = .main, - stream: @escaping Handler) -> Self { - let parser = { [unowned self] (data: Data) in - self.serializationQueue.async { - // Start work on serialization queue. - let string = String(decoding: data, as: UTF8.self) - // End work on serialization queue. - self.underlyingQueue.async { - self.eventMonitor?.request(self, didParseStream: .success(string)) - - queue.async { - self.capturingError { - try stream(.init(event: .stream(.success(string)), token: .init(self))) - } - - self.updateAndCompleteIfPossible() - } - } - } - } - - $streamMutableState.write { $0.streams.append(parser) } - appendStreamCompletion(on: queue, stream: stream) - - return self - } - - private func updateAndCompleteIfPossible() { - $streamMutableState.write { state in - state.numberOfExecutingStreams -= 1 - - guard state.numberOfExecutingStreams == 0, !state.enqueuedCompletionEvents.isEmpty else { return } - - let completionEvents = state.enqueuedCompletionEvents - self.underlyingQueue.async { completionEvents.forEach { $0() } } - state.enqueuedCompletionEvents.removeAll() - } - } - - /// Adds a `StreamHandler` which parses incoming `Data` using the provided `DataDecoder`. - /// - /// - Parameters: - /// - type: `Decodable` type to parse incoming `Data` into. - /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. - /// - decoder: `DataDecoder` used to decode the incoming `Data`. - /// - preprocessor: `DataPreprocessor` used to process the incoming `Data` before it's passed to the `decoder`. - /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. - /// - /// - Returns: The `DataStreamRequest`. - @discardableResult - public func responseStreamDecodable(of type: T.Type = T.self, - on queue: DispatchQueue = .main, - using decoder: DataDecoder = JSONDecoder(), - preprocessor: DataPreprocessor = PassthroughPreprocessor(), - stream: @escaping Handler) -> Self { - responseStream(using: DecodableStreamSerializer(decoder: decoder, dataPreprocessor: preprocessor), - stream: stream) - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift deleted file mode 100644 index 39ac2860..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Result+Alamofire.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// Result+Alamofire.swift -// -// Copyright (c) 2019 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 - -/// Default type of `Result` returned by Alamofire, with an `AFError` `Failure` type. -public typealias AFResult = Result - -// MARK: - Internal APIs - -extension Result { - /// Returns whether the instance is `.success`. - var isSuccess: Bool { - guard case .success = self else { return false } - return true - } - - /// Returns whether the instance is `.failure`. - var isFailure: Bool { - !isSuccess - } - - /// Returns the associated value if the result is a success, `nil` otherwise. - var success: Success? { - guard case let .success(value) = self else { return nil } - return value - } - - /// Returns the associated error value if the result is a failure, `nil` otherwise. - var failure: Failure? { - guard case let .failure(error) = self else { return nil } - return error - } - - /// Initializes a `Result` from value or error. Returns `.failure` if the error is non-nil, `.success` otherwise. - /// - /// - Parameters: - /// - value: A value. - /// - error: An `Error`. - init(value: Success, error: Failure?) { - if let error = error { - self = .failure(error) - } else { - self = .success(value) - } - } - - /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. - /// - /// Use the `tryMap` method with a closure that may throw an error. For example: - /// - /// let possibleData: Result = .success(Data(...)) - /// let possibleObject = possibleData.tryMap { - /// 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. - func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { - switch self { - case let .success(value): - do { - return try .success(transform(value)) - } catch { - return .failure(error) - } - case let .failure(error): - return .failure(error) - } - } - - /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. - /// - /// Use the `tryMapError` function with a closure that may throw an error. For example: - /// - /// let possibleData: Result = .success(Data(...)) - /// let possibleObject = possibleData.tryMapError { - /// 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 success. - func tryMapError(_ transform: (Failure) throws -> NewFailure) -> Result { - switch self { - case let .failure(error): - do { - return try .failure(transform(error)) - } catch { - return .failure(error) - } - case let .success(value): - return .success(value) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift deleted file mode 100644 index e9cbcaf4..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/RetryPolicy.swift +++ /dev/null @@ -1,370 +0,0 @@ -// -// RetryPolicy.swift -// -// Copyright (c) 2019-2020 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 retry policy that retries requests using an exponential backoff for allowed HTTP methods and HTTP status codes -/// as well as certain types of networking errors. -open class RetryPolicy: RequestInterceptor { - /// The default retry limit for retry policies. - public static let defaultRetryLimit: UInt = 2 - - /// The default exponential backoff base for retry policies (must be a minimum of 2). - public static let defaultExponentialBackoffBase: UInt = 2 - - /// The default exponential backoff scale for retry policies. - public static let defaultExponentialBackoffScale: Double = 0.5 - - /// The default HTTP methods to retry. - /// See [RFC 2616 - Section 9.1.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) for more information. - public static let defaultRetryableHTTPMethods: Set = [.delete, // [Delete](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7) - not always idempotent - .get, // [GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3) - generally idempotent - .head, // [HEAD](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4) - generally idempotent - .options, // [OPTIONS](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2) - inherently idempotent - .put, // [PUT](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) - not always idempotent - .trace // [TRACE](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8) - inherently idempotent - ] - - /// The default HTTP status codes to retry. - /// See [RFC 2616 - Section 10](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10) for more information. - public static let defaultRetryableHTTPStatusCodes: Set = [408, // [Request Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9) - 500, // [Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) - 502, // [Bad Gateway](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3) - 503, // [Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) - 504 // [Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5) - ] - - /// The default URL error codes to retry. - public static let defaultRetryableURLErrorCodes: Set = [// [Security] App Transport Security disallowed a connection because there is no secure network connection. - // - [Disabled] ATS settings do not change at runtime. - // .appTransportSecurityRequiresSecureConnection, - - // [System] An app or app extension attempted to connect to a background session that is already connected to a - // process. - // - [Enabled] The other process could release the background session. - .backgroundSessionInUseByAnotherProcess, - - // [System] The shared container identifier of the URL session configuration is needed but has not been set. - // - [Disabled] Cannot change at runtime. - // .backgroundSessionRequiresSharedContainer, - - // [System] The app is suspended or exits while a background data task is processing. - // - [Enabled] App can be foregrounded or launched to recover. - .backgroundSessionWasDisconnected, - - // [Network] The URL Loading system received bad data from the server. - // - [Enabled] Server could return valid data when retrying. - .badServerResponse, - - // [Resource] A malformed URL prevented a URL request from being initiated. - // - [Disabled] URL was most likely constructed incorrectly. - // .badURL, - - // [System] A connection was attempted while a phone call is active on a network that does not support - // simultaneous phone and data communication (EDGE or GPRS). - // - [Enabled] Phone call could be ended to allow request to recover. - .callIsActive, - - // [Client] An asynchronous load has been canceled. - // - [Disabled] Request was cancelled by the client. - // .cancelled, - - // [File System] A download task couldn’t close the downloaded file on disk. - // - [Disabled] File system error is unlikely to recover with retry. - // .cannotCloseFile, - - // [Network] An attempt to connect to a host failed. - // - [Enabled] Server or DNS lookup could recover during retry. - .cannotConnectToHost, - - // [File System] A download task couldn’t create the downloaded file on disk because of an I/O failure. - // - [Disabled] File system error is unlikely to recover with retry. - // .cannotCreateFile, - - // [Data] Content data received during a connection request had an unknown content encoding. - // - [Disabled] Server is unlikely to modify the content encoding during a retry. - // .cannotDecodeContentData, - - // [Data] Content data received during a connection request could not be decoded for a known content encoding. - // - [Disabled] Server is unlikely to modify the content encoding during a retry. - // .cannotDecodeRawData, - - // [Network] The host name for a URL could not be resolved. - // - [Enabled] Server or DNS lookup could recover during retry. - .cannotFindHost, - - // [Network] A request to load an item only from the cache could not be satisfied. - // - [Enabled] Cache could be populated during a retry. - .cannotLoadFromNetwork, - - // [File System] A download task was unable to move a downloaded file on disk. - // - [Disabled] File system error is unlikely to recover with retry. - // .cannotMoveFile, - - // [File System] A download task was unable to open the downloaded file on disk. - // - [Disabled] File system error is unlikely to recover with retry. - // .cannotOpenFile, - - // [Data] A task could not parse a response. - // - [Disabled] Invalid response is unlikely to recover with retry. - // .cannotParseResponse, - - // [File System] A download task was unable to remove a downloaded file from disk. - // - [Disabled] File system error is unlikely to recover with retry. - // .cannotRemoveFile, - - // [File System] A download task was unable to write to the downloaded file on disk. - // - [Disabled] File system error is unlikely to recover with retry. - // .cannotWriteToFile, - - // [Security] A client certificate was rejected. - // - [Disabled] Client certificate is unlikely to change with retry. - // .clientCertificateRejected, - - // [Security] A client certificate was required to authenticate an SSL connection during a request. - // - [Disabled] Client certificate is unlikely to be provided with retry. - // .clientCertificateRequired, - - // [Data] The length of the resource data exceeds the maximum allowed. - // - [Disabled] Resource will likely still exceed the length maximum on retry. - // .dataLengthExceedsMaximum, - - // [System] The cellular network disallowed a connection. - // - [Enabled] WiFi connection could be established during retry. - .dataNotAllowed, - - // [Network] The host address could not be found via DNS lookup. - // - [Enabled] DNS lookup could succeed during retry. - .dnsLookupFailed, - - // [Data] A download task failed to decode an encoded file during the download. - // - [Enabled] Server could correct the decoding issue with retry. - .downloadDecodingFailedMidStream, - - // [Data] A download task failed to decode an encoded file after downloading. - // - [Enabled] Server could correct the decoding issue with retry. - .downloadDecodingFailedToComplete, - - // [File System] A file does not exist. - // - [Disabled] File system error is unlikely to recover with retry. - // .fileDoesNotExist, - - // [File System] A request for an FTP file resulted in the server responding that the file is not a plain file, - // but a directory. - // - [Disabled] FTP directory is not likely to change to a file during a retry. - // .fileIsDirectory, - - // [Network] A redirect loop has been detected or the threshold for number of allowable redirects has been - // exceeded (currently 16). - // - [Disabled] The redirect loop is unlikely to be resolved within the retry window. - // .httpTooManyRedirects, - - // [System] The attempted connection required activating a data context while roaming, but international roaming - // is disabled. - // - [Enabled] WiFi connection could be established during retry. - .internationalRoamingOff, - - // [Connectivity] A client or server connection was severed in the middle of an in-progress load. - // - [Enabled] A network connection could be established during retry. - .networkConnectionLost, - - // [File System] A resource couldn’t be read because of insufficient permissions. - // - [Disabled] Permissions are unlikely to be granted during retry. - // .noPermissionsToReadFile, - - // [Connectivity] A network resource was requested, but an internet connection has not been established and - // cannot be established automatically. - // - [Enabled] A network connection could be established during retry. - .notConnectedToInternet, - - // [Resource] A redirect was specified by way of server response code, but the server did not accompany this - // code with a redirect URL. - // - [Disabled] The redirect URL is unlikely to be supplied during a retry. - // .redirectToNonExistentLocation, - - // [Client] A body stream is needed but the client did not provide one. - // - [Disabled] The client will be unlikely to supply a body stream during retry. - // .requestBodyStreamExhausted, - - // [Resource] A requested resource couldn’t be retrieved. - // - [Disabled] The resource is unlikely to become available during the retry window. - // .resourceUnavailable, - - // [Security] An attempt to establish a secure connection failed for reasons that can’t be expressed more - // specifically. - // - [Enabled] The secure connection could be established during a retry given the lack of specificity - // provided by the error. - .secureConnectionFailed, - - // [Security] A server certificate had a date which indicates it has expired, or is not yet valid. - // - [Enabled] The server certificate could become valid within the retry window. - .serverCertificateHasBadDate, - - // [Security] A server certificate was not signed by any root server. - // - [Disabled] The server certificate is unlikely to change during the retry window. - // .serverCertificateHasUnknownRoot, - - // [Security] A server certificate is not yet valid. - // - [Enabled] The server certificate could become valid within the retry window. - .serverCertificateNotYetValid, - - // [Security] A server certificate was signed by a root server that isn’t trusted. - // - [Disabled] The server certificate is unlikely to become trusted within the retry window. - // .serverCertificateUntrusted, - - // [Network] An asynchronous operation timed out. - // - [Enabled] The request timed out for an unknown reason and should be retried. - .timedOut - - // [System] The URL Loading System encountered an error that it can’t interpret. - // - [Disabled] The error could not be interpreted and is unlikely to be recovered from during a retry. - // .unknown, - - // [Resource] A properly formed URL couldn’t be handled by the framework. - // - [Disabled] The URL is unlikely to change during a retry. - // .unsupportedURL, - - // [Client] Authentication is required to access a resource. - // - [Disabled] The user authentication is unlikely to be provided by retrying. - // .userAuthenticationRequired, - - // [Client] An asynchronous request for authentication has been canceled by the user. - // - [Disabled] The user cancelled authentication and explicitly took action to not retry. - // .userCancelledAuthentication, - - // [Resource] A server reported that a URL has a non-zero content length, but terminated the network connection - // gracefully without sending any data. - // - [Disabled] The server is unlikely to provide data during the retry window. - // .zeroByteResource, - ] - - /// The total number of times the request is allowed to be retried. - public let retryLimit: UInt - - /// The base of the exponential backoff policy (should always be greater than or equal to 2). - public let exponentialBackoffBase: UInt - - /// The scale of the exponential backoff. - public let exponentialBackoffScale: Double - - /// The HTTP methods that are allowed to be retried. - public let retryableHTTPMethods: Set - - /// The HTTP status codes that are automatically retried by the policy. - public let retryableHTTPStatusCodes: Set - - /// The URL error codes that are automatically retried by the policy. - public let retryableURLErrorCodes: Set - - /// Creates an `ExponentialBackoffRetryPolicy` from the specified parameters. - /// - /// - Parameters: - /// - retryLimit: The total number of times the request is allowed to be retried. `2` by default. - /// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default. - /// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default. - /// - retryableHTTPMethods: The HTTP methods that are allowed to be retried. - /// `RetryPolicy.defaultRetryableHTTPMethods` by default. - /// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy. - /// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default. - /// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy. - /// `RetryPolicy.defaultRetryableURLErrorCodes` by default. - public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit, - exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, - exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, - retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods, - retryableHTTPStatusCodes: Set = RetryPolicy.defaultRetryableHTTPStatusCodes, - retryableURLErrorCodes: Set = RetryPolicy.defaultRetryableURLErrorCodes) { - precondition(exponentialBackoffBase >= 2, "The `exponentialBackoffBase` must be a minimum of 2.") - - self.retryLimit = retryLimit - self.exponentialBackoffBase = exponentialBackoffBase - self.exponentialBackoffScale = exponentialBackoffScale - self.retryableHTTPMethods = retryableHTTPMethods - self.retryableHTTPStatusCodes = retryableHTTPStatusCodes - self.retryableURLErrorCodes = retryableURLErrorCodes - } - - open func retry(_ request: Request, - for session: Session, - dueTo error: Error, - completion: @escaping (RetryResult) -> Void) { - if request.retryCount < retryLimit, shouldRetry(request: request, dueTo: error) { - completion(.retryWithDelay(pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale)) - } else { - completion(.doNotRetry) - } - } - - /// Determines whether or not to retry the provided `Request`. - /// - /// - Parameters: - /// - request: `Request` that failed due to the provided `Error`. - /// - error: `Error` encountered while executing the `Request`. - /// - /// - Returns: `Bool` determining whether or not to retry the `Request`. - open func shouldRetry(request: Request, dueTo error: Error) -> Bool { - guard let httpMethod = request.request?.method, retryableHTTPMethods.contains(httpMethod) else { return false } - - if let statusCode = request.response?.statusCode, retryableHTTPStatusCodes.contains(statusCode) { - return true - } else { - let errorCode = (error as? URLError)?.code - let afErrorCode = (error.asAFError?.underlyingError as? URLError)?.code - - guard let code = errorCode ?? afErrorCode else { return false } - - return retryableURLErrorCodes.contains(code) - } - } -} - -// MARK: - - -/// A retry policy that automatically retries idempotent requests for network connection lost errors. For more -/// information about retrying network connection lost errors, please refer to Apple's -/// [technical document](https://developer.apple.com/library/content/qa/qa1941/_index.html). -open class ConnectionLostRetryPolicy: RetryPolicy { - /// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters. - /// - /// - Parameters: - /// - retryLimit: The total number of times the request is allowed to be retried. - /// `RetryPolicy.defaultRetryLimit` by default. - /// - exponentialBackoffBase: The base of the exponential backoff policy. - /// `RetryPolicy.defaultExponentialBackoffBase` by default. - /// - exponentialBackoffScale: The scale of the exponential backoff. - /// `RetryPolicy.defaultExponentialBackoffScale` by default. - /// - retryableHTTPMethods: The idempotent http methods to retry. - /// `RetryPolicy.defaultRetryableHTTPMethods` by default. - public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit, - exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase, - exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale, - retryableHTTPMethods: Set = RetryPolicy.defaultRetryableHTTPMethods) { - super.init(retryLimit: retryLimit, - exponentialBackoffBase: exponentialBackoffBase, - exponentialBackoffScale: exponentialBackoffScale, - retryableHTTPMethods: retryableHTTPMethods, - retryableHTTPStatusCodes: [], - retryableURLErrorCodes: [.networkConnectionLost]) - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift deleted file mode 100644 index 9ca94f16..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/ServerTrustEvaluation.swift +++ /dev/null @@ -1,619 +0,0 @@ -// -// ServerTrustPolicy.swift -// -// Copyright (c) 2014-2016 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 `ServerTrustEvaluating` values to given hosts. -open class ServerTrustManager { - /// Determines whether all hosts for this `ServerTrustManager` must be evaluated. `true` by default. - public let allHostsMustBeEvaluated: Bool - - /// The dictionary of policies mapped to a particular host. - public let evaluators: [String: ServerTrustEvaluating] - - /// Initializes the `ServerTrustManager` instance with the given evaluators. - /// - /// Since different servers and web services can have different leaf certificates, intermediate and even root - /// certificates, 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. - /// - /// - Parameters: - /// - allHostsMustBeEvaluated: The value determining whether all hosts for this instance must be evaluated. `true` - /// by default. - /// - evaluators: A dictionary of evaluators mapped to hosts. - public init(allHostsMustBeEvaluated: Bool = true, evaluators: [String: ServerTrustEvaluating]) { - self.allHostsMustBeEvaluated = allHostsMustBeEvaluated - self.evaluators = evaluators - } - - /// Returns the `ServerTrustEvaluating` value for the given host, if one is set. - /// - /// 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 `ServerTrustEvaluating` value for the given host if found, `nil` otherwise. - /// - Throws: `AFError.serverTrustEvaluationFailed` if `allHostsMustBeEvaluated` is `true` and no matching - /// evaluators are found. - open func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? { - guard let evaluator = evaluators[host] else { - if allHostsMustBeEvaluated { - throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host)) - } - - return nil - } - - return evaluator - } -} - -/// A protocol describing the API used to evaluate server trusts. -public protocol ServerTrustEvaluating { - #if os(Linux) - // Implement this once Linux has API for evaluating server trusts. - #else - /// Evaluates the given `SecTrust` value for the given `host`. - /// - /// - Parameters: - /// - trust: The `SecTrust` value to evaluate. - /// - host: The host for which to evaluate the `SecTrust` value. - /// - /// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`. - func evaluate(_ trust: SecTrust, forHost host: String) throws - #endif -} - -// MARK: - Server Trust Evaluators - -/// An evaluator which 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. -public final class DefaultTrustEvaluator: ServerTrustEvaluating { - private let validateHost: Bool - - /// Creates a `DefaultTrustEvaluator`. - /// - /// - Parameter validateHost: Determines whether or not the evaluator should validate the host. `true` by default. - public init(validateHost: Bool = true) { - self.validateHost = validateHost - } - - public func evaluate(_ trust: SecTrust, forHost host: String) throws { - if validateHost { - try trust.af.performValidation(forHost: host) - } - - try trust.af.performDefaultValidation(forHost: host) - } -} - -/// An evaluator which 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. -public final class RevocationTrustEvaluator: ServerTrustEvaluating { - /// Represents the options to be use when evaluating the status of a certificate. - /// Only Revocation Policy Constants are valid, and can be found in [Apple's documentation](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/policies/1563600-revocation_policy_constants). - public struct Options: OptionSet { - /// Perform revocation checking using the CRL (Certification Revocation List) method. - public static let crl = Options(rawValue: kSecRevocationCRLMethod) - /// Consult only locally cached replies; do not use network access. - public static let networkAccessDisabled = Options(rawValue: kSecRevocationNetworkAccessDisabled) - /// Perform revocation checking using OCSP (Online Certificate Status Protocol). - public static let ocsp = Options(rawValue: kSecRevocationOCSPMethod) - /// Prefer CRL revocation checking over OCSP; by default, OCSP is preferred. - public static let preferCRL = Options(rawValue: kSecRevocationPreferCRL) - /// Require a positive response to pass the policy. If the flag is not set, revocation checking is done on a - /// "best attempt" basis, where failure to reach the server is not considered fatal. - public static let requirePositiveResponse = Options(rawValue: kSecRevocationRequirePositiveResponse) - /// Perform either OCSP or CRL checking. The checking is performed according to the method(s) specified in the - /// certificate and the value of `preferCRL`. - public static let any = Options(rawValue: kSecRevocationUseAnyAvailableMethod) - - /// The raw value of the option. - public let rawValue: CFOptionFlags - - /// Creates an `Options` value with the given `CFOptionFlags`. - /// - /// - Parameter rawValue: The `CFOptionFlags` value to initialize with. - public init(rawValue: CFOptionFlags) { - self.rawValue = rawValue - } - } - - private let performDefaultValidation: Bool - private let validateHost: Bool - private let options: Options - - /// Creates a `RevocationTrustEvaluator`. - /// - /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use - /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. - /// - /// - Parameters: - /// - performDefaultValidation: Determines whether default validation should be performed in addition to - /// evaluating the pinned certificates. `true` by default. - /// - validateHost: Determines whether or not the evaluator should validate the host, in addition - /// to performing the default evaluation, even if `performDefaultValidation` is - /// `false`. `true` by default. - /// - options: The `Options` to use to check the revocation status of the certificate. `.any` - /// by default. - public init(performDefaultValidation: Bool = true, validateHost: Bool = true, options: Options = .any) { - self.performDefaultValidation = performDefaultValidation - self.validateHost = validateHost - self.options = options - } - - public func evaluate(_ trust: SecTrust, forHost host: String) throws { - if performDefaultValidation { - try trust.af.performDefaultValidation(forHost: host) - } - - if validateHost { - try trust.af.performValidation(forHost: host) - } - - if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) { - try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options)) - } else { - try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in - AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options)) - } - } - } -} - -/// 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. -public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating { - private let certificates: [SecCertificate] - private let acceptSelfSignedCertificates: Bool - private let performDefaultValidation: Bool - private let validateHost: Bool - - /// Creates a `PinnedCertificatesTrustEvaluator`. - /// - /// - Parameters: - /// - certificates: The certificates to use to evaluate the trust. All `cer`, `crt`, and `der` - /// certificates in `Bundle.main` by default. - /// - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing - /// self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE - /// FALSE IN PRODUCTION! - /// - performDefaultValidation: Determines whether default validation should be performed in addition to - /// evaluating the pinned certificates. `true` by default. - /// - validateHost: Determines whether or not the evaluator should validate the host, in addition - /// to performing the default evaluation, even if `performDefaultValidation` is - /// `false`. `true` by default. - public init(certificates: [SecCertificate] = Bundle.main.af.certificates, - acceptSelfSignedCertificates: Bool = false, - performDefaultValidation: Bool = true, - validateHost: Bool = true) { - self.certificates = certificates - self.acceptSelfSignedCertificates = acceptSelfSignedCertificates - self.performDefaultValidation = performDefaultValidation - self.validateHost = validateHost - } - - public func evaluate(_ trust: SecTrust, forHost host: String) throws { - guard !certificates.isEmpty else { - throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound) - } - - if acceptSelfSignedCertificates { - try trust.af.setAnchorCertificates(certificates) - } - - if performDefaultValidation { - try trust.af.performDefaultValidation(forHost: host) - } - - if validateHost { - try trust.af.performValidation(forHost: host) - } - - let serverCertificatesData = Set(trust.af.certificateData) - let pinnedCertificatesData = Set(certificates.af.data) - let pinnedCertificatesInServerData = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData) - if !pinnedCertificatesInServerData { - throw AFError.serverTrustEvaluationFailed(reason: .certificatePinningFailed(host: host, - trust: trust, - pinnedCertificates: certificates, - serverCertificates: trust.af.certificates)) - } - } -} - -/// 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. -public final class PublicKeysTrustEvaluator: ServerTrustEvaluating { - private let keys: [SecKey] - private let performDefaultValidation: Bool - private let validateHost: Bool - - /// Creates a `PublicKeysTrustEvaluator`. - /// - /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use - /// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates. - /// - /// - Parameters: - /// - keys: The `SecKey`s to use to validate public keys. Defaults to the public keys of all - /// certificates included in the main bundle. - /// - performDefaultValidation: Determines whether default validation should be performed in addition to - /// evaluating the pinned certificates. `true` by default. - /// - validateHost: Determines whether or not the evaluator should validate the host, in addition to - /// performing the default evaluation, even if `performDefaultValidation` is `false`. - /// `true` by default. - public init(keys: [SecKey] = Bundle.main.af.publicKeys, - performDefaultValidation: Bool = true, - validateHost: Bool = true) { - self.keys = keys - self.performDefaultValidation = performDefaultValidation - self.validateHost = validateHost - } - - public func evaluate(_ trust: SecTrust, forHost host: String) throws { - guard !keys.isEmpty else { - throw AFError.serverTrustEvaluationFailed(reason: .noPublicKeysFound) - } - - if performDefaultValidation { - try trust.af.performDefaultValidation(forHost: host) - } - - if validateHost { - try trust.af.performValidation(forHost: host) - } - - let pinnedKeysInServerKeys: Bool = { - for serverPublicKey in trust.af.publicKeys { - for pinnedPublicKey in keys { - if serverPublicKey == pinnedPublicKey { - return true - } - } - } - return false - }() - - if !pinnedKeysInServerKeys { - throw AFError.serverTrustEvaluationFailed(reason: .publicKeyPinningFailed(host: host, - trust: trust, - pinnedKeys: keys, - serverKeys: trust.af.publicKeys)) - } - } -} - -/// Uses the provided evaluators to validate the server trust. The trust is only considered valid if all of the -/// evaluators consider it valid. -public final class CompositeTrustEvaluator: ServerTrustEvaluating { - private let evaluators: [ServerTrustEvaluating] - - /// Creates a `CompositeTrustEvaluator`. - /// - /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust. - public init(evaluators: [ServerTrustEvaluating]) { - self.evaluators = evaluators - } - - public func evaluate(_ trust: SecTrust, forHost host: String) throws { - try evaluators.evaluate(trust, forHost: host) - } -} - -/// Disables all evaluation which in turn will always consider any server trust as valid. -/// -/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test -/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html). -/// -/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!** -@available(*, deprecated, renamed: "DisabledTrustEvaluator", message: "DisabledEvaluator has been renamed DisabledTrustEvaluator.") -public typealias DisabledEvaluator = DisabledTrustEvaluator - -/// Disables all evaluation which in turn will always consider any server trust as valid. -/// -/// -/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test -/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html). -/// -/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!** -public final class DisabledTrustEvaluator: ServerTrustEvaluating { - /// Creates an instance. - public init() {} - - public func evaluate(_ trust: SecTrust, forHost host: String) throws {} -} - -// MARK: - Extensions - -extension Array where Element == ServerTrustEvaluating { - #if os(Linux) - // Add this same convenience method for Linux. - #else - /// Evaluates the given `SecTrust` value for the given `host`. - /// - /// - Parameters: - /// - trust: The `SecTrust` value to evaluate. - /// - host: The host for which to evaluate the `SecTrust` value. - /// - /// - Returns: Whether or not the evaluator considers the `SecTrust` value valid for `host`. - public func evaluate(_ trust: SecTrust, forHost host: String) throws { - for evaluator in self { - try evaluator.evaluate(trust, forHost: host) - } - } - #endif -} - -extension Bundle: AlamofireExtended {} -extension AlamofireExtension where ExtendedType: Bundle { - /// Returns all valid `cer`, `crt`, and `der` certificates in the bundle. - public var certificates: [SecCertificate] { - paths(forResourcesOfTypes: [".cer", ".CER", ".crt", ".CRT", ".der", ".DER"]).compactMap { path in - guard - let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, - let certificate = SecCertificateCreateWithData(nil, certificateData) else { return nil } - - return certificate - } - } - - /// Returns all public keys for the valid certificates in the bundle. - public var publicKeys: [SecKey] { - certificates.af.publicKeys - } - - /// Returns all pathnames for the resources identified by the provided file extensions. - /// - /// - Parameter types: The filename extensions locate. - /// - /// - Returns: All pathnames for the given filename extensions. - public func paths(forResourcesOfTypes types: [String]) -> [String] { - Array(Set(types.flatMap { type.paths(forResourcesOfType: $0, inDirectory: nil) })) - } -} - -extension SecTrust: AlamofireExtended {} -extension AlamofireExtension where ExtendedType == SecTrust { - /// Evaluates `self` after applying the `SecPolicy` value provided. - /// - /// - Parameter policy: The `SecPolicy` to apply to `self` before evaluation. - /// - /// - Throws: Any `Error` from applying the `SecPolicy` or from evaluation. - @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) - public func evaluate(afterApplying policy: SecPolicy) throws { - try apply(policy: policy).af.evaluate() - } - - /// Attempts to validate `self` using the `SecPolicy` provided and transforming any error produced using the closure passed. - /// - /// - Parameters: - /// - policy: The `SecPolicy` used to evaluate `self`. - /// - errorProducer: The closure used transform the failed `OSStatus` and `SecTrustResultType`. - /// - Throws: Any `Error` from applying the `policy`, or the result of `errorProducer` if validation fails. - @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)") - @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate(afterApplying:)") - @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)") - @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate(afterApplying:)") - public func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { - try apply(policy: policy).af.validate(errorProducer: errorProducer) - } - - /// Applies a `SecPolicy` to `self`, throwing if it fails. - /// - /// - Parameter policy: The `SecPolicy`. - /// - /// - Returns: `self`, with the policy applied. - /// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.policyApplicationFailed` reason. - public func apply(policy: SecPolicy) throws -> SecTrust { - let status = SecTrustSetPolicies(type, policy) - - guard status.af.isSuccess else { - throw AFError.serverTrustEvaluationFailed(reason: .policyApplicationFailed(trust: type, - policy: policy, - status: status)) - } - - return type - } - - /// Evaluate `self`, throwing an `Error` if evaluation fails. - /// - /// - Throws: `AFError.serverTrustEvaluationFailed` with reason `.trustValidationFailed` and associated error from - /// the underlying evaluation. - @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) - public func evaluate() throws { - var error: CFError? - let evaluationSucceeded = SecTrustEvaluateWithError(type, &error) - - if !evaluationSucceeded { - throw AFError.serverTrustEvaluationFailed(reason: .trustEvaluationFailed(error: error)) - } - } - - /// Validate `self`, passing any failure values through `errorProducer`. - /// - /// - Parameter errorProducer: The closure used to transform the failed `OSStatus` and `SecTrustResultType` into an - /// `Error`. - /// - Throws: The `Error` produced by the `errorProducer` closure. - @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate()") - @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate()") - @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate()") - @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate()") - public func validate(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { - var result = SecTrustResultType.invalid - let status = SecTrustEvaluate(type, &result) - - guard status.af.isSuccess && result.af.isSuccess else { - throw errorProducer(status, result) - } - } - - /// Sets a custom certificate chain on `self`, allowing full validation of a self-signed certificate and its chain. - /// - /// - Parameter certificates: The `SecCertificate`s to add to the chain. - /// - Throws: Any error produced when applying the new certificate chain. - public func setAnchorCertificates(_ certificates: [SecCertificate]) throws { - // Add additional anchor certificates. - let status = SecTrustSetAnchorCertificates(type, certificates as CFArray) - guard status.af.isSuccess else { - throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: status, - certificates: certificates)) - } - - // Trust only the set anchor certs. - let onlyStatus = SecTrustSetAnchorCertificatesOnly(type, true) - guard onlyStatus.af.isSuccess else { - throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: onlyStatus, - certificates: certificates)) - } - } - - /// The public keys contained in `self`. - public var publicKeys: [SecKey] { - certificates.af.publicKeys - } - - /// The `SecCertificate`s contained i `self`. - public var certificates: [SecCertificate] { - (0.. SecPolicy { - SecPolicyCreateSSL(true, hostname as CFString) - } - - /// Creates a `SecPolicy` which checks the revocation of certificates. - /// - /// - Parameter options: The `RevocationTrustEvaluator.Options` for evaluation. - /// - /// - Returns: The `SecPolicy`. - /// - Throws: An `AFError.serverTrustEvaluationFailed` error with reason `.revocationPolicyCreationFailed` - /// if the policy cannot be created. - public static func revocation(options: RevocationTrustEvaluator.Options) throws -> SecPolicy { - guard let policy = SecPolicyCreateRevocation(options.rawValue) else { - throw AFError.serverTrustEvaluationFailed(reason: .revocationPolicyCreationFailed) - } - - return policy - } -} - -extension Array: AlamofireExtended {} -extension AlamofireExtension where ExtendedType == [SecCertificate] { - /// All `Data` values for the contained `SecCertificate`s. - public var data: [Data] { - type.map { SecCertificateCopyData($0) as Data } - } - - /// All public `SecKey` values for the contained `SecCertificate`s. - public var publicKeys: [SecKey] { - type.compactMap { $0.af.publicKey } - } -} - -extension SecCertificate: AlamofireExtended {} -extension AlamofireExtension where ExtendedType == SecCertificate { - /// The public key for `self`, if it can be extracted. - public var publicKey: SecKey? { - let policy = SecPolicyCreateBasicX509() - var trust: SecTrust? - let trustCreationStatus = SecTrustCreateWithCertificates(type, policy, &trust) - - guard let createdTrust = trust, trustCreationStatus == errSecSuccess else { return nil } - - return SecTrustCopyPublicKey(createdTrust) - } -} - -extension OSStatus: AlamofireExtended {} -extension AlamofireExtension where ExtendedType == OSStatus { - /// Returns whether `self` is `errSecSuccess`. - public var isSuccess: Bool { type == errSecSuccess } -} - -extension SecTrustResultType: AlamofireExtended {} -extension AlamofireExtension where ExtendedType == SecTrustResultType { - /// Returns whether `self is `.unspecified` or `.proceed`. - public var isSuccess: Bool { - type == .unspecified || type == .proceed - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift deleted file mode 100644 index ac0ab229..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Session.swift +++ /dev/null @@ -1,1258 +0,0 @@ -// -// Session.swift -// -// Copyright (c) 2014-2018 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 - -/// `Session` creates and manages Alamofire's `Request` types during their lifetimes. It also provides common -/// functionality for all `Request`s, including queuing, interception, trust management, redirect handling, and response -/// cache handling. -open class Session { - /// Shared singleton instance used by all `AF.request` APIs. Cannot be modified. - public static let `default` = Session() - - /// Underlying `URLSession` used to create `URLSessionTasks` for this instance, and for which this instance's - /// `delegate` handles `URLSessionDelegate` callbacks. - /// - /// - Note: This instance should **NOT** be used to interact with the underlying `URLSessionTask`s. Doing so will - /// break internal Alamofire logic that tracks those tasks. - /// - public let session: URLSession - /// Instance's `SessionDelegate`, which handles the `URLSessionDelegate` methods and `Request` interaction. - public let delegate: SessionDelegate - /// Root `DispatchQueue` for all internal callbacks and state update. **MUST** be a serial queue. - public let rootQueue: DispatchQueue - /// Value determining whether this instance automatically calls `resume()` on all created `Request`s. - public let startRequestsImmediately: Bool - /// `DispatchQueue` on which `URLRequest`s are created asynchronously. By default this queue uses `rootQueue` as its - /// `target`, but a separate queue can be used if request creation is determined to be a bottleneck. Always profile - /// and test before introducing an additional queue. - public let requestQueue: DispatchQueue - /// `DispatchQueue` passed to all `Request`s on which they perform their response serialization. By default this - /// queue uses `rootQueue` as its `target` but a separate queue can be used if response serialization is determined - /// to be a bottleneck. Always profile and test before introducing an additional queue. - public let serializationQueue: DispatchQueue - /// `RequestInterceptor` used for all `Request` created by the instance. `RequestInterceptor`s can also be set on a - /// per-`Request` basis, in which case the `Request`'s interceptor takes precedence over this value. - public let interceptor: RequestInterceptor? - /// `ServerTrustManager` instance used to evaluate all trust challenges and provide certificate and key pinning. - public let serverTrustManager: ServerTrustManager? - /// `RedirectHandler` instance used to provide customization for request redirection. - public let redirectHandler: RedirectHandler? - /// `CachedResponseHandler` instance used to provide customization of cached response handling. - public let cachedResponseHandler: CachedResponseHandler? - /// `CompositeEventMonitor` used to compose Alamofire's `defaultEventMonitors` and any passed `EventMonitor`s. - public let eventMonitor: CompositeEventMonitor - /// `EventMonitor`s included in all instances. `[AlamofireNotifications()]` by default. - public let defaultEventMonitors: [EventMonitor] = [AlamofireNotifications()] - - /// Internal map between `Request`s and any `URLSessionTasks` that may be in flight for them. - var requestTaskMap = RequestTaskMap() - /// `Set` of currently active `Request`s. - var activeRequests: Set = [] - /// Completion events awaiting `URLSessionTaskMetrics`. - var waitingCompletions: [URLSessionTask: () -> Void] = [:] - - /// Creates a `Session` from a `URLSession` and other parameters. - /// - /// - Note: When passing a `URLSession`, you must create the `URLSession` with a specific `delegateQueue` value and - /// pass the `delegateQueue`'s `underlyingQueue` as the `rootQueue` parameter of this initializer. - /// - /// - Parameters: - /// - session: Underlying `URLSession` for this instance. - /// - delegate: `SessionDelegate` that handles `session`'s delegate callbacks as well as `Request` - /// interaction. - /// - rootQueue: Root `DispatchQueue` for all internal callbacks and state updates. **MUST** be a - /// serial queue. - /// - startRequestsImmediately: Determines whether this instance will automatically start all `Request`s. `true` - /// by default. If set to `false`, all `Request`s created must have `.resume()` called. - /// on them for them to start. - /// - requestQueue: `DispatchQueue` on which to perform `URLRequest` creation. By default this queue - /// will use the `rootQueue` as its `target`. A separate queue can be used if it's - /// determined request creation is a bottleneck, but that should only be done after - /// careful testing and profiling. `nil` by default. - /// - serializationQueue: `DispatchQueue` on which to perform all response serialization. By default this - /// queue will use the `rootQueue` as its `target`. A separate queue can be used if - /// it's determined response serialization is a bottleneck, but that should only be - /// done after careful testing and profiling. `nil` by default. - /// - interceptor: `RequestInterceptor` to be used for all `Request`s created by this instance. `nil` - /// by default. - /// - serverTrustManager: `ServerTrustManager` to be used for all trust evaluations by this instance. `nil` - /// by default. - /// - redirectHandler: `RedirectHandler` to be used by all `Request`s created by this instance. `nil` by - /// default. - /// - cachedResponseHandler: `CachedResponseHandler` to be used by all `Request`s created by this instance. - /// `nil` by default. - /// - eventMonitors: Additional `EventMonitor`s used by the instance. Alamofire always adds a - /// `AlamofireNotifications` `EventMonitor` to the array passed here. `[]` by default. - public init(session: URLSession, - delegate: SessionDelegate, - rootQueue: DispatchQueue, - startRequestsImmediately: Bool = true, - requestQueue: DispatchQueue? = nil, - serializationQueue: DispatchQueue? = nil, - interceptor: RequestInterceptor? = nil, - serverTrustManager: ServerTrustManager? = nil, - redirectHandler: RedirectHandler? = nil, - cachedResponseHandler: CachedResponseHandler? = nil, - eventMonitors: [EventMonitor] = []) { - precondition(session.configuration.identifier == nil, - "Alamofire does not support background URLSessionConfigurations.") - precondition(session.delegateQueue.underlyingQueue === rootQueue, - "Session(session:) initializer must be passed the DispatchQueue used as the delegateQueue's underlyingQueue as rootQueue.") - - self.session = session - self.delegate = delegate - self.rootQueue = rootQueue - self.startRequestsImmediately = startRequestsImmediately - self.requestQueue = requestQueue ?? DispatchQueue(label: "\(rootQueue.label).requestQueue", target: rootQueue) - self.serializationQueue = serializationQueue ?? DispatchQueue(label: "\(rootQueue.label).serializationQueue", target: rootQueue) - self.interceptor = interceptor - self.serverTrustManager = serverTrustManager - self.redirectHandler = redirectHandler - self.cachedResponseHandler = cachedResponseHandler - eventMonitor = CompositeEventMonitor(monitors: defaultEventMonitors + eventMonitors) - delegate.eventMonitor = eventMonitor - delegate.stateProvider = self - } - - /// Creates a `Session` from a `URLSessionConfiguration`. - /// - /// - Note: This initializer lets Alamofire handle the creation of the underlying `URLSession` and its - /// `delegateQueue`, and is the recommended initializer for most uses. - /// - /// - Parameters: - /// - configuration: `URLSessionConfiguration` to be used to create the underlying `URLSession`. Changes - /// to this value after being passed to this initializer will have no effect. - /// `URLSessionConfiguration.af.default` by default. - /// - delegate: `SessionDelegate` that handles `session`'s delegate callbacks as well as `Request` - /// interaction. `SessionDelegate()` by default. - /// - rootQueue: Root `DispatchQueue` for all internal callbacks and state updates. **MUST** be a - /// serial queue. `DispatchQueue(label: "org.alamofire.session.rootQueue")` by default. - /// - startRequestsImmediately: Determines whether this instance will automatically start all `Request`s. `true` - /// by default. If set to `false`, all `Request`s created must have `.resume()` called. - /// on them for them to start. - /// - requestQueue: `DispatchQueue` on which to perform `URLRequest` creation. By default this queue - /// will use the `rootQueue` as its `target`. A separate queue can be used if it's - /// determined request creation is a bottleneck, but that should only be done after - /// careful testing and profiling. `nil` by default. - /// - serializationQueue: `DispatchQueue` on which to perform all response serialization. By default this - /// queue will use the `rootQueue` as its `target`. A separate queue can be used if - /// it's determined response serialization is a bottleneck, but that should only be - /// done after careful testing and profiling. `nil` by default. - /// - interceptor: `RequestInterceptor` to be used for all `Request`s created by this instance. `nil` - /// by default. - /// - serverTrustManager: `ServerTrustManager` to be used for all trust evaluations by this instance. `nil` - /// by default. - /// - redirectHandler: `RedirectHandler` to be used by all `Request`s created by this instance. `nil` by - /// default. - /// - cachedResponseHandler: `CachedResponseHandler` to be used by all `Request`s created by this instance. - /// `nil` by default. - /// - eventMonitors: Additional `EventMonitor`s used by the instance. Alamofire always adds a - /// `AlamofireNotifications` `EventMonitor` to the array passed here. `[]` by default. - public convenience init(configuration: URLSessionConfiguration = URLSessionConfiguration.af.default, - delegate: SessionDelegate = SessionDelegate(), - rootQueue: DispatchQueue = DispatchQueue(label: "org.alamofire.session.rootQueue"), - startRequestsImmediately: Bool = true, - requestQueue: DispatchQueue? = nil, - serializationQueue: DispatchQueue? = nil, - interceptor: RequestInterceptor? = nil, - serverTrustManager: ServerTrustManager? = nil, - redirectHandler: RedirectHandler? = nil, - cachedResponseHandler: CachedResponseHandler? = nil, - eventMonitors: [EventMonitor] = []) { - precondition(configuration.identifier == nil, "Alamofire does not support background URLSessionConfigurations.") - - let delegateQueue = OperationQueue(maxConcurrentOperationCount: 1, underlyingQueue: rootQueue, name: "org.alamofire.session.sessionDelegateQueue") - let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue) - - self.init(session: session, - delegate: delegate, - rootQueue: rootQueue, - startRequestsImmediately: startRequestsImmediately, - requestQueue: requestQueue, - serializationQueue: serializationQueue, - interceptor: interceptor, - serverTrustManager: serverTrustManager, - redirectHandler: redirectHandler, - cachedResponseHandler: cachedResponseHandler, - eventMonitors: eventMonitors) - } - - deinit { - finishRequestsForDeinit() - session.invalidateAndCancel() - } - - // MARK: - All Requests API - - /// Perform an action on all active `Request`s. - /// - /// - Note: The provided `action` closure is performed asynchronously, meaning that some `Request`s may complete and - /// be unavailable by time it runs. Additionally, this action is performed on the instances's `rootQueue`, - /// so care should be taken that actions are fast. Once the work on the `Request`s is complete, any - /// additional work should be performed on another queue. - /// - /// - Parameters: - /// - action: Closure to perform with all `Request`s. - public func withAllRequests(perform action: @escaping (Set) -> Void) { - rootQueue.async { - action(self.activeRequests) - } - } - - /// Cancel all active `Request`s, optionally calling a completion handler when complete. - /// - /// - Note: This is an asynchronous operation and does not block the creation of future `Request`s. Cancelled - /// `Request`s may not cancel immediately due internal work, and may not cancel at all if they are close to - /// completion when cancelled. - /// - /// - Parameters: - /// - queue: `DispatchQueue` on which the completion handler is run. `.main` by default. - /// - completion: Closure to be called when all `Request`s have been cancelled. - public func cancelAllRequests(completingOnQueue queue: DispatchQueue = .main, completion: (() -> Void)? = nil) { - withAllRequests { requests in - requests.forEach { $0.cancel() } - queue.async { - completion?() - } - } - } - - // MARK: - DataRequest - - /// Closure which provides a `URLRequest` for mutation. - public typealias RequestModifier = (inout URLRequest) throws -> Void - - struct RequestConvertible: URLRequestConvertible { - let url: URLConvertible - let method: HTTPMethod - let parameters: Parameters? - let encoding: ParameterEncoding - let headers: HTTPHeaders? - let requestModifier: RequestModifier? - - func asURLRequest() throws -> URLRequest { - var request = try URLRequest(url: url, method: method, headers: headers) - try requestModifier?(&request) - - return try encoding.encode(request, with: parameters) - } - } - - /// Creates a `DataRequest` from a `URLRequest` created using the passed components and a `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. - /// - parameters: `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by - /// default. - /// - encoding: `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`. - /// `URLEncoding.default` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided - /// parameters. `nil` by default. - /// - /// - Returns: The created `DataRequest`. - open func request(_ convertible: URLConvertible, - method: HTTPMethod = .get, - parameters: Parameters? = nil, - encoding: ParameterEncoding = URLEncoding.default, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - requestModifier: RequestModifier? = nil) -> DataRequest { - let convertible = RequestConvertible(url: convertible, - method: method, - parameters: parameters, - encoding: encoding, - headers: headers, - requestModifier: requestModifier) - - return request(convertible, interceptor: interceptor) - } - - struct RequestEncodableConvertible: URLRequestConvertible { - let url: URLConvertible - let method: HTTPMethod - let parameters: Parameters? - let encoder: ParameterEncoder - let headers: HTTPHeaders? - let requestModifier: RequestModifier? - - func asURLRequest() throws -> URLRequest { - var request = try URLRequest(url: url, method: method, headers: headers) - try requestModifier?(&request) - - return try parameters.map { try encoder.encode($0, into: request) } ?? request - } - } - - /// Creates a `DataRequest` from a `URLRequest` created using the passed components, `Encodable` parameters, and a - /// `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. - /// - parameters: `Encodable` value to be encoded into the `URLRequest`. `nil` by default. - /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the `URLRequest`. - /// `URLEncodedFormParameterEncoder.default` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - /// - Returns: The created `DataRequest`. - open func request(_ convertible: URLConvertible, - method: HTTPMethod = .get, - parameters: Parameters? = nil, - encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - requestModifier: RequestModifier? = nil) -> DataRequest { - let convertible = RequestEncodableConvertible(url: convertible, - method: method, - parameters: parameters, - encoder: encoder, - headers: headers, - requestModifier: requestModifier) - - return request(convertible, interceptor: interceptor) - } - - /// Creates a `DataRequest` from a `URLRequestConvertible` value and a `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - /// - Returns: The created `DataRequest`. - open func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest { - let request = DataRequest(convertible: convertible, - underlyingQueue: rootQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: self) - - perform(request) - - return request - } - - // MARK: - DataStreamRequest - - /// Creates a `DataStreamRequest` from the passed components, `Encodable` parameters, and `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. - /// - parameters: `Encodable` value to be encoded into the `URLRequest`. `nil` by default. - /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the - /// `URLRequest`. - /// `URLEncodedFormParameterEncoder.default` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` - /// is thrown while serializing stream `Data`. `false` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` - /// by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from - /// the provided parameters. `nil` by default. - /// - /// - Returns: The created `DataStream` request. - open func streamRequest(_ convertible: URLConvertible, - method: HTTPMethod = .get, - parameters: Parameters? = nil, - encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, - headers: HTTPHeaders? = nil, - automaticallyCancelOnStreamError: Bool = false, - interceptor: RequestInterceptor? = nil, - requestModifier: RequestModifier? = nil) -> DataStreamRequest { - let convertible = RequestEncodableConvertible(url: convertible, - method: method, - parameters: parameters, - encoder: encoder, - headers: headers, - requestModifier: requestModifier) - - return streamRequest(convertible, - automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, - interceptor: interceptor) - } - - /// Creates a `DataStreamRequest` from the passed components and `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` - /// is thrown while serializing stream `Data`. `false` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` - /// by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from - /// the provided parameters. `nil` by default. - /// - /// - Returns: The created `DataStream` request. - open func streamRequest(_ convertible: URLConvertible, - method: HTTPMethod = .get, - headers: HTTPHeaders? = nil, - automaticallyCancelOnStreamError: Bool = false, - interceptor: RequestInterceptor? = nil, - requestModifier: RequestModifier? = nil) -> DataStreamRequest { - let convertible = RequestEncodableConvertible(url: convertible, - method: method, - parameters: Empty?.none, - encoder: URLEncodedFormParameterEncoder.default, - headers: headers, - requestModifier: requestModifier) - - return streamRequest(convertible, - automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, - interceptor: interceptor) - } - - /// Creates a `DataStreamRequest` from the passed `URLRequestConvertible` value and `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - automaticallyCancelOnStreamError: `Bool` indicating whether the instance should be canceled when an `Error` - /// is thrown while serializing stream `Data`. `false` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` - /// by default. - /// - /// - Returns: The created `DataStreamRequest`. - open func streamRequest(_ convertible: URLRequestConvertible, - automaticallyCancelOnStreamError: Bool = false, - interceptor: RequestInterceptor? = nil) -> DataStreamRequest { - let request = DataStreamRequest(convertible: convertible, - automaticallyCancelOnStreamError: automaticallyCancelOnStreamError, - underlyingQueue: rootQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: self) - - perform(request) - - return request - } - - // MARK: - DownloadRequest - - /// Creates a `DownloadRequest` using a `URLRequest` created using the passed components, `RequestInterceptor`, and - /// `Destination`. - /// - /// - Parameters: - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. - /// - parameters: `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by - /// default. - /// - encoding: `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`. - /// Defaults to `URLEncoding.default`. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided - /// parameters. `nil` by default. - /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file - /// should be moved. `nil` by default. - /// - /// - Returns: The created `DownloadRequest`. - open func download(_ convertible: URLConvertible, - method: HTTPMethod = .get, - parameters: Parameters? = nil, - encoding: ParameterEncoding = URLEncoding.default, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - requestModifier: RequestModifier? = nil, - to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { - let convertible = RequestConvertible(url: convertible, - method: method, - parameters: parameters, - encoding: encoding, - headers: headers, - requestModifier: requestModifier) - - return download(convertible, interceptor: interceptor, to: destination) - } - - /// Creates a `DownloadRequest` from a `URLRequest` created using the passed components, `Encodable` parameters, and - /// a `RequestInterceptor`. - /// - /// - Parameters: - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.get` by default. - /// - parameters: Value conforming to `Encodable` to be encoded into the `URLRequest`. `nil` by default. - /// - encoder: `ParameterEncoder` to be used to encode the `parameters` value into the `URLRequest`. - /// Defaults to `URLEncodedFormParameterEncoder.default`. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided - /// parameters. `nil` by default. - /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file - /// should be moved. `nil` by default. - /// - /// - Returns: The created `DownloadRequest`. - open func download(_ convertible: URLConvertible, - method: HTTPMethod = .get, - parameters: Parameters? = nil, - encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - requestModifier: RequestModifier? = nil, - to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { - let convertible = RequestEncodableConvertible(url: convertible, - method: method, - parameters: parameters, - encoder: encoder, - headers: headers, - requestModifier: requestModifier) - - return download(convertible, interceptor: interceptor, to: destination) - } - - /// Creates a `DownloadRequest` from a `URLRequestConvertible` value, a `RequestInterceptor`, and a `Destination`. - /// - /// - Parameters: - /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file - /// should be moved. `nil` by default. - /// - /// - Returns: The created `DownloadRequest`. - open func download(_ convertible: URLRequestConvertible, - interceptor: RequestInterceptor? = nil, - to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { - let request = DownloadRequest(downloadable: .request(convertible), - underlyingQueue: rootQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: self, - destination: destination ?? DownloadRequest.defaultDestination) - - perform(request) - - return request - } - - /// Creates a `DownloadRequest` from the `resumeData` produced from a previously cancelled `DownloadRequest`, as - /// well as a `RequestInterceptor`, and a `Destination`. - /// - /// - Note: If `destination` is not specified, the download will be moved to a temporary location determined by - /// Alamofire. The file will not be deleted until the system purges the temporary files. - /// - /// - Note: On some versions of all Apple platforms (iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1), - /// `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 [this Stack Overflow post](http://stackoverflow.com/a/39347461/1342462). - /// - /// - Parameters: - /// - data: The resume data from a previously cancelled `DownloadRequest` or `URLSessionDownloadTask`. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - destination: `DownloadRequest.Destination` closure used to determine how and where the downloaded file - /// should be moved. `nil` by default. - /// - /// - Returns: The created `DownloadRequest`. - open func download(resumingWith data: Data, - interceptor: RequestInterceptor? = nil, - to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { - let request = DownloadRequest(downloadable: .resumeData(data), - underlyingQueue: rootQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - delegate: self, - destination: destination ?? DownloadRequest.defaultDestination) - - perform(request) - - return request - } - - // MARK: - UploadRequest - - struct ParameterlessRequestConvertible: URLRequestConvertible { - let url: URLConvertible - let method: HTTPMethod - let headers: HTTPHeaders? - let requestModifier: RequestModifier? - - func asURLRequest() throws -> URLRequest { - var request = try URLRequest(url: url, method: method, headers: headers) - try requestModifier?(&request) - - return request - } - } - - struct Upload: UploadConvertible { - let request: URLRequestConvertible - let uploadable: UploadableConvertible - - func createUploadable() throws -> UploadRequest.Uploadable { - try uploadable.createUploadable() - } - - func asURLRequest() throws -> URLRequest { - try request.asURLRequest() - } - } - - // MARK: Data - - /// Creates an `UploadRequest` for the given `Data`, `URLRequest` components, and `RequestInterceptor`. - /// - /// - Parameters: - /// - data: The `Data` to upload. - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided - /// parameters. `nil` by default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(_ data: Data, - to convertible: URLConvertible, - method: HTTPMethod = .post, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default, - requestModifier: RequestModifier? = nil) -> UploadRequest { - let convertible = ParameterlessRequestConvertible(url: convertible, - method: method, - headers: headers, - requestModifier: requestModifier) - - return upload(data, with: convertible, interceptor: interceptor, fileManager: fileManager) - } - - /// Creates an `UploadRequest` for the given `Data` using the `URLRequestConvertible` value and `RequestInterceptor`. - /// - /// - Parameters: - /// - data: The `Data` to upload. - /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(_ data: Data, - with convertible: URLRequestConvertible, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default) -> UploadRequest { - upload(.data(data), with: convertible, interceptor: interceptor, fileManager: fileManager) - } - - // MARK: File - - /// Creates an `UploadRequest` for the file at the given file `URL`, using a `URLRequest` from the provided - /// components and `RequestInterceptor`. - /// - /// - Parameters: - /// - fileURL: The `URL` of the file to upload. - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `UploadRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided - /// parameters. `nil` by default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(_ fileURL: URL, - to convertible: URLConvertible, - method: HTTPMethod = .post, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default, - requestModifier: RequestModifier? = nil) -> UploadRequest { - let convertible = ParameterlessRequestConvertible(url: convertible, - method: method, - headers: headers, - requestModifier: requestModifier) - - return upload(fileURL, with: convertible, interceptor: interceptor, fileManager: fileManager) - } - - /// Creates an `UploadRequest` for the file at the given file `URL` using the `URLRequestConvertible` value and - /// `RequestInterceptor`. - /// - /// - Parameters: - /// - fileURL: The `URL` of the file to upload. - /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(_ fileURL: URL, - with convertible: URLRequestConvertible, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default) -> UploadRequest { - upload(.file(fileURL, shouldRemove: false), with: convertible, interceptor: interceptor, fileManager: fileManager) - } - - // MARK: InputStream - - /// Creates an `UploadRequest` from the `InputStream` provided using a `URLRequest` from the provided components and - /// `RequestInterceptor`. - /// - /// - Parameters: - /// - stream: The `InputStream` that provides the data to upload. - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided - /// parameters. `nil` by default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(_ stream: InputStream, - to convertible: URLConvertible, - method: HTTPMethod = .post, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default, - requestModifier: RequestModifier? = nil) -> UploadRequest { - let convertible = ParameterlessRequestConvertible(url: convertible, - method: method, - headers: headers, - requestModifier: requestModifier) - - return upload(stream, with: convertible, interceptor: interceptor, fileManager: fileManager) - } - - /// Creates an `UploadRequest` from the provided `InputStream` using the `URLRequestConvertible` value and - /// `RequestInterceptor`. - /// - /// - Parameters: - /// - stream: The `InputStream` that provides the data to upload. - /// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(_ stream: InputStream, - with convertible: URLRequestConvertible, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default) -> UploadRequest { - upload(.stream(stream), with: convertible, interceptor: interceptor, fileManager: fileManager) - } - - // MARK: MultipartFormData - - /// Creates an `UploadRequest` for the multipart form data built using a closure and sent using the provided - /// `URLRequest` components and `RequestInterceptor`. - /// - /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative - /// 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. - /// - /// - Parameters: - /// - multipartFormData: `MultipartFormData` building closure. - /// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or - /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by - /// default. - /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is - /// written to disk before being uploaded. `.default` instance by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the - /// provided parameters. `nil` by default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(multipartFormData: @escaping (MultipartFormData) -> Void, - to url: URLConvertible, - usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, - method: HTTPMethod = .post, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default, - requestModifier: RequestModifier? = nil) -> UploadRequest { - let convertible = ParameterlessRequestConvertible(url: url, - method: method, - headers: headers, - requestModifier: requestModifier) - - let formData = MultipartFormData(fileManager: fileManager) - multipartFormData(formData) - - return upload(multipartFormData: formData, - with: convertible, - usingThreshold: encodingMemoryThreshold, - interceptor: interceptor, - fileManager: fileManager) - } - - /// Creates an `UploadRequest` using a `MultipartFormData` building closure, the provided `URLRequestConvertible` - /// value, and a `RequestInterceptor`. - /// - /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative - /// 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. - /// - /// - Parameters: - /// - multipartFormData: `MultipartFormData` building closure. - /// - request: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or - /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by - /// default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is - /// written to disk before being uploaded. `.default` instance by default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(multipartFormData: @escaping (MultipartFormData) -> Void, - with request: URLRequestConvertible, - usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default) -> UploadRequest { - let formData = MultipartFormData(fileManager: fileManager) - multipartFormData(formData) - - return upload(multipartFormData: formData, - with: request, - usingThreshold: encodingMemoryThreshold, - interceptor: interceptor, - fileManager: fileManager) - } - - /// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the provided `URLRequest` components - /// and `RequestInterceptor`. - /// - /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative - /// 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. - /// - /// - Parameters: - /// - multipartFormData: `MultipartFormData` instance to upload. - /// - url: `URLConvertible` value to be used as the `URLRequest`'s `URL`. - /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or - /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by - /// default. - /// - method: `HTTPMethod` for the `URLRequest`. `.post` by default. - /// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is - /// written to disk before being uploaded. `.default` instance by default. - /// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the - /// provided parameters. `nil` by default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(multipartFormData: MultipartFormData, - to url: URLConvertible, - usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, - method: HTTPMethod = .post, - headers: HTTPHeaders? = nil, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default, - requestModifier: RequestModifier? = nil) -> UploadRequest { - let convertible = ParameterlessRequestConvertible(url: url, - method: method, - headers: headers, - requestModifier: requestModifier) - - let multipartUpload = MultipartUpload(encodingMemoryThreshold: encodingMemoryThreshold, - request: convertible, - multipartFormData: multipartFormData) - - return upload(multipartUpload, interceptor: interceptor, fileManager: fileManager) - } - - /// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the providing `URLRequestConvertible` - /// value and `RequestInterceptor`. - /// - /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative - /// 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. - /// - /// - Parameters: - /// - multipartFormData: `MultipartFormData` instance to upload. - /// - request: `URLRequestConvertible` value to be used to create the `URLRequest`. - /// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or - /// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by - /// default. - /// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default. - /// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by - /// default. - /// - /// - Returns: The created `UploadRequest`. - open func upload(multipartFormData: MultipartFormData, - with request: URLRequestConvertible, - usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, - interceptor: RequestInterceptor? = nil, - fileManager: FileManager = .default) -> UploadRequest { - let multipartUpload = MultipartUpload(encodingMemoryThreshold: encodingMemoryThreshold, - request: request, - multipartFormData: multipartFormData) - - return upload(multipartUpload, interceptor: interceptor, fileManager: fileManager) - } - - // MARK: - Internal API - - // MARK: Uploadable - - func upload(_ uploadable: UploadRequest.Uploadable, - with convertible: URLRequestConvertible, - interceptor: RequestInterceptor?, - fileManager: FileManager) -> UploadRequest { - let uploadable = Upload(request: convertible, uploadable: uploadable) - - return upload(uploadable, interceptor: interceptor, fileManager: fileManager) - } - - func upload(_ upload: UploadConvertible, interceptor: RequestInterceptor?, fileManager: FileManager) -> UploadRequest { - let request = UploadRequest(convertible: upload, - underlyingQueue: rootQueue, - serializationQueue: serializationQueue, - eventMonitor: eventMonitor, - interceptor: interceptor, - fileManager: fileManager, - delegate: self) - - perform(request) - - return request - } - - // MARK: Perform - - /// Starts performing the provided `Request`. - /// - /// - Parameter request: The `Request` to perform. - func perform(_ request: Request) { - rootQueue.async { - guard !request.isCancelled else { return } - - self.activeRequests.insert(request) - - self.requestQueue.async { - // Leaf types must come first, otherwise they will cast as their superclass. - switch request { - case let r as UploadRequest: self.performUploadRequest(r) // UploadRequest must come before DataRequest due to subtype relationship. - case let r as DataRequest: self.performDataRequest(r) - case let r as DownloadRequest: self.performDownloadRequest(r) - case let r as DataStreamRequest: self.performDataStreamRequest(r) - default: fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))") - } - } - } - } - - func performDataRequest(_ request: DataRequest) { - dispatchPrecondition(condition: .onQueue(requestQueue)) - - performSetupOperations(for: request, convertible: request.convertible) - } - - func performDataStreamRequest(_ request: DataStreamRequest) { - dispatchPrecondition(condition: .onQueue(requestQueue)) - - performSetupOperations(for: request, convertible: request.convertible) - } - - func performUploadRequest(_ request: UploadRequest) { - dispatchPrecondition(condition: .onQueue(requestQueue)) - - performSetupOperations(for: request, convertible: request.convertible) { - do { - let uploadable = try request.upload.createUploadable() - self.rootQueue.async { request.didCreateUploadable(uploadable) } - return true - } catch { - self.rootQueue.async { request.didFailToCreateUploadable(with: error.asAFError(or: .createUploadableFailed(error: error))) } - return false - } - } - } - - func performDownloadRequest(_ request: DownloadRequest) { - dispatchPrecondition(condition: .onQueue(requestQueue)) - - switch request.downloadable { - case let .request(convertible): - performSetupOperations(for: request, convertible: convertible) - case let .resumeData(resumeData): - rootQueue.async { self.didReceiveResumeData(resumeData, for: request) } - } - } - - func performSetupOperations(for request: Request, - convertible: URLRequestConvertible, - shouldCreateTask: @escaping () -> Bool = { true }) - { - dispatchPrecondition(condition: .onQueue(requestQueue)) - - let initialRequest: URLRequest - - do { - initialRequest = try convertible.asURLRequest() - try initialRequest.validate() - } catch { - rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) } - return - } - - rootQueue.async { request.didCreateInitialURLRequest(initialRequest) } - - guard !request.isCancelled else { return } - - guard let adapter = adapter(for: request) else { - guard shouldCreateTask() else { return } - rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) } - return - } - - adapter.adapt(initialRequest, for: self) { result in - do { - let adaptedRequest = try result.get() - try adaptedRequest.validate() - - self.rootQueue.async { request.didAdaptInitialRequest(initialRequest, to: adaptedRequest) } - - guard shouldCreateTask() else { return } - - self.rootQueue.async { self.didCreateURLRequest(adaptedRequest, for: request) } - } catch { - self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) } - } - } - } - - // MARK: - Task Handling - - func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - request.didCreateURLRequest(urlRequest) - - guard !request.isCancelled else { return } - - let task = request.task(for: urlRequest, using: session) - requestTaskMap[request] = task - request.didCreateTask(task) - - updateStatesForTask(task, request: request) - } - - func didReceiveResumeData(_ data: Data, for request: DownloadRequest) { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - guard !request.isCancelled else { return } - - let task = request.task(forResumeData: data, using: session) - requestTaskMap[request] = task - request.didCreateTask(task) - - updateStatesForTask(task, request: request) - } - - func updateStatesForTask(_ task: URLSessionTask, request: Request) { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - request.withState { state in - switch state { - case .initialized, .finished: - // Do nothing. - break - case .resumed: - task.resume() - rootQueue.async { request.didResumeTask(task) } - case .suspended: - task.suspend() - rootQueue.async { request.didSuspendTask(task) } - case .cancelled: - // Resume to ensure metrics are gathered. - task.resume() - task.cancel() - rootQueue.async { request.didCancelTask(task) } - } - } - } - - // MARK: - Adapters and Retriers - - func adapter(for request: Request) -> RequestAdapter? { - if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { - return Interceptor(adapters: [requestInterceptor, sessionInterceptor]) - } else { - return request.interceptor ?? interceptor - } - } - - func retrier(for request: Request) -> RequestRetrier? { - if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { - return Interceptor(retriers: [requestInterceptor, sessionInterceptor]) - } else { - return request.interceptor ?? interceptor - } - } - - // MARK: - Invalidation - - func finishRequestsForDeinit() { - requestTaskMap.requests.forEach { request in - rootQueue.async { - request.finish(error: AFError.sessionDeinitialized) - } - } - } -} - -// MARK: - RequestDelegate - -extension Session: RequestDelegate { - public var sessionConfiguration: URLSessionConfiguration { - session.configuration - } - - public var startImmediately: Bool { startRequestsImmediately } - - public func cleanup(after request: Request) { - activeRequests.remove(request) - } - - public func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void) { - guard let retrier = retrier(for: request) else { - rootQueue.async { completion(.doNotRetry) } - return - } - - retrier.retry(request, for: self, dueTo: error) { retryResult in - self.rootQueue.async { - guard let retryResultError = retryResult.error else { completion(retryResult); return } - - let retryError = AFError.requestRetryFailed(retryError: retryResultError, originalError: error) - completion(.doNotRetryWithError(retryError)) - } - } - } - - public func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?) { - rootQueue.async { - let retry: () -> Void = { - guard !request.isCancelled else { return } - - request.prepareForRetry() - self.perform(request) - } - - if let retryDelay = timeDelay { - self.rootQueue.after(retryDelay) { retry() } - } else { - retry() - } - } - } -} - -// MARK: - SessionStateProvider - -extension Session: SessionStateProvider { - func request(for task: URLSessionTask) -> Request? { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - return requestTaskMap[task] - } - - func didGatherMetricsForTask(_ task: URLSessionTask) { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterGatheringMetricsForTask(task) - - if didDisassociate { - waitingCompletions[task]?() - waitingCompletions[task] = nil - } - } - - func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterCompletingTask(task) - - if didDisassociate { - completion() - } else { - waitingCompletions[task] = completion - } - } - - func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - return requestTaskMap[task]?.credential ?? - session.configuration.urlCredentialStorage?.defaultCredential(for: protectionSpace) - } - - func cancelRequestsForSessionInvalidation(with error: Error?) { - dispatchPrecondition(condition: .onQueue(rootQueue)) - - requestTaskMap.requests.forEach { $0.finish(error: AFError.sessionInvalidated(error: error)) } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift deleted file mode 100644 index befc80ea..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/SessionDelegate.swift +++ /dev/null @@ -1,330 +0,0 @@ -// -// SessionDelegate.swift -// -// Copyright (c) 2014-2018 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 - -/// Class which implements the various `URLSessionDelegate` methods to connect various Alamofire features. -open class SessionDelegate: NSObject { - private let fileManager: FileManager - - weak var stateProvider: SessionStateProvider? - var eventMonitor: EventMonitor? - - /// Creates an instance from the given `FileManager`. - /// - /// - Parameter fileManager: `FileManager` to use for underlying file management, such as moving downloaded files. - /// `.default` by default. - public init(fileManager: FileManager = .default) { - self.fileManager = fileManager - } - - /// Internal method to find and cast requests while maintaining some integrity checking. - /// - /// - Parameters: - /// - task: The `URLSessionTask` for which to find the associated `Request`. - /// - type: The `Request` subclass type to cast any `Request` associate with `task`. - func request(for task: URLSessionTask, as type: R.Type) -> R? { - guard let provider = stateProvider else { - assertionFailure("StateProvider is nil.") - return nil - } - - return provider.request(for: task) as? R - } -} - -/// Type which provides various `Session` state values. -protocol SessionStateProvider: AnyObject { - var serverTrustManager: ServerTrustManager? { get } - var redirectHandler: RedirectHandler? { get } - var cachedResponseHandler: CachedResponseHandler? { get } - - func request(for task: URLSessionTask) -> Request? - func didGatherMetricsForTask(_ task: URLSessionTask) - func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) - func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? - func cancelRequestsForSessionInvalidation(with error: Error?) -} - -// MARK: URLSessionDelegate - -extension SessionDelegate: URLSessionDelegate { - open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { - eventMonitor?.urlSession(session, didBecomeInvalidWithError: error) - - stateProvider?.cancelRequestsForSessionInvalidation(with: error) - } -} - -// MARK: URLSessionTaskDelegate - -extension SessionDelegate: URLSessionTaskDelegate { - /// Result of a `URLAuthenticationChallenge` evaluation. - typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?, error: AFError?) - - open func urlSession(_ session: URLSession, - task: URLSessionTask, - didReceive challenge: URLAuthenticationChallenge, - completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { - eventMonitor?.urlSession(session, task: task, didReceive: challenge) - - let evaluation: ChallengeEvaluation - switch challenge.protectionSpace.authenticationMethod { - case NSURLAuthenticationMethodServerTrust: - evaluation = attemptServerTrustAuthentication(with: challenge) - case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM, - NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate: - evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task) - default: - evaluation = (.performDefaultHandling, nil, nil) - } - - if let error = evaluation.error { - stateProvider?.request(for: task)?.didFailTask(task, earlyWithError: error) - } - - completionHandler(evaluation.disposition, evaluation.credential) - } - - /// Evaluates the server trust `URLAuthenticationChallenge` received. - /// - /// - Parameter challenge: The `URLAuthenticationChallenge`. - /// - /// - Returns: The `ChallengeEvaluation`. - func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation { - let host = challenge.protectionSpace.host - - guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, - let trust = challenge.protectionSpace.serverTrust - else { - return (.performDefaultHandling, nil, nil) - } - - do { - guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else { - return (.performDefaultHandling, nil, nil) - } - - try evaluator.evaluate(trust, forHost: host) - - return (.useCredential, URLCredential(trust: trust), nil) - } catch { - return (.cancelAuthenticationChallenge, nil, error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error)))) - } - } - - /// Evaluates the credential-based authentication `URLAuthenticationChallenge` received for `task`. - /// - /// - Parameters: - /// - challenge: The `URLAuthenticationChallenge`. - /// - task: The `URLSessionTask` which received the challenge. - /// - /// - Returns: The `ChallengeEvaluation`. - func attemptCredentialAuthentication(for challenge: URLAuthenticationChallenge, - belongingTo task: URLSessionTask) -> ChallengeEvaluation { - guard challenge.previousFailureCount == 0 else { - return (.rejectProtectionSpace, nil, nil) - } - - guard let credential = stateProvider?.credential(for: task, in: challenge.protectionSpace) else { - return (.performDefaultHandling, nil, nil) - } - - return (.useCredential, credential, nil) - } - - open func urlSession(_ session: URLSession, - task: URLSessionTask, - didSendBodyData bytesSent: Int64, - totalBytesSent: Int64, - totalBytesExpectedToSend: Int64) { - eventMonitor?.urlSession(session, - task: task, - didSendBodyData: bytesSent, - totalBytesSent: totalBytesSent, - totalBytesExpectedToSend: totalBytesExpectedToSend) - - stateProvider?.request(for: task)?.updateUploadProgress(totalBytesSent: totalBytesSent, - totalBytesExpectedToSend: totalBytesExpectedToSend) - } - - open func urlSession(_ session: URLSession, - task: URLSessionTask, - needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) { - eventMonitor?.urlSession(session, taskNeedsNewBodyStream: task) - - guard let request = request(for: task, as: UploadRequest.self) else { - assertionFailure("needNewBodyStream did not find UploadRequest.") - completionHandler(nil) - return - } - - completionHandler(request.inputStream()) - } - - open func urlSession(_ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest, - completionHandler: @escaping (URLRequest?) -> Void) { - eventMonitor?.urlSession(session, task: task, willPerformHTTPRedirection: response, newRequest: request) - - if let redirectHandler = stateProvider?.request(for: task)?.redirectHandler ?? stateProvider?.redirectHandler { - redirectHandler.task(task, willBeRedirectedTo: request, for: response, completion: completionHandler) - } else { - completionHandler(request) - } - } - - open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { - eventMonitor?.urlSession(session, task: task, didFinishCollecting: metrics) - - stateProvider?.request(for: task)?.didGatherMetrics(metrics) - - stateProvider?.didGatherMetricsForTask(task) - } - - open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - eventMonitor?.urlSession(session, task: task, didCompleteWithError: error) - - let request = stateProvider?.request(for: task) - - stateProvider?.didCompleteTask(task) { - request?.didCompleteTask(task, with: error.map { $0.asAFError(or: .sessionTaskFailed(error: $0)) }) - } - } - - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { - eventMonitor?.urlSession(session, taskIsWaitingForConnectivity: task) - } -} - -// MARK: URLSessionDataDelegate - -extension SessionDelegate: URLSessionDataDelegate { - open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data) - - if let request = request(for: dataTask, as: DataRequest.self) { - request.didReceive(data: data) - } else if let request = request(for: dataTask, as: DataStreamRequest.self) { - request.didReceive(data: data) - } else { - assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive") - return - } - } - - open func urlSession(_ session: URLSession, - dataTask: URLSessionDataTask, - willCacheResponse proposedResponse: CachedURLResponse, - completionHandler: @escaping (CachedURLResponse?) -> Void) { - eventMonitor?.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) - - if let handler = stateProvider?.request(for: dataTask)?.cachedResponseHandler ?? stateProvider?.cachedResponseHandler { - handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler) - } else { - completionHandler(proposedResponse) - } - } -} - -// MARK: URLSessionDownloadDelegate - -extension SessionDelegate: URLSessionDownloadDelegate { - open func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didResumeAtOffset fileOffset: Int64, - expectedTotalBytes: Int64) { - eventMonitor?.urlSession(session, - downloadTask: downloadTask, - didResumeAtOffset: fileOffset, - expectedTotalBytes: expectedTotalBytes) - guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else { - assertionFailure("downloadTask did not find DownloadRequest.") - return - } - - downloadRequest.updateDownloadProgress(bytesWritten: fileOffset, - totalBytesExpectedToWrite: expectedTotalBytes) - } - - open func urlSession(_ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64) { - eventMonitor?.urlSession(session, - downloadTask: downloadTask, - didWriteData: bytesWritten, - totalBytesWritten: totalBytesWritten, - totalBytesExpectedToWrite: totalBytesExpectedToWrite) - guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else { - assertionFailure("downloadTask did not find DownloadRequest.") - return - } - - downloadRequest.updateDownloadProgress(bytesWritten: bytesWritten, - totalBytesExpectedToWrite: totalBytesExpectedToWrite) - } - - open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { - eventMonitor?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) - - guard let request = request(for: downloadTask, as: DownloadRequest.self) else { - assertionFailure("downloadTask did not find DownloadRequest.") - return - } - - let (destination, options): (URL, DownloadRequest.Options) - if let response = request.response { - (destination, options) = request.destination(location, response) - } else { - // If there's no response this is likely a local file download, so generate the temporary URL directly. - (destination, options) = (DownloadRequest.defaultDestinationURL(location), []) - } - - eventMonitor?.request(request, didCreateDestinationURL: destination) - - do { - if options.contains(.removePreviousFile), fileManager.fileExists(atPath: destination.path) { - try fileManager.removeItem(at: destination) - } - - if options.contains(.createIntermediateDirectories) { - let directory = destination.deletingLastPathComponent() - try fileManager.createDirectory(at: directory, withIntermediateDirectories: true) - } - - try fileManager.moveItem(at: location, to: destination) - - request.didFinishDownloading(using: downloadTask, with: .success(destination)) - } catch { - request.didFinishDownloading(using: downloadTask, with: .failure(.downloadedFileMoveFailed(error: error, - source: location, - destination: destination))) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift deleted file mode 100644 index 8fa61333..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/StringEncoding+Alamofire.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// StringEncoding+Alamofire.swift -// -// Copyright (c) 2020 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 String.Encoding { - /// Creates an encoding from the IANA charset name. - /// - /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html) - /// - /// - Parameter name: IANA charset name. - init?(ianaCharsetName name: String) { - switch name.lowercased() { - case "utf-8": - self = .utf8 - case "iso-8859-1": - self = .isoLatin1 - case "unicode-1-1", "iso-10646-ucs-2", "utf-16": - self = .utf16 - case "utf-16be": - self = .utf16BigEndian - case "utf-16le": - self = .utf16LittleEndian - case "utf-32": - self = .utf32 - case "utf-32be": - self = .utf32BigEndian - case "utf-32le": - self = .utf32LittleEndian - default: - return nil - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift deleted file mode 100644 index 455c4bcb..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// URLConvertible+URLRequestConvertible.swift -// -// Copyright (c) 2014-2018 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 `URL`s, which can then be used to construct -/// `URLRequests`. -public protocol URLConvertible { - /// Returns a `URL` from the conforming instance or throws. - /// - /// - Returns: The `URL` created from the instance. - /// - Throws: Any error thrown while creating the `URL`. - func asURL() throws -> URL -} - -extension String: URLConvertible { - /// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws. - /// - /// - Returns: The `URL` initialized with `self`. - /// - Throws: An `AFError.invalidURL` instance. - 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 { self } -} - -extension URLComponents: URLConvertible { - /// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws. - /// - /// - Returns: The `URL` from the `url` property. - /// - Throws: An `AFError.invalidURL` instance. - 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 safely construct `URLRequest`s. -public protocol URLRequestConvertible { - /// Returns a `URLRequest` or throws if an `Error` was encountered. - /// - /// - Returns: A `URLRequest`. - /// - Throws: Any error thrown while constructing the `URLRequest`. - func asURLRequest() throws -> URLRequest -} - -extension URLRequestConvertible { - /// The `URLRequest` returned by discarding any `Error` encountered. - public var urlRequest: URLRequest? { try? asURLRequest() } -} - -extension URLRequest: URLRequestConvertible { - /// Returns `self`. - public func asURLRequest() throws -> URLRequest { self } -} - -// MARK: - - -extension URLRequest { - /// Creates an instance with the specified `url`, `method`, and `headers`. - /// - /// - Parameters: - /// - url: The `URLConvertible` value. - /// - method: The `HTTPMethod`. - /// - headers: The `HTTPHeaders`, `nil` by default. - /// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`. - public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { - let url = try url.asURL() - - self.init(url: url) - - httpMethod = method.rawValue - allHTTPHeaderFields = headers?.dictionary - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift deleted file mode 100644 index e5cc0c50..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLEncodedFormEncoder.swift +++ /dev/null @@ -1,976 +0,0 @@ -// -// URLEncodedFormEncoder.swift -// -// Copyright (c) 2019 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 - -/// An object that encodes instances into URL-encoded query strings. -/// -/// There is no published specification for how to encode collection types. By default, 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`) is used. Optionally, `ArrayEncoding` can be used to omit the -/// square brackets appended to array keys. -/// -/// `BoolEncoding` can be used to configure how `Bool` values are encoded. The default behavior is to encode -/// `true` as 1 and `false` as 0. -/// -/// `DateEncoding` can be used to configure how `Date` values are encoded. By default, the `.deferredToDate` -/// strategy is used, which formats dates from their structure. -/// -/// `SpaceEncoding` can be used to configure how spaces are encoded. Modern encodings use percent replacement (`%20`), -/// while older encodings may expect spaces to be replaced with `+`. -/// -/// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project. -public final class URLEncodedFormEncoder { - /// Encoding to use for `Array` values. - public enum ArrayEncoding { - /// An empty set of square brackets ("[]") are appended to the key for every value. This is the default encoding. - case brackets - /// No brackets are appended to the key and the key is encoded as is. - case noBrackets - - /// Encodes the key according to the encoding. - /// - /// - Parameter key: The `key` to encode. - /// - Returns: The encoded key. - func encode(_ key: String) -> String { - switch self { - case .brackets: return "\(key)[]" - case .noBrackets: return key - } - } - } - - /// Encoding to use for `Bool` values. - public enum BoolEncoding { - /// Encodes `true` as `1`, `false` as `0`. - case numeric - /// Encodes `true` as "true", `false` as "false". This is the default encoding. - case literal - - /// Encodes the given `Bool` as a `String`. - /// - /// - Parameter value: The `Bool` to encode. - /// - /// - Returns: The encoded `String`. - func encode(_ value: Bool) -> String { - switch self { - case .numeric: return value ? "1" : "0" - case .literal: return value ? "true" : "false" - } - } - } - - /// Encoding to use for `Data` values. - public enum DataEncoding { - /// Defers encoding to the `Data` type. - case deferredToData - /// Encodes `Data` as a Base64-encoded string. This is the default encoding. - case base64 - /// Encode the `Data` as a custom value encoded by the given closure. - case custom((Data) throws -> String) - - /// Encodes `Data` according to the encoding. - /// - /// - Parameter data: The `Data` to encode. - /// - /// - Returns: The encoded `String`, or `nil` if the `Data` should be encoded according to its - /// `Encodable` implementation. - func encode(_ data: Data) throws -> String? { - switch self { - case .deferredToData: return nil - case .base64: return data.base64EncodedString() - case let .custom(encoding): return try encoding(data) - } - } - } - - /// Encoding to use for `Date` values. - public enum DateEncoding { - /// ISO8601 and RFC3339 formatter. - private static let iso8601Formatter: ISO8601DateFormatter = { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = .withInternetDateTime - return formatter - }() - - /// Defers encoding to the `Date` type. This is the default encoding. - case deferredToDate - /// Encodes `Date`s as seconds since midnight UTC on January 1, 1970. - case secondsSince1970 - /// Encodes `Date`s as milliseconds since midnight UTC on January 1, 1970. - case millisecondsSince1970 - /// Encodes `Date`s according to the ISO8601 and RFC3339 standards. - case iso8601 - /// Encodes `Date`s using the given `DateFormatter`. - case formatted(DateFormatter) - /// Encodes `Date`s using the given closure. - case custom((Date) throws -> String) - - /// Encodes the date according to the encoding. - /// - /// - Parameter date: The `Date` to encode. - /// - /// - Returns: The encoded `String`, or `nil` if the `Date` should be encoded according to its - /// `Encodable` implementation. - func encode(_ date: Date) throws -> String? { - switch self { - case .deferredToDate: - return nil - case .secondsSince1970: - return String(date.timeIntervalSince1970) - case .millisecondsSince1970: - return String(date.timeIntervalSince1970 * 1000.0) - case .iso8601: - return DateEncoding.iso8601Formatter.string(from: date) - case let .formatted(formatter): - return formatter.string(from: date) - case let .custom(closure): - return try closure(date) - } - } - } - - /// Encoding to use for keys. - /// - /// This type is derived from [`JSONEncoder`'s `KeyEncodingStrategy`](https://github.com/apple/swift/blob/6aa313b8dd5f05135f7f878eccc1db6f9fbe34ff/stdlib/public/Darwin/Foundation/JSONEncoder.swift#L128) - /// and [`XMLEncoder`s `KeyEncodingStrategy`](https://github.com/MaxDesiatov/XMLCoder/blob/master/Sources/XMLCoder/Encoder/XMLEncoder.swift#L102). - public enum KeyEncoding { - /// Use the keys specified by each type. This is the default encoding. - case useDefaultKeys - /// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key. - /// - /// Capital characters are determined by testing membership in - /// `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` - /// (Unicode General Categories Lu and Lt). - /// The conversion to lower case uses `Locale.system`, also known as - /// the ICU "root" locale. This means the result is consistent - /// regardless of the current user's locale and language preferences. - /// - /// Converting from camel case to snake case: - /// 1. Splits words at the boundary of lower-case to upper-case - /// 2. Inserts `_` between words - /// 3. Lowercases the entire string - /// 4. Preserves starting and ending `_`. - /// - /// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`. - /// - /// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted. - case convertToSnakeCase - /// Same as convertToSnakeCase, but using `-` instead of `_`. - /// For example `oneTwoThree` becomes `one-two-three`. - case convertToKebabCase - /// Capitalize the first letter only. - /// For example `oneTwoThree` becomes `OneTwoThree`. - case capitalized - /// Uppercase all letters. - /// For example `oneTwoThree` becomes `ONETWOTHREE`. - case uppercased - /// Lowercase all letters. - /// For example `oneTwoThree` becomes `onetwothree`. - case lowercased - /// A custom encoding using the provided closure. - case custom((String) -> String) - - func encode(_ key: String) -> String { - switch self { - case .useDefaultKeys: return key - case .convertToSnakeCase: return convertToSnakeCase(key) - case .convertToKebabCase: return convertToKebabCase(key) - case .capitalized: return String(key.prefix(1).uppercased() + key.dropFirst()) - case .uppercased: return key.uppercased() - case .lowercased: return key.lowercased() - case let .custom(encoding): return encoding(key) - } - } - - private func convertToSnakeCase(_ key: String) -> String { - convert(key, usingSeparator: "_") - } - - private func convertToKebabCase(_ key: String) -> String { - convert(key, usingSeparator: "-") - } - - private func convert(_ key: String, usingSeparator separator: String) -> String { - guard !key.isEmpty else { return key } - - var words: [Range] = [] - // The general idea of this algorithm is to split words on - // transition from lower to upper case, then on transition of >1 - // upper case characters to lowercase - // - // myProperty -> my_property - // myURLProperty -> my_url_property - // - // It is assumed, per Swift naming conventions, that the first character of the key is lowercase. - var wordStart = key.startIndex - var searchRange = key.index(after: wordStart)..1 capital letters. Turn those into a word, stopping at the capital before the lower case character. - let beforeLowerIndex = key.index(before: lowerCaseRange.lowerBound) - words.append(upperCaseRange.lowerBound.. String { - switch self { - case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20") - case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+") - } - } - } - - /// `URLEncodedFormEncoder` error. - public enum Error: Swift.Error { - /// An invalid root object was created by the encoder. Only keyed values are valid. - case invalidRootObject(String) - - var localizedDescription: String { - switch self { - case let .invalidRootObject(object): - return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead." - } - } - } - - /// Whether or not to sort the encoded key value pairs. - /// - /// - Note: This setting ensures a consistent ordering for all encodings of the same parameters. When set to `false`, - /// encoded `Dictionary` values may have a different encoded order each time they're encoded due to - /// ` Dictionary`'s random storage order, but `Encodable` types will maintain their encoded order. - public let alphabetizeKeyValuePairs: Bool - /// The `ArrayEncoding` to use. - public let arrayEncoding: ArrayEncoding - /// The `BoolEncoding` to use. - public let boolEncoding: BoolEncoding - /// THe `DataEncoding` to use. - public let dataEncoding: DataEncoding - /// The `DateEncoding` to use. - public let dateEncoding: DateEncoding - /// The `KeyEncoding` to use. - public let keyEncoding: KeyEncoding - /// The `SpaceEncoding` to use. - public let spaceEncoding: SpaceEncoding - /// The `CharacterSet` of allowed (non-escaped) characters. - public var allowedCharacters: CharacterSet - - /// Creates an instance from the supplied parameters. - /// - /// - Parameters: - /// - alphabetizeKeyValuePairs: Whether or not to sort the encoded key value pairs. `true` by default. - /// - arrayEncoding: The `ArrayEncoding` to use. `.brackets` by default. - /// - boolEncoding: The `BoolEncoding` to use. `.numeric` by default. - /// - dataEncoding: The `DataEncoding` to use. `.base64` by default. - /// - dateEncoding: The `DateEncoding` to use. `.deferredToDate` by default. - /// - keyEncoding: The `KeyEncoding` to use. `.useDefaultKeys` by default. - /// - spaceEncoding: The `SpaceEncoding` to use. `.percentEscaped` by default. - /// - allowedCharacters: The `CharacterSet` of allowed (non-escaped) characters. `.afURLQueryAllowed` by - /// default. - public init(alphabetizeKeyValuePairs: Bool = true, - arrayEncoding: ArrayEncoding = .brackets, - boolEncoding: BoolEncoding = .numeric, - dataEncoding: DataEncoding = .base64, - dateEncoding: DateEncoding = .deferredToDate, - keyEncoding: KeyEncoding = .useDefaultKeys, - spaceEncoding: SpaceEncoding = .percentEscaped, - allowedCharacters: CharacterSet = .afURLQueryAllowed) { - self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs - self.arrayEncoding = arrayEncoding - self.boolEncoding = boolEncoding - self.dataEncoding = dataEncoding - self.dateEncoding = dateEncoding - self.keyEncoding = keyEncoding - self.spaceEncoding = spaceEncoding - self.allowedCharacters = allowedCharacters - } - - func encode(_ value: Encodable) throws -> URLEncodedFormComponent { - let context = URLEncodedFormContext(.object([])) - let encoder = _URLEncodedFormEncoder(context: context, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - try value.encode(to: encoder) - - return context.component - } - - /// Encodes the `value` as a URL form encoded `String`. - /// - /// - Parameter value: The `Encodable` value.` - /// - /// - Returns: The encoded `String`. - /// - Throws: An `Error` or `EncodingError` instance if encoding fails. - public func encode(_ value: Encodable) throws -> String { - let component: URLEncodedFormComponent = try encode(value) - - guard case let .object(object) = component else { - throw Error.invalidRootObject("\(component)") - } - - let serializer = URLEncodedFormSerializer(alphabetizeKeyValuePairs: alphabetizeKeyValuePairs, - arrayEncoding: arrayEncoding, - keyEncoding: keyEncoding, - spaceEncoding: spaceEncoding, - allowedCharacters: allowedCharacters) - let query = serializer.serialize(object) - - return query - } - - /// Encodes the value as `Data`. This is performed by first creating an encoded `String` and then returning the - /// `.utf8` data. - /// - /// - Parameter value: The `Encodable` value. - /// - /// - Returns: The encoded `Data`. - /// - /// - Throws: An `Error` or `EncodingError` instance if encoding fails. - public func encode(_ value: Encodable) throws -> Data { - let string: String = try encode(value) - - return Data(string.utf8) - } -} - -final class _URLEncodedFormEncoder { - var codingPath: [CodingKey] - // Returns an empty dictionary, as this encoder doesn't support userInfo. - var userInfo: [CodingUserInfoKey: Any] { [:] } - - let context: URLEncodedFormContext - - private let boolEncoding: URLEncodedFormEncoder.BoolEncoding - private let dataEncoding: URLEncodedFormEncoder.DataEncoding - private let dateEncoding: URLEncodedFormEncoder.DateEncoding - - init(context: URLEncodedFormContext, - codingPath: [CodingKey] = [], - boolEncoding: URLEncodedFormEncoder.BoolEncoding, - dataEncoding: URLEncodedFormEncoder.DataEncoding, - dateEncoding: URLEncodedFormEncoder.DateEncoding) { - self.context = context - self.codingPath = codingPath - self.boolEncoding = boolEncoding - self.dataEncoding = dataEncoding - self.dateEncoding = dateEncoding - } -} - -extension _URLEncodedFormEncoder: Encoder { - func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key: CodingKey { - let container = _URLEncodedFormEncoder.KeyedContainer(context: context, - codingPath: codingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - return KeyedEncodingContainer(container) - } - - func unkeyedContainer() -> UnkeyedEncodingContainer { - _URLEncodedFormEncoder.UnkeyedContainer(context: context, - codingPath: codingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } - - func singleValueContainer() -> SingleValueEncodingContainer { - _URLEncodedFormEncoder.SingleValueContainer(context: context, - codingPath: codingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } -} - -final class URLEncodedFormContext { - var component: URLEncodedFormComponent - - init(_ component: URLEncodedFormComponent) { - self.component = component - } -} - -enum URLEncodedFormComponent { - typealias Object = [(key: String, value: URLEncodedFormComponent)] - - case string(String) - case array([URLEncodedFormComponent]) - case object(Object) - - /// Converts self to an `[URLEncodedFormData]` or returns `nil` if not convertible. - var array: [URLEncodedFormComponent]? { - switch self { - case let .array(array): return array - default: return nil - } - } - - /// Converts self to an `Object` or returns `nil` if not convertible. - var object: Object? { - switch self { - case let .object(object): return object - default: return nil - } - } - - /// Sets self to the supplied value at a given path. - /// - /// data.set(to: "hello", at: ["path", "to", "value"]) - /// - /// - parameters: - /// - value: Value of `Self` to set at the supplied path. - /// - path: `CodingKey` path to update with the supplied value. - public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) { - set(&self, to: value, at: path) - } - - /// Recursive backing method to `set(to:at:)`. - private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) { - guard path.count >= 1 else { - context = value - return - } - - let end = path[0] - var child: URLEncodedFormComponent - switch path.count { - case 1: - child = value - case 2...: - if let index = end.intValue { - let array = context.array ?? [] - if array.count > index { - child = array[index] - } else { - child = .array([]) - } - set(&child, to: value, at: Array(path[1...])) - } else { - child = context.object?.first { $0.key == end.stringValue }?.value ?? .object(.init()) - set(&child, to: value, at: Array(path[1...])) - } - default: fatalError("Unreachable") - } - - if let index = end.intValue { - if var array = context.array { - if array.count > index { - array[index] = child - } else { - array.append(child) - } - context = .array(array) - } else { - context = .array([child]) - } - } else { - if var object = context.object { - if let index = object.firstIndex(where: { $0.key == end.stringValue }) { - object[index] = (key: end.stringValue, value: child) - } else { - object.append((key: end.stringValue, value: child)) - } - context = .object(object) - } else { - context = .object([(key: end.stringValue, value: child)]) - } - } - } -} - -struct AnyCodingKey: CodingKey, Hashable { - let stringValue: String - let intValue: Int? - - init?(stringValue: String) { - self.stringValue = stringValue - intValue = nil - } - - init?(intValue: Int) { - stringValue = "\(intValue)" - self.intValue = intValue - } - - init(_ base: Key) where Key: CodingKey { - if let intValue = base.intValue { - self.init(intValue: intValue)! - } else { - self.init(stringValue: base.stringValue)! - } - } -} - -extension _URLEncodedFormEncoder { - final class KeyedContainer where Key: CodingKey { - var codingPath: [CodingKey] - - private let context: URLEncodedFormContext - private let boolEncoding: URLEncodedFormEncoder.BoolEncoding - private let dataEncoding: URLEncodedFormEncoder.DataEncoding - private let dateEncoding: URLEncodedFormEncoder.DateEncoding - - init(context: URLEncodedFormContext, - codingPath: [CodingKey], - boolEncoding: URLEncodedFormEncoder.BoolEncoding, - dataEncoding: URLEncodedFormEncoder.DataEncoding, - dateEncoding: URLEncodedFormEncoder.DateEncoding) { - self.context = context - self.codingPath = codingPath - self.boolEncoding = boolEncoding - self.dataEncoding = dataEncoding - self.dateEncoding = dateEncoding - } - - private func nestedCodingPath(for key: CodingKey) -> [CodingKey] { - codingPath + [key] - } - } -} - -extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol { - func encodeNil(forKey key: Key) throws { - let context = EncodingError.Context(codingPath: codingPath, - debugDescription: "URLEncodedFormEncoder cannot encode nil values.") - throw EncodingError.invalidValue("\(key): nil", context) - } - - func encode(_ value: T, forKey key: Key) throws where T: Encodable { - var container = nestedSingleValueEncoder(for: key) - try container.encode(value) - } - - func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer { - let container = _URLEncodedFormEncoder.SingleValueContainer(context: context, - codingPath: nestedCodingPath(for: key), - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - - return container - } - - func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context, - codingPath: nestedCodingPath(for: key), - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - - return container - } - - func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey: CodingKey { - let container = _URLEncodedFormEncoder.KeyedContainer(context: context, - codingPath: nestedCodingPath(for: key), - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - - return KeyedEncodingContainer(container) - } - - func superEncoder() -> Encoder { - _URLEncodedFormEncoder(context: context, - codingPath: codingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } - - func superEncoder(forKey key: Key) -> Encoder { - _URLEncodedFormEncoder(context: context, - codingPath: nestedCodingPath(for: key), - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } -} - -extension _URLEncodedFormEncoder { - final class SingleValueContainer { - var codingPath: [CodingKey] - - private var canEncodeNewValue = true - - private let context: URLEncodedFormContext - private let boolEncoding: URLEncodedFormEncoder.BoolEncoding - private let dataEncoding: URLEncodedFormEncoder.DataEncoding - private let dateEncoding: URLEncodedFormEncoder.DateEncoding - - init(context: URLEncodedFormContext, - codingPath: [CodingKey], - boolEncoding: URLEncodedFormEncoder.BoolEncoding, - dataEncoding: URLEncodedFormEncoder.DataEncoding, - dateEncoding: URLEncodedFormEncoder.DateEncoding) { - self.context = context - self.codingPath = codingPath - self.boolEncoding = boolEncoding - self.dataEncoding = dataEncoding - self.dateEncoding = dateEncoding - } - - private func checkCanEncode(value: Any?) throws { - guard canEncodeNewValue else { - let context = EncodingError.Context(codingPath: codingPath, - debugDescription: "Attempt to encode value through single value container when previously value already encoded.") - throw EncodingError.invalidValue(value as Any, context) - } - } - } -} - -extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer { - func encodeNil() throws { - try checkCanEncode(value: nil) - defer { canEncodeNewValue = false } - - let context = EncodingError.Context(codingPath: codingPath, - debugDescription: "URLEncodedFormEncoder cannot encode nil values.") - throw EncodingError.invalidValue("nil", context) - } - - func encode(_ value: Bool) throws { - try encode(value, as: String(boolEncoding.encode(value))) - } - - func encode(_ value: String) throws { - try encode(value, as: value) - } - - func encode(_ value: Double) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: Float) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: Int) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: Int8) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: Int16) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: Int32) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: Int64) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: UInt) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: UInt8) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: UInt16) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: UInt32) throws { - try encode(value, as: String(value)) - } - - func encode(_ value: UInt64) throws { - try encode(value, as: String(value)) - } - - private func encode(_ value: T, as string: String) throws where T: Encodable { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(string), at: codingPath) - } - - func encode(_ value: T) throws where T: Encodable { - switch value { - case let date as Date: - guard let string = try dateEncoding.encode(date) else { - try attemptToEncode(value) - return - } - - try encode(value, as: string) - case let data as Data: - guard let string = try dataEncoding.encode(data) else { - try attemptToEncode(value) - return - } - - try encode(value, as: string) - case let decimal as Decimal: - // Decimal's `Encodable` implementation returns an object, not a single value, so override it. - try encode(value, as: String(describing: decimal)) - default: - try attemptToEncode(value) - } - } - - private func attemptToEncode(_ value: T) throws where T: Encodable { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - let encoder = _URLEncodedFormEncoder(context: context, - codingPath: codingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - try value.encode(to: encoder) - } -} - -extension _URLEncodedFormEncoder { - final class UnkeyedContainer { - var codingPath: [CodingKey] - - var count = 0 - var nestedCodingPath: [CodingKey] { - codingPath + [AnyCodingKey(intValue: count)!] - } - - private let context: URLEncodedFormContext - private let boolEncoding: URLEncodedFormEncoder.BoolEncoding - private let dataEncoding: URLEncodedFormEncoder.DataEncoding - private let dateEncoding: URLEncodedFormEncoder.DateEncoding - - init(context: URLEncodedFormContext, - codingPath: [CodingKey], - boolEncoding: URLEncodedFormEncoder.BoolEncoding, - dataEncoding: URLEncodedFormEncoder.DataEncoding, - dateEncoding: URLEncodedFormEncoder.DateEncoding) { - self.context = context - self.codingPath = codingPath - self.boolEncoding = boolEncoding - self.dataEncoding = dataEncoding - self.dateEncoding = dateEncoding - } - } -} - -extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer { - func encodeNil() throws { - let context = EncodingError.Context(codingPath: codingPath, - debugDescription: "URLEncodedFormEncoder cannot encode nil values.") - throw EncodingError.invalidValue("nil", context) - } - - func encode(_ value: T) throws where T: Encodable { - var container = nestedSingleValueContainer() - try container.encode(value) - } - - func nestedSingleValueContainer() -> SingleValueEncodingContainer { - defer { count += 1 } - - return _URLEncodedFormEncoder.SingleValueContainer(context: context, - codingPath: nestedCodingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } - - func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey: CodingKey { - defer { count += 1 } - let container = _URLEncodedFormEncoder.KeyedContainer(context: context, - codingPath: nestedCodingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - - return KeyedEncodingContainer(container) - } - - func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - defer { count += 1 } - - return _URLEncodedFormEncoder.UnkeyedContainer(context: context, - codingPath: nestedCodingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } - - func superEncoder() -> Encoder { - defer { count += 1 } - - return _URLEncodedFormEncoder(context: context, - codingPath: codingPath, - boolEncoding: boolEncoding, - dataEncoding: dataEncoding, - dateEncoding: dateEncoding) - } -} - -final class URLEncodedFormSerializer { - private let alphabetizeKeyValuePairs: Bool - private let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding - private let keyEncoding: URLEncodedFormEncoder.KeyEncoding - private let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding - private let allowedCharacters: CharacterSet - - init(alphabetizeKeyValuePairs: Bool, - arrayEncoding: URLEncodedFormEncoder.ArrayEncoding, - keyEncoding: URLEncodedFormEncoder.KeyEncoding, - spaceEncoding: URLEncodedFormEncoder.SpaceEncoding, - allowedCharacters: CharacterSet) { - self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs - self.arrayEncoding = arrayEncoding - self.keyEncoding = keyEncoding - self.spaceEncoding = spaceEncoding - self.allowedCharacters = allowedCharacters - } - - func serialize(_ object: URLEncodedFormComponent.Object) -> String { - var output: [String] = [] - for (key, component) in object { - let value = serialize(component, forKey: key) - output.append(value) - } - output = alphabetizeKeyValuePairs ? output.sorted() : output - - return output.joinedWithAmpersands() - } - - func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String { - switch component { - case let .string(string): return "\(escape(keyEncoding.encode(key)))=\(escape(string))" - case let .array(array): return serialize(array, forKey: key) - case let .object(object): return serialize(object, forKey: key) - } - } - - func serialize(_ object: URLEncodedFormComponent.Object, forKey key: String) -> String { - var segments: [String] = object.map { subKey, value in - let keyPath = "[\(subKey)]" - return serialize(value, forKey: key + keyPath) - } - segments = alphabetizeKeyValuePairs ? segments.sorted() : segments - - return segments.joinedWithAmpersands() - } - - func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String { - var segments: [String] = array.map { component in - let keyPath = arrayEncoding.encode(key) - return serialize(component, forKey: keyPath) - } - segments = alphabetizeKeyValuePairs ? segments.sorted() : segments - - return segments.joinedWithAmpersands() - } - - func escape(_ query: String) -> String { - var allowedCharactersWithSpace = allowedCharacters - allowedCharactersWithSpace.insert(charactersIn: " ") - let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query - let spaceEncodedQuery = spaceEncoding.encode(escapedQuery) - - return spaceEncodedQuery - } -} - -extension Array where Element == String { - func joinedWithAmpersands() -> String { - joined(separator: "&") - } -} - -extension CharacterSet { - /// Creates a CharacterSet from RFC 3986 allowed characters. - /// - /// 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. - public static let afURLQueryAllowed: CharacterSet = { - let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 - let subDelimitersToEncode = "!$&'()*+,;=" - let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") - - return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters) - }() -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift deleted file mode 100644 index be27c8ed..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLRequest+Alamofire.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// URLRequest+Alamofire.swift -// -// Copyright (c) 2019 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 URLRequest { - /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. - public var method: HTTPMethod? { - get { httpMethod.flatMap(HTTPMethod.init) } - set { httpMethod = newValue?.rawValue } - } - - public func validate() throws { - if method == .get, let bodyData = httpBody { - throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift deleted file mode 100644 index de3e290a..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// URLSessionConfiguration+Alamofire.swift -// -// Copyright (c) 2014-2018 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 URLSessionConfiguration: AlamofireExtended {} -extension AlamofireExtension where ExtendedType: URLSessionConfiguration { - /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default - /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. - public static var `default`: URLSessionConfiguration { - let configuration = URLSessionConfiguration.default - configuration.headers = .default - - return configuration - } -} diff --git a/test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift b/test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift deleted file mode 100644 index bd2a2795..00000000 --- a/test/fixtures/cocoapods/Pods/Alamofire/Source/Validation.swift +++ /dev/null @@ -1,302 +0,0 @@ -// -// Validation.swift -// -// Copyright (c) 2014-2018 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 a validation succeeded or failed. - public typealias ValidationResult = Result - - fileprivate struct MIMEType { - let type: String - let subtype: String - - var isWildcard: Bool { type == "*" && subtype == "*" } - - init?(_ string: String) { - let components: [String] = { - let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) - let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] - - 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: Range { 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.isEmpty else { return .success(()) } - - return validate(contentType: acceptableContentTypes, response: response) - } - - fileprivate func validate(contentType acceptableContentTypes: S, - response: HTTPURLResponse) - -> ValidationResult - where S.Iterator.Element == String { - 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 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 statusCode: `Sequence` of acceptable response status codes. - /// - /// - Returns: The instance. - @discardableResult - public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { - validate { [unowned self] _, response, _ in - 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: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { - validate { [unowned self] _, response, data in - 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 { - let contentTypes: () -> [String] = { [unowned self] in - self.acceptableContentTypes - } - return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) - } -} - -extension DataStreamRequest { - /// A closure used to validate a request that takes a `URLRequest` and `HTTPURLResponse` and returns whether the - /// request was valid. - public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> ValidationResult - - /// 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 statusCode: `Sequence` of acceptable response status codes. - /// - /// - Returns: The instance. - @discardableResult - public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { - validate { [unowned self] _, response in - 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: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { - validate { [unowned self] _, response in - self.validate(contentType: acceptableContentTypes(), response: response) - } - } - - /// 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 instance. - @discardableResult - public func validate() -> Self { - let contentTypes: () -> [String] = { [unowned self] in - self.acceptableContentTypes - } - return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) - } -} - -// 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, - _ fileURL: URL?) - -> ValidationResult - - /// 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 statusCode: `Sequence` of acceptable response status codes. - /// - /// - Returns: The instance. - @discardableResult - public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { - validate { [unowned self] _, response, _ in - 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: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { - validate { [unowned self] _, response, fileURL in - 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 { - let contentTypes = { [unowned self] in - self.acceptableContentTypes - } - return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift deleted file mode 100644 index 8d2529e4..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/BaseChatItemPresenter.swift +++ /dev/null @@ -1,123 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public enum ChatItemVisibility { - case hidden - case appearing - case visible -} - -open class BaseChatItemPresenter: ChatItemPresenterProtocol { - public final weak var cell: CellT? - - public init() {} - - open class func registerCells(_ collectionView: UICollectionView) { - assert(false, "Implement in subclass") - } - - open var isItemUpdateSupported: Bool { - fatalError("Implement in subclass") - } - - open func update(with chatItem: ChatItemProtocol) { - fatalError("Implement in subclass") - } - - open var canCalculateHeightInBackground: Bool { - return false - } - - open func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat { - assert(false, "Implement in subclass") - return 0 - } - - open func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { - assert(false, "Implemenent in subclass") - return UICollectionViewCell() - } - - open func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) { - assert(false, "Implemenent in subclass") - } - - final public private(set) var itemVisibility: ChatItemVisibility = .hidden - - // Need to override default implementatios. Otherwise subclasses's code won't be executed - // http://stackoverflow.com/questions/31795158/swift-2-protocol-extension-not-calling-overriden-method-correctly - public final func cellWillBeShown(_ cell: UICollectionViewCell) { - if let cell = cell as? CellT { - self.cell = cell - self.itemVisibility = .appearing - self.cellWillBeShown() - self.itemVisibility = .visible - } else { - assert(false, "Invalid cell was given to presenter!") - } - } - - open func cellWillBeShown() { - // Hook for subclasses - } - - open func shouldShowMenu() -> Bool { - return false - } - - public final func cellWasHidden(_ cell: UICollectionViewCell) { - // Carefull!! This doesn't mean that this is no longer visible - // If cell is replaced (due to a reload for instance) we can have the following sequence: - // - New cell is taken from the pool and configured. We'll get cellWillBeShown - // - Old cell is removed. We'll get cellWasHidden - // --> We need to check that this cell is the last one made visible - if let cell = cell as? CellT { - if cell === self.cell { - self.cell = nil - self.itemVisibility = .hidden - self.cellWasHidden() - } - } else { - assert(false, "Invalid cell was given to presenter!") - } - } - - open func cellWasHidden() { - // Hook for subclasses. Here we are not visible for real. - } - - open func canPerformMenuControllerAction(_ action: Selector) -> Bool { - return false - } - - open func performMenuControllerAction(_ action: Selector) { - assert(self.canPerformMenuControllerAction(action)) - } - - // MARK: - ChatItemSpotlighting - - open func spotlight() {} -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift deleted file mode 100644 index 489b1a25..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemCompanion.swift +++ /dev/null @@ -1,52 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2015-present Badoo Trading Limited. - -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 protocol ChatItemsDecoratorProtocol { - func decorateItems(_ chatItems: [ChatItemProtocol]) -> [DecoratedChatItem] -} - -public struct DecoratedChatItem: UniqueIdentificable { - public let uid: String - public let chatItem: ChatItemProtocol - public let decorationAttributes: ChatItemDecorationAttributesProtocol? - - public init(chatItem: ChatItemProtocol, decorationAttributes: ChatItemDecorationAttributesProtocol?) { - self.init(uid: chatItem.uid, chatItem: chatItem, decorationAttributes: decorationAttributes) - } - - public init(uid: String, chatItem: ChatItemProtocol, decorationAttributes: ChatItemDecorationAttributesProtocol?) { - self.uid = uid - self.chatItem = chatItem - self.decorationAttributes = decorationAttributes - } -} - -public struct ChatItemCompanion: UniqueIdentificable { - public let uid: String - public let chatItem: ChatItemProtocol - public let presenter: ChatItemPresenterProtocol - public var decorationAttributes: ChatItemDecorationAttributesProtocol? -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift deleted file mode 100644 index c4a975a5..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift +++ /dev/null @@ -1,84 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public typealias ChatItemType = String - -public protocol ChatItemProtocol: AnyObject, UniqueIdentificable { - var type: ChatItemType { get } -} - -public protocol ChatItemDecorationAttributesProtocol { - var bottomMargin: CGFloat { get } -} - -public protocol ChatItemMenuPresenterProtocol { - func shouldShowMenu() -> Bool - func canPerformMenuControllerAction(_ action: Selector) -> Bool - func performMenuControllerAction(_ action: Selector) -} - -public protocol ChatItemSpotlighting { - func spotlight() -} - -extension ChatItemSpotlighting { - public func spotlight() {} -} - -public protocol ChatItemPresenterProtocol: AnyObject, ChatItemMenuPresenterProtocol, ChatItemSpotlighting { - static func registerCells(_ collectionView: UICollectionView) - - var isItemUpdateSupported: Bool { get } - func update(with chatItem: ChatItemProtocol) - - var canCalculateHeightInBackground: Bool { get } // Default is false - func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat - func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell - func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) - func cellWillBeShown(_ cell: UICollectionViewCell) // optional - func cellWasHidden(_ cell: UICollectionViewCell) // optional -} - -public extension ChatItemPresenterProtocol { // Optionals - var canCalculateHeightInBackground: Bool { return false } - func cellWillBeShown(_ cell: UICollectionViewCell) {} - func cellWasHidden(_ cell: UICollectionViewCell) {} - func shouldShowMenu() -> Bool { return false } - func canPerformMenuControllerAction(_ action: Selector) -> Bool { return false } - func performMenuControllerAction(_ action: Selector) {} -} - -public protocol ChatItemPresenterBuilderProtocol { - func canHandleChatItem(_ chatItem: ChatItemProtocol) -> Bool - func createPresenterWithChatItem(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol - var presenterType: ChatItemPresenterProtocol.Type { get } -} - -// MARK: - Updatable Chat Items - -public protocol ContentEquatableChatItemProtocol: ChatItemProtocol { - func hasSameContent(as anotherItem: ChatItemProtocol) -> Bool -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift deleted file mode 100644 index 9c0ddd65..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Chat Items/DummyChatItemPresenter.swift +++ /dev/null @@ -1,57 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -// Handles messages which aren't supported. So, they appear as invisible. -class DummyChatItemPresenter: ChatItemPresenterProtocol { - - class func registerCells(_ collectionView: UICollectionView) { - collectionView.register(DummyCollectionViewCell.self, forCellWithReuseIdentifier: "cell-id-unhandled-message") - } - - let isItemUpdateSupported = true - - func update(with chatItem: ChatItemProtocol) { - // Does nothing - } - - var canCalculateHeightInBackground: Bool { - return true - } - - func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat { - return 0 - } - - func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { - return collectionView.dequeueReusableCell(withReuseIdentifier: "cell-id-unhandled-message", for: indexPath) - } - - func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) { - cell.isHidden = true - } -} - -class DummyCollectionViewCell: UICollectionViewCell {} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift deleted file mode 100644 index 0f5bdb66..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift +++ /dev/null @@ -1,36 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 BaseChatViewController { // Cell Pan Gesture Handler - - final var cellPanGestureHandlerIsEnabled: Bool { - get { - return self.cellPanGestureHandler.isEnabled - } set { - self.cellPanGestureHandler.isEnabled = newValue - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift deleted file mode 100644 index 0d2ff2b0..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Changes.swift +++ /dev/null @@ -1,373 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 struct HashableItem: Hashable { - private let uid: String - private let type: String - - init(_ decoratedChatItem: DecoratedChatItem) { - self.uid = decoratedChatItem.uid - self.type = decoratedChatItem.chatItem.type - } - - init(_ chatItemCompanion: ChatItemCompanion) { - self.uid = chatItemCompanion.uid - self.type = chatItemCompanion.chatItem.type - } -} - -extension BaseChatViewController { - - public func enqueueModelUpdate(updateType: UpdateType, completion: (() -> Void)? = nil) { - let newItems = self.chatDataSource?.chatItems ?? [] - - if self.updatesConfig.coalesceUpdates { - self.updateQueue.flushQueue() - } - - self.updateQueue.addTask({ [weak self] (runNextTask) -> Void in - guard let sSelf = self else { return } - - let oldItems = sSelf.chatItemCompanionCollection - sSelf.updateModels(newItems: newItems, oldItems: oldItems, updateType: updateType, completion: { - guard let sSelf = self else { return } - if sSelf.updateQueue.isEmpty { - sSelf.enqueueMessageCountReductionIfNeeded() - } - completion?() - DispatchQueue.main.async(execute: { () -> Void in - // Reduces inconsistencies before next update: https://github.com/diegosanchezr/UICollectionViewStressing - runNextTask() - }) - }) - }) - } - - public func enqueueMessageCountReductionIfNeeded() { - guard let preferredMaxMessageCount = self.constants.preferredMaxMessageCount, (self.chatDataSource?.chatItems.count ?? 0) > preferredMaxMessageCount else { return } - self.updateQueue.addTask { [weak self] (completion) -> Void in - guard let sSelf = self else { return } - sSelf.chatDataSource?.adjustNumberOfMessages(preferredMaxCount: sSelf.constants.preferredMaxMessageCountAdjustment, focusPosition: sSelf.focusPosition, completion: { (didAdjust) -> Void in - guard didAdjust, let sSelf = self else { - completion() - return - } - let newItems = sSelf.chatDataSource?.chatItems ?? [] - let oldItems = sSelf.chatItemCompanionCollection - sSelf.updateModels(newItems: newItems, oldItems: oldItems, updateType: .messageCountReduction, completion: completion ) - }) - } - } - - // Returns scrolling position in interval [0, 1], 0 top, 1 bottom - public var focusPosition: Double { - guard let collectionView = self.collectionView else { return 0 } - if self.isCloseToBottom() { - return 1 - } else if self.isCloseToTop() { - return 0 - } - - let contentHeight = collectionView.contentSize.height - guard contentHeight > 0 else { - return 0.5 - } - - // Rough estimation - let collectionViewContentYOffset = collectionView.contentOffset.y - let midContentOffset = collectionViewContentYOffset + self.visibleRect().height / 2 - return min(max(0, Double(midContentOffset / contentHeight)), 1.0) - } - - func updateVisibleCells(_ changes: CollectionChanges) { - // Datasource should be already updated! - - assert(self.visibleCellsAreValid(changes: changes), "Invalid visible cells. Don't call me") - - let cellsToUpdate = updated(collection: self.visibleCellsFromCollectionViewApi(), withChanges: changes) - self.visibleCells = cellsToUpdate - - cellsToUpdate.forEach { (indexPath, cell) in - let presenter = self.presenterForIndexPath(indexPath) - presenter.configureCell(cell, decorationAttributes: self.decorationAttributesForIndexPath(indexPath)) - presenter.cellWillBeShown(cell) // `createModelUpdates` may have created a new presenter instance for existing visible cell so we need to tell it that its cell is visible - } - } - - private func visibleCellsFromCollectionViewApi() -> [IndexPath: UICollectionViewCell] { - var visibleCells: [IndexPath: UICollectionViewCell] = [:] - guard let collectionView = self.collectionView else { return visibleCells } - collectionView.indexPathsForVisibleItems.forEach({ (indexPath) in - if let cell = collectionView.cellForItem(at: indexPath) { - visibleCells[indexPath] = cell - } - }) - return visibleCells - } - - private func visibleCellsAreValid(changes: CollectionChanges) -> Bool { - // Afer performBatchUpdates, indexPathForCell may return a cell refering to the state before the update - // if self.updatesConfig.fastUpdates is enabled, very fast updates could result in `updateVisibleCells` updating wrong cells. - // See more: https://github.com/diegosanchezr/UICollectionViewStressing - - if self.updatesConfig.fastUpdates { - return updated(collection: self.visibleCells, withChanges: changes) == updated(collection: self.visibleCellsFromCollectionViewApi(), withChanges: changes) - } else { - return true // never seen inconsistency without fastUpdates - } - } - - private enum ScrollAction { - case scrollToBottom - case preservePosition(rectForReferenceIndexPathBeforeUpdate: CGRect?, referenceIndexPathAfterUpdate: IndexPath?) - } - - func performBatchUpdates(updateModelClosure: @escaping () -> Void, // swiftlint:disable:this cyclomatic_complexity - changes: CollectionChanges, - updateType: UpdateType, - completion: @escaping () -> Void) { - guard let collectionView = self.collectionView else { - completion() - return - } - let usesBatchUpdates: Bool - do { // Recover from too fast updates... - let visibleCellsAreValid = self.visibleCellsAreValid(changes: changes) - let wantsReloadData = updateType != .normal - let hasUnfinishedBatchUpdates = self.unfinishedBatchUpdatesCount > 0 // This can only happen when enabling self.updatesConfig.fastUpdates - - // a) It's unsafe to perform reloadData while there's a performBatchUpdates animating: https://github.com/diegosanchezr/UICollectionViewStressing/tree/master/GhostCells - // Note: using reloadSections instead reloadData is safe and might not need a delay. However, using always reloadSections causes flickering on pagination and a crash on the first layout that needs a workaround. Let's stick to reloaData for now - // b) If it's a performBatchUpdates but visible cells are invalid let's wait until all finish (otherwise we would give wrong cells to presenters in updateVisibleCells) - let mustDelayUpdate = hasUnfinishedBatchUpdates && (wantsReloadData || !visibleCellsAreValid) - guard !mustDelayUpdate else { - // For reference, it is possible to force the current performBatchUpdates to finish in the next run loop, by cancelling animations: - // self.collectionView.subviews.forEach { $0.layer.removeAllAnimations() } - self.onAllBatchUpdatesFinished = { [weak self] in - self?.onAllBatchUpdatesFinished = nil - self?.performBatchUpdates(updateModelClosure: updateModelClosure, changes: changes, updateType: updateType, completion: completion) - } - return - } - // ... if they are still invalid the only thing we can do is a reloadData - let mustDoReloadData = !visibleCellsAreValid // Only way to recover from this inconsistent state - usesBatchUpdates = !wantsReloadData && !mustDoReloadData - } - - let scrollAction: ScrollAction - do { // Scroll action - if updateType != .pagination && self.isScrolledAtBottom() { - scrollAction = .scrollToBottom - } else { - let (oldReferenceIndexPath, newReferenceIndexPath) = self.referenceIndexPathsToRestoreScrollPositionOnUpdate(itemsBeforeUpdate: self.chatItemCompanionCollection, changes: changes) - let oldRect = self.rectAtIndexPath(oldReferenceIndexPath) - scrollAction = .preservePosition(rectForReferenceIndexPathBeforeUpdate: oldRect, referenceIndexPathAfterUpdate: newReferenceIndexPath) - } - } - - let myCompletion: () -> Void - do { // Completion - var myCompletionExecuted = false - myCompletion = { - if myCompletionExecuted { return } - myCompletionExecuted = true - completion() - } - } - - if usesBatchUpdates { - UIView.animate(withDuration: self.constants.updatesAnimationDuration, animations: { () -> Void in - self.unfinishedBatchUpdatesCount += 1 - collectionView.performBatchUpdates({ () -> Void in - updateModelClosure() - self.updateVisibleCells(changes) // For instance, to support removal of tails - - collectionView.deleteItems(at: Array(changes.deletedIndexPaths)) - collectionView.insertItems(at: Array(changes.insertedIndexPaths)) - for move in changes.movedIndexPaths { - collectionView.moveItem(at: move.indexPathOld, to: move.indexPathNew) - } - }, completion: { [weak self] (_) -> Void in - defer { myCompletion() } - guard let sSelf = self else { return } - sSelf.unfinishedBatchUpdatesCount -= 1 - if sSelf.unfinishedBatchUpdatesCount == 0, let onAllBatchUpdatesFinished = self?.onAllBatchUpdatesFinished { - DispatchQueue.main.async(execute: onAllBatchUpdatesFinished) - } - }) - if self.placeMessagesFromBottom { - self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) - } - }) - } else { - self.visibleCells = [:] - updateModelClosure() - collectionView.reloadData() - collectionView.collectionViewLayout.prepare() - if self.placeMessagesFromBottom { - self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) - } - } - - switch scrollAction { - case .scrollToBottom: - self.scrollToBottom(animated: updateType == .normal) - case .preservePosition(rectForReferenceIndexPathBeforeUpdate: let oldRect, referenceIndexPathAfterUpdate: let indexPath): - let newRect = self.rectAtIndexPath(indexPath) - self.scrollToPreservePosition(oldRefRect: oldRect, newRefRect: newRect) - } - - if !usesBatchUpdates || self.updatesConfig.fastUpdates { - myCompletion() - } - } - - private func updateModels(newItems: [ChatItemProtocol], oldItems: ChatItemCompanionCollection, updateType: UpdateType, completion: @escaping () -> Void) { - guard let collectionView = self.collectionView else { - completion() - return - } - let collectionViewWidth = collectionView.bounds.width - let updateType = self.isFirstLayout ? .firstLoad : updateType - let performInBackground = updateType != .firstLoad - - self.autoLoadingEnabled = false - let perfomBatchUpdates: (_ changes: CollectionChanges, _ updateModelClosure: @escaping () -> Void) -> Void = { [weak self] (changes, updateModelClosure) in - self?.performBatchUpdates( - updateModelClosure: updateModelClosure, - changes: changes, - updateType: updateType, - completion: { () -> Void in - self?.autoLoadingEnabled = true - completion() - }) - } - - let createModelUpdate = { - return self.createModelUpdates( - newItems: newItems, - oldItems: oldItems, - collectionViewWidth: collectionViewWidth) - } - - if performInBackground { - DispatchQueue.global(qos: .userInitiated).async { () -> Void in - let modelUpdate = createModelUpdate() - DispatchQueue.main.async(execute: { () -> Void in - perfomBatchUpdates(modelUpdate.changes, modelUpdate.updateModelClosure) - }) - } - } else { - let modelUpdate = createModelUpdate() - perfomBatchUpdates(modelUpdate.changes, modelUpdate.updateModelClosure) - } - } - - private func createModelUpdates(newItems: [ChatItemProtocol], oldItems: ChatItemCompanionCollection, collectionViewWidth: CGFloat) -> (changes: CollectionChanges, updateModelClosure: () -> Void) { - let newDecoratedItems = self.chatItemsDecorator?.decorateItems(newItems) ?? newItems.map { DecoratedChatItem(chatItem: $0, decorationAttributes: nil) } - let changes = Chatto.generateChanges(oldCollection: oldItems.map(HashableItem.init), - newCollection: newDecoratedItems.map(HashableItem.init)) - let itemCompanionCollection = self.createCompanionCollection(fromChatItems: newDecoratedItems, previousCompanionCollection: oldItems) - let layoutModel = self.createLayoutModel(itemCompanionCollection, collectionViewWidth: collectionViewWidth) - let updateModelClosure : () -> Void = { [weak self] in - self?.layoutModel = layoutModel - self?.chatItemCompanionCollection = itemCompanionCollection - } - return (changes, updateModelClosure) - } - - private func createCompanionCollection(fromChatItems newItems: [DecoratedChatItem], previousCompanionCollection oldItems: ChatItemCompanionCollection) -> ChatItemCompanionCollection { - return ChatItemCompanionCollection(items: newItems.map { (decoratedChatItem) -> ChatItemCompanion in - - /* - We use an assumption, that message having a specific messageId never changes its type. - If such changes has to be supported, then generation of changes has to suppport reloading items. - Otherwise, updateVisibleCells may try to update the existing cells with new presenters which aren't able to work with another types. - */ - - let presenter: ChatItemPresenterProtocol = { - guard let oldChatItemCompanion = oldItems[decoratedChatItem.uid] ?? oldItems[decoratedChatItem.chatItem.uid], - oldChatItemCompanion.chatItem.type == decoratedChatItem.chatItem.type, - oldChatItemCompanion.presenter.isItemUpdateSupported else { - return self.createPresenterForChatItem(decoratedChatItem.chatItem) - } - - oldChatItemCompanion.presenter.update(with: decoratedChatItem.chatItem) - return oldChatItemCompanion.presenter - }() - - return ChatItemCompanion(uid: decoratedChatItem.uid, chatItem: decoratedChatItem.chatItem, presenter: presenter, decorationAttributes: decoratedChatItem.decorationAttributes) - }) - } - - private func createLayoutModel(_ items: ChatItemCompanionCollection, collectionViewWidth: CGFloat) -> ChatCollectionViewLayoutModel { - // swiftlint:disable:next nesting - typealias IntermediateItemLayoutData = (height: CGFloat?, bottomMargin: CGFloat) - typealias ItemLayoutData = (height: CGFloat, bottomMargin: CGFloat) - // swiftlint:disable:previous nesting - - func createLayoutModel(intermediateLayoutData: [IntermediateItemLayoutData]) -> ChatCollectionViewLayoutModel { - let layoutData = intermediateLayoutData.map { (intermediateLayoutData: IntermediateItemLayoutData) -> ItemLayoutData in - return (height: intermediateLayoutData.height!, bottomMargin: intermediateLayoutData.bottomMargin) - } - return ChatCollectionViewLayoutModel.createModel(collectionViewWidth, itemsLayoutData: layoutData) - } - - let isInbackground = !Thread.isMainThread - var intermediateLayoutData = [IntermediateItemLayoutData]() - var itemsForMainThread = [(index: Int, itemCompanion: ChatItemCompanion)]() - - for (index, itemCompanion) in items.enumerated() { - var height: CGFloat? - let bottomMargin: CGFloat = itemCompanion.decorationAttributes?.bottomMargin ?? 0 - if !isInbackground || itemCompanion.presenter.canCalculateHeightInBackground { - height = itemCompanion.presenter.heightForCell(maximumWidth: collectionViewWidth, decorationAttributes: itemCompanion.decorationAttributes) - } else { - itemsForMainThread.append((index: index, itemCompanion: itemCompanion)) - } - intermediateLayoutData.append((height: height, bottomMargin: bottomMargin)) - } - - if itemsForMainThread.count > 0 { - DispatchQueue.main.sync(execute: { () -> Void in - for (index, itemCompanion) in itemsForMainThread { - let height = itemCompanion.presenter.heightForCell(maximumWidth: collectionViewWidth, decorationAttributes: itemCompanion.decorationAttributes) - intermediateLayoutData[index].height = height - } - }) - } - return createLayoutModel(intermediateLayoutData: intermediateLayoutData) - } - - public func chatCollectionViewLayoutModel() -> ChatCollectionViewLayoutModel { - guard let collectionView = self.collectionView else { return self.layoutModel } - if self.layoutModel.calculatedForWidth != collectionView.bounds.width { - self.layoutModel = self.createLayoutModel(self.chatItemCompanionCollection, collectionViewWidth: collectionView.bounds.width) - } - return self.layoutModel - } - -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift deleted file mode 100644 index c3aa5663..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Presenters.swift +++ /dev/null @@ -1,137 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -extension BaseChatViewController: ChatCollectionViewLayoutDelegate { - - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return self.chatItemCompanionCollection.count - } - - @objc(collectionView:cellForItemAtIndexPath:) - public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let presenter = self.presenterForIndexPath(indexPath) - let cell = presenter.dequeueCell(collectionView: collectionView, indexPath: indexPath) - let decorationAttributes = self.decorationAttributesForIndexPath(indexPath) - presenter.configureCell(cell, decorationAttributes: decorationAttributes) - return cell - } - - @objc(collectionView:didEndDisplayingCell:forItemAtIndexPath:) - open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - // Carefull: this index path can refer to old data source after an update. Don't use it to grab items from the model - // Instead let's use a mapping presenter <--> cell - if let oldPresenterForCell = self.presentersByCell.object(forKey: cell) as? ChatItemPresenterProtocol { - self.presentersByCell.removeObject(forKey: cell) - oldPresenterForCell.cellWasHidden(cell) - } - - if self.updatesConfig.fastUpdates { - if let visibleCell = self.visibleCells[indexPath], visibleCell === cell { - self.visibleCells[indexPath] = nil - } else { - self.visibleCells.forEach({ (indexPath, storedCell) in - if cell === storedCell { - // Inconsistency found, likely due to very fast updates - self.visibleCells[indexPath] = nil - } - }) - } - } - } - - @objc(collectionView:willDisplayCell:forItemAtIndexPath:) - open func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - // Here indexPath should always referer to updated data source. - - let presenter = self.presenterForIndexPath(indexPath) - self.presentersByCell.setObject(presenter, forKey: cell) - if self.updatesConfig.fastUpdates { - self.visibleCells[indexPath] = cell - } - - if self.isAdjustingInputContainer { - UIView.performWithoutAnimation({ - // See https://github.com/badoo/Chatto/issues/133 - presenter.cellWillBeShown(cell) - cell.layoutIfNeeded() - }) - } else { - presenter.cellWillBeShown(cell) - } - } - - @objc(collectionView:shouldShowMenuForItemAtIndexPath:) - open func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath?) -> Bool { - // Note: IndexPath set optional due to https://github.com/badoo/Chatto/issues/310 - // Might be related: https://bugs.swift.org/browse/SR-2417 - guard let indexPath = indexPath else { return false } - return self.presenterForIndexPath(indexPath).shouldShowMenu() - } - - @objc(collectionView:canPerformAction:forItemAtIndexPath:withSender:) - open func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath?, withSender sender: Any?) -> Bool { - // Note: IndexPath set optional due to https://github.com/badoo/Chatto/issues/247. SR-2417 might be related - // Might be related: https://bugs.swift.org/browse/SR-2417 - guard let indexPath = indexPath else { return false } - return self.presenterForIndexPath(indexPath).canPerformMenuControllerAction(action) - } - - @objc(collectionView:performAction:forItemAtIndexPath:withSender:) - open func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) { - self.presenterForIndexPath(indexPath).performMenuControllerAction(action) - } - - func presenterForIndexPath(_ indexPath: IndexPath) -> ChatItemPresenterProtocol { - return self.presenterForIndex(indexPath.item, chatItemCompanionCollection: self.chatItemCompanionCollection) - } - - func presenterForIndex(_ index: Int, chatItemCompanionCollection items: ChatItemCompanionCollection) -> ChatItemPresenterProtocol { - guard index < items.count else { - // This can happen from didEndDisplayingCell if we reloaded with less messages - return DummyChatItemPresenter() - } - return items[index].presenter - } - - public func createPresenterForChatItem(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol { - assert(self.presenterFactory != nil, "Presenter factory is not initialized") - return self.presenterFactory.createChatItemPresenter(chatItem) - } - - public func confugureCollectionViewWithPresenters() { - assert(self.presenterFactory == nil, "Presenter factory is already initialized") - guard let collectionView = self.collectionView else { - assertionFailure("CollectionView is not initialized") - return - } - self.presenterFactory = self.createPresenterFactory() - self.presenterFactory.configure(withCollectionView: collectionView ) - } - - public func decorationAttributesForIndexPath(_ indexPath: IndexPath) -> ChatItemDecorationAttributesProtocol? { - return self.chatItemCompanionCollection[indexPath.item].decorationAttributes - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift deleted file mode 100644 index 96453ad3..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift +++ /dev/null @@ -1,205 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public enum CellVerticalEdge { - case top - case bottom -} - -extension CGFloat { - static let bma_epsilon: CGFloat = 0.001 -} - -extension BaseChatViewController { - - private static var nextDidEndScrollingAnimationHandlersKey: Int = 0 - private var nextDidEndScrollingAnimationHandlers: [() -> Void] { - get { objc_getAssociatedObject(self, &Self.nextDidEndScrollingAnimationHandlersKey) as? [() -> Void] ?? [] } - set { objc_setAssociatedObject(self, &Self.nextDidEndScrollingAnimationHandlersKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - - public func isScrolledAtBottom() -> Bool { - guard let collectionView = self.collectionView else { return true } - guard collectionView.numberOfSections > 0 && collectionView.numberOfItems(inSection: 0) > 0 else { return true } - let sectionIndex = collectionView.numberOfSections - 1 - let itemIndex = collectionView.numberOfItems(inSection: sectionIndex) - 1 - let lastIndexPath = IndexPath(item: itemIndex, section: sectionIndex) - return self.isIndexPathVisible(lastIndexPath, atEdge: .bottom) - } - - public func isScrolledAtTop() -> Bool { - guard let collectionView = self.collectionView else { return true } - guard collectionView.numberOfSections > 0 && collectionView.numberOfItems(inSection: 0) > 0 else { return true } - let firstIndexPath = IndexPath(item: 0, section: 0) - return self.isIndexPathVisible(firstIndexPath, atEdge: .top) - } - - public func isCloseToBottom() -> Bool { - guard let collectionView = self.collectionView else { return true } - guard collectionView.contentSize.height > 0 else { return true } - return (self.visibleRect().maxY / collectionView.contentSize.height) > (1 - self.constants.autoloadingFractionalThreshold) - } - - public func isCloseToTop() -> Bool { - guard let collectionView = self.collectionView else { return true } - guard collectionView.contentSize.height > 0 else { return true } - return (self.visibleRect().minY / collectionView.contentSize.height) < self.constants.autoloadingFractionalThreshold - } - - public func isIndexPathVisible(_ indexPath: IndexPath, atEdge edge: CellVerticalEdge) -> Bool { - guard let collectionView = self.collectionView else { return true } - guard let attributes = collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath) else { return false } - let visibleRect = self.visibleRect() - let intersection = visibleRect.intersection(attributes.frame) - if edge == .top { - return abs(intersection.minY - attributes.frame.minY) < CGFloat.bma_epsilon - } else { - return abs(intersection.maxY - attributes.frame.maxY) < CGFloat.bma_epsilon - } - } - - public func visibleRect() -> CGRect { - guard let collectionView = self.collectionView else { return CGRect.zero } - let contentInset = collectionView.contentInset - let collectionViewBounds = collectionView.bounds - let contentSize = collectionView.collectionViewLayout.collectionViewContentSize - return CGRect(x: CGFloat(0), y: collectionView.contentOffset.y + contentInset.top, width: collectionViewBounds.width, height: min(contentSize.height, collectionViewBounds.height - contentInset.top - contentInset.bottom)) - } - - @objc - open func scrollToBottom(animated: Bool) { - guard let collectionView = self.collectionView else { return } - // Cancel current scrolling - collectionView.setContentOffset(collectionView.contentOffset, animated: false) - - // Note that we don't rely on collectionView's contentSize. This is because it won't be valid after performBatchUpdates or reloadData - // After reload data, collectionViewLayout.collectionViewContentSize won't be even valid, so you may want to refresh the layout manually - let offsetY = max(-collectionView.contentInset.top, collectionView.collectionViewLayout.collectionViewContentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom) - - // Don't use setContentOffset(:animated). If animated, contentOffset property will be updated along with the animation for each frame update - // If a message is inserted while scrolling is happening (as in very fast typing), we want to take the "final" content offset (not the "real time" one) to check if we should scroll to bottom again - if animated { - UIView.animate(withDuration: self.constants.updatesAnimationDuration, animations: { () -> Void in - collectionView.contentOffset = CGPoint(x: 0, y: offsetY) - }) - } else { - collectionView.contentOffset = CGPoint(x: 0, y: offsetY) - } - } - - public func scrollToPreservePosition(oldRefRect: CGRect?, newRefRect: CGRect?) { - guard let collectionView = self.collectionView else { return } - guard let oldRefRect = oldRefRect, let newRefRect = newRefRect else { - return - } - let diffY = newRefRect.minY - oldRefRect.minY - collectionView.contentOffset = CGPoint(x: 0, y: collectionView.contentOffset.y + diffY) - } - - public func scrollToItem(withId itemId: String, - position: UICollectionView.ScrollPosition = .centeredVertically, - animated: Bool = false, - spotlight: Bool = false) { - guard let collectionView = self.collectionView else { return } - guard let itemIndex = self.chatItemCompanionCollection.indexOf(itemId) else { return } - - let indexPath = IndexPath(item: itemIndex, section: 0) - guard let rect = self.rectAtIndexPath(indexPath) else { return } - - if animated { - let pageHeight = collectionView.bounds.height - let twoPagesHeight = pageHeight * 2 - let isScrollingUp = rect.minY < collectionView.contentOffset.y - - if isScrollingUp { - let isNeedToScrollUpMoreThenTwoPages = rect.minY < collectionView.contentOffset.y - twoPagesHeight - if isNeedToScrollUpMoreThenTwoPages { - let lastPageOriginY = collectionView.contentSize.height - pageHeight - var preScrollRect = rect - preScrollRect.origin.y = min(lastPageOriginY, rect.minY + pageHeight) - collectionView.scrollRectToVisible(preScrollRect, animated: false) - } - } else { - let isNeedToScrollDownMoreThenTwoPages = rect.minY > collectionView.contentOffset.y + twoPagesHeight - if isNeedToScrollDownMoreThenTwoPages { - var preScrollRect = rect - preScrollRect.origin.y = max(0, rect.minY - pageHeight) - collectionView.scrollRectToVisible(preScrollRect, animated: false) - } - } - } - - if spotlight { - guard let presenter = self.chatItemCompanionCollection[itemId]?.presenter else { return } - let contentOffsetWillBeChanged = !collectionView.indexPathsForVisibleItems.contains(indexPath) - if contentOffsetWillBeChanged { - self.nextDidEndScrollingAnimationHandlers.append { [weak presenter] in - presenter?.spotlight() - } - } else { - presenter.spotlight() - } - } - - collectionView.scrollToItem(at: indexPath, at: position, animated: animated) - } - - open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { - for handler in self.nextDidEndScrollingAnimationHandlers { - handler() - } - self.nextDidEndScrollingAnimationHandlers = [] - } - - open func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard let collectionView = self.collectionView else { return } - if collectionView.isDragging { - self.autoLoadMoreContentIfNeeded() - } - self.scrollViewEventsHandler?.onScrollViewDidScroll(scrollView) - } - - open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - self.scrollViewEventsHandler?.onScrollViewDidEndDragging(scrollView, decelerate) - } - - open func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { - self.autoLoadMoreContentIfNeeded() - } - - open func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {} - open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {} - - public func autoLoadMoreContentIfNeeded() { - guard self.autoLoadingEnabled, let dataSource = self.chatDataSource else { return } - - if self.isCloseToTop() && dataSource.hasMorePrevious { - dataSource.loadPrevious() - } else if self.isCloseToBottom() && dataSource.hasMoreNext { - dataSource.loadNext() - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift deleted file mode 100644 index e94221ce..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/BaseChatViewController.swift +++ /dev/null @@ -1,600 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public protocol KeyboardEventsHandling: AnyObject { - func onKeyboardStateDidChange(_ height: CGFloat, _ status: KeyboardStatus) -} - -public protocol ScrollViewEventsHandling: AnyObject { - func onScrollViewDidScroll(_ scrollView: UIScrollView) - func onScrollViewDidEndDragging(_ scrollView: UIScrollView, _ decelerate: Bool) -} - -public protocol ReplyActionHandler: AnyObject { - func handleReply(for: ChatItemProtocol) -} - -open class BaseChatViewController: UIViewController, - UICollectionViewDataSource, - UICollectionViewDelegate, - ChatDataSourceDelegateProtocol, - InputPositionControlling, - ReplyIndicatorRevealerDelegate { - - open weak var keyboardEventsHandler: KeyboardEventsHandling? - open weak var scrollViewEventsHandler: ScrollViewEventsHandling? - open var replyActionHandler: ReplyActionHandler? - open var replyFeedbackGenerator: ReplyFeedbackGeneratorProtocol? = BaseChatViewController.makeReplyFeedbackGenerator() - - open var layoutConfiguration: ChatLayoutConfigurationProtocol = ChatLayoutConfiguration.defaultConfiguration { - didSet { - self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) - } - } - - public struct Constants { - public var updatesAnimationDuration: TimeInterval = 0.33 - public var preferredMaxMessageCount: Int? = 500 // If not nil, will ask data source to reduce number of messages when limit is reached. @see ChatDataSourceDelegateProtocol - public var preferredMaxMessageCountAdjustment: Int = 400 // When the above happens, will ask to adjust with this value. It may be wise for this to be smaller to reduce number of adjustments - public var autoloadingFractionalThreshold: CGFloat = 0.05 // in [0, 1] - } - - public var constants = Constants() - - public struct UpdatesConfig { - - // Allows another performBatchUpdates to be called before completion of a previous one (not recommended). - // Changing this value after viewDidLoad is not supported - public var fastUpdates = true - - // If receiving data source updates too fast, while an update it's being processed, only the last one will be executed - public var coalesceUpdates = true - } - - public var updatesConfig = UpdatesConfig() - - open var customPresentersConfigurationPoint = false // If true then confugureCollectionViewWithPresenters() will not be called in viewDidLoad() method and has to be called manually - - public private(set) var collectionView: UICollectionView? - public final internal(set) var chatItemCompanionCollection = ChatItemCompanionCollection(items: []) - private var _chatDataSource: ChatDataSourceProtocol? - public final var chatDataSource: ChatDataSourceProtocol? { - get { - return _chatDataSource - } - set { - self.setChatDataSource(newValue, triggeringUpdateType: .normal) - } - } - - // If set to false messages will start appearing on top and goes down - // If true then messages will start from bottom and goes up. - public var placeMessagesFromBottom = false { - didSet { - self.adjustCollectionViewInsets(shouldUpdateContentOffset: false) - } - } - - // If set to false user is responsible to make sure that view provided in loadView() implements BaseChatViewContollerViewProtocol. - // Must be set before loadView is called. - public var substitutesMainViewAutomatically = true - - // Custom update on setting the data source. if triggeringUpdateType is nil it won't enqueue any update (you should do it later manually) - public final func setChatDataSource(_ dataSource: ChatDataSourceProtocol?, triggeringUpdateType updateType: UpdateType?) { - self._chatDataSource = dataSource - self._chatDataSource?.delegate = self - if let updateType = updateType { - self.enqueueModelUpdate(updateType: updateType) - } - } - - deinit { - self.collectionView?.delegate = nil - self.collectionView?.dataSource = nil - } - - open override func loadView() { // swiftlint:disable:this prohibited_super_call - if substitutesMainViewAutomatically { - self.view = BaseChatViewControllerView() // http://stackoverflow.com/questions/24596031/uiviewcontroller-with-inputaccessoryview-is-not-deallocated - self.view.backgroundColor = UIColor.white - } else { - super.loadView() - } - - } - - override open func viewDidLoad() { - super.viewDidLoad() - self.addCollectionView() - self.addInputBarContainer() - self.addInputView() - self.addInputContentContainer() - self.setupKeyboardTracker() - self.setupTapGestureRecognizer() - } - - private func setupTapGestureRecognizer() { - self.collectionView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(BaseChatViewController.userDidTapOnCollectionView))) - } - - public var endsEditingWhenTappingOnChatBackground = true - @objc - open func userDidTapOnCollectionView() { - if self.endsEditingWhenTappingOnChatBackground { - self.view.endEditing(true) - } - } - - open override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - self.keyboardTracker.startTracking() - } - - open override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - self.keyboardTracker?.stopTracking() - } - - private func addCollectionView() { - let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: self.createCollectionViewLayout()) - collectionView.contentInset = self.layoutConfiguration.contentInsets - collectionView.scrollIndicatorInsets = self.layoutConfiguration.scrollIndicatorInsets - collectionView.alwaysBounceVertical = true - collectionView.backgroundColor = UIColor.clear - collectionView.keyboardDismissMode = .interactive - collectionView.showsVerticalScrollIndicator = true - collectionView.showsHorizontalScrollIndicator = false - collectionView.allowsSelection = false - collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.autoresizingMask = [] - self.view.addSubview(collectionView) - NSLayoutConstraint.activate([ - self.view.topAnchor.constraint(equalTo: collectionView.topAnchor), - self.view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) - ]) - - let leadingAnchor: NSLayoutXAxisAnchor - let trailingAnchor: NSLayoutXAxisAnchor - if #available(iOS 11.0, *) { - let guide = self.view.safeAreaLayoutGuide - leadingAnchor = guide.leadingAnchor - trailingAnchor = guide.trailingAnchor - } else { - leadingAnchor = self.view.leadingAnchor - trailingAnchor = self.view.trailingAnchor - } - - NSLayoutConstraint.activate([ - collectionView.leadingAnchor.constraint(equalTo: leadingAnchor), - collectionView.trailingAnchor.constraint(equalTo: trailingAnchor) - ]) - - collectionView.dataSource = self - collectionView.delegate = self - collectionView.chatto_setContentInsetAdjustment(enabled: false, in: self) - collectionView.chatto_setAutomaticallyAdjustsScrollIndicatorInsets(false) - collectionView.chatto_setIsPrefetchingEnabled(false) - - self.cellPanGestureHandler = CellPanGestureHandler(collectionView: collectionView) - self.cellPanGestureHandler.replyDelegate = self - self.cellPanGestureHandler.config = self.cellPanGestureHandlerConfig - self.collectionView = collectionView - - if !self.customPresentersConfigurationPoint { - self.confugureCollectionViewWithPresenters() - } - } - - var unfinishedBatchUpdatesCount: Int = 0 - var onAllBatchUpdatesFinished: (() -> Void)? - - var inputContainerBottomConstraint: NSLayoutConstraint! - private func addInputBarContainer() { - self.inputBarContainer = UIView(frame: CGRect.zero) - self.inputBarContainer.autoresizingMask = UIView.AutoresizingMask() - self.inputBarContainer.translatesAutoresizingMaskIntoConstraints = false - self.inputBarContainer.backgroundColor = .white - self.view.addSubview(self.inputBarContainer) - NSLayoutConstraint.activate([ - self.inputBarContainer.topAnchor.constraint(greaterThanOrEqualTo: self.topLayoutGuide.bottomAnchor) - ]) - let leadingAnchor: NSLayoutXAxisAnchor - let trailingAnchor: NSLayoutXAxisAnchor - if #available(iOS 11.0, *) { - let guide = self.view.safeAreaLayoutGuide - leadingAnchor = guide.leadingAnchor - trailingAnchor = guide.trailingAnchor - } else { - leadingAnchor = self.view.leadingAnchor - trailingAnchor = self.view.trailingAnchor - } - - NSLayoutConstraint.activate([ - self.inputBarContainer.leadingAnchor.constraint(equalTo: leadingAnchor), - self.inputBarContainer.trailingAnchor.constraint(equalTo: trailingAnchor) - ]) - self.inputContainerBottomConstraint = self.view.bottomAnchor.constraint(equalTo: self.inputBarContainer.bottomAnchor) - self.view.addConstraint(self.inputContainerBottomConstraint) - } - - private func addInputView() { - let inputView = self.createChatInputView() - self.inputBarContainer.addSubview(inputView) - NSLayoutConstraint.activate([ - self.inputBarContainer.topAnchor.constraint(equalTo: inputView.topAnchor), - self.inputBarContainer.bottomAnchor.constraint(equalTo: inputView.bottomAnchor), - self.inputBarContainer.leadingAnchor.constraint(equalTo: inputView.leadingAnchor), - self.inputBarContainer.trailingAnchor.constraint(equalTo: inputView.trailingAnchor) - ]) - } - - private func addInputContentContainer() { - self.inputContentContainer = UIView(frame: CGRect.zero) - self.inputContentContainer.autoresizingMask = UIView.AutoresizingMask() - self.inputContentContainer.translatesAutoresizingMaskIntoConstraints = false - self.inputContentContainer.backgroundColor = .white - self.view.addSubview(self.inputContentContainer) - NSLayoutConstraint.activate([ - self.view.bottomAnchor.constraint(equalTo: self.inputContentContainer.bottomAnchor), - self.view.leadingAnchor.constraint(equalTo: self.inputContentContainer.leadingAnchor), - self.view.trailingAnchor.constraint(equalTo: self.inputContentContainer.trailingAnchor), - self.inputContentContainer.topAnchor.constraint(equalTo: self.inputBarContainer.bottomAnchor) - ]) - } - - private func updateInputContainerBottomBaseOffset() { - if #available(iOS 11.0, *) { - let offset = self.bottomLayoutGuide.length - if self.inputContainerBottomBaseOffset != offset { - self.inputContainerBottomBaseOffset = offset - } - } else { - // If we have been pushed on nav controller and hidesBottomBarWhenPushed = true, then ignore bottomLayoutMargin - // because it has incorrect value when we actually have a bottom bar (tabbar) - // Also if instance of BaseChatViewController is added as childViewController to another view controller, we had to check all this stuf on parent instance instead of self - // UPD: Fixed in iOS 11.0 - let navigatedController: UIViewController - if let parent = self.parent, !(parent is UINavigationController || parent is UITabBarController) { - navigatedController = parent - } else { - navigatedController = self - } - - if navigatedController.hidesBottomBarWhenPushed && (navigationController?.viewControllers.count ?? 0) > 1 && navigationController?.viewControllers.last == navigatedController { - self.inputContainerBottomBaseOffset = 0 - } else { - self.inputContainerBottomBaseOffset = self.bottomLayoutGuide.length - } - } - } - - private var inputContainerBottomBaseOffset: CGFloat = 0 { - didSet { self.updateInputContainerBottomConstraint() } - } - - private var inputContainerBottomAdditionalOffset: CGFloat = 0 { - didSet { self.updateInputContainerBottomConstraint() } - } - - private func updateInputContainerBottomConstraint() { - self.inputContainerBottomConstraint.constant = max(self.inputContainerBottomBaseOffset, self.inputContainerBottomAdditionalOffset) - self.view.setNeedsLayout() - } - - var isAdjustingInputContainer: Bool = false - - open func setupKeyboardTracker() { - let heightBlock = { [weak self] (bottomMargin: CGFloat, keyboardStatus: KeyboardStatus) in - guard let sSelf = self else { return } - if let keyboardObservingDelegate = sSelf.keyboardEventsHandler { - keyboardObservingDelegate.onKeyboardStateDidChange(bottomMargin, keyboardStatus) - } else { - sSelf.changeInputContentBottomMarginTo(bottomMargin) - } - } - self.keyboardTracker = KeyboardTracker(viewController: self, inputBarContainer: self.inputBarContainer, heightBlock: heightBlock, notificationCenter: self.notificationCenter) - - (self.view as? BaseChatViewControllerViewProtocol)?.bmaInputAccessoryView = self.keyboardTracker?.trackingView - } - - var notificationCenter = NotificationCenter.default - var keyboardTracker: KeyboardTracker! - - public private(set) var isFirstLayout: Bool = true - override open func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - - self.adjustCollectionViewInsets(shouldUpdateContentOffset: true) - self.keyboardTracker.adjustTrackingViewSizeIfNeeded() - - if self.isFirstLayout { - self.updateQueue.start() - self.isFirstLayout = false - } - - self.updateInputContainerBottomBaseOffset() - } - - public var allContentFits: Bool { - guard let collectionView = self.collectionView else { return false } - let inputHeightWithKeyboard = self.view.bounds.height - self.inputBarContainer.frame.minY - let insetTop = self.topLayoutGuide.length + self.layoutConfiguration.contentInsets.top - let insetBottom = self.layoutConfiguration.contentInsets.bottom + inputHeightWithKeyboard - let availableHeight = collectionView.bounds.height - (insetTop + insetBottom) - let contentSize = collectionView.collectionViewLayout.collectionViewContentSize - return availableHeight >= contentSize.height - } - - private var previousBoundsUsedForInsetsAdjustment: CGRect? - func adjustCollectionViewInsets(shouldUpdateContentOffset: Bool) { - guard let collectionView = self.collectionView else { return } - let isInteracting = collectionView.panGestureRecognizer.numberOfTouches > 0 - let isBouncingAtTop = isInteracting && collectionView.contentOffset.y < -collectionView.contentInset.top - if !self.placeMessagesFromBottom && isBouncingAtTop { return } - - let inputHeightWithKeyboard = self.view.bounds.height - self.inputBarContainer.frame.minY - let newInsetBottom = self.layoutConfiguration.contentInsets.bottom + inputHeightWithKeyboard - let insetBottomDiff = newInsetBottom - collectionView.contentInset.bottom - var newInsetTop = self.topLayoutGuide.length + self.layoutConfiguration.contentInsets.top - let contentSize = collectionView.collectionViewLayout.collectionViewContentSize - - let needToPlaceMessagesAtBottom = self.placeMessagesFromBottom && self.allContentFits - if needToPlaceMessagesAtBottom { - let realContentHeight = contentSize.height + newInsetTop + newInsetBottom - newInsetTop += collectionView.bounds.height - realContentHeight - } - - let insetTopDiff = newInsetTop - collectionView.contentInset.top - let needToUpdateContentInset = self.placeMessagesFromBottom && (insetTopDiff != 0 || insetBottomDiff != 0) - - let prevContentOffsetY = collectionView.contentOffset.y - - let boundsHeightDiff: CGFloat = { - guard shouldUpdateContentOffset, let lastUsedBounds = self.previousBoundsUsedForInsetsAdjustment else { - return 0 - } - let diff = lastUsedBounds.height - collectionView.bounds.height - // When collectionView is scrolled to bottom and height increases, - // collectionView adjusts its contentOffset automatically - let isScrolledToBottom = contentSize.height <= collectionView.bounds.maxY - collectionView.contentInset.bottom - return isScrolledToBottom ? max(0, diff) : diff - }() - self.previousBoundsUsedForInsetsAdjustment = collectionView.bounds - - let newContentOffsetY: CGFloat = { - let minOffset = -newInsetTop - let maxOffset = contentSize.height - (collectionView.bounds.height - newInsetBottom) - let targetOffset = prevContentOffsetY + insetBottomDiff + boundsHeightDiff - return max(min(maxOffset, targetOffset), minOffset) - }() - - collectionView.contentInset = { - var currentInsets = collectionView.contentInset - currentInsets.bottom = newInsetBottom - currentInsets.top = newInsetTop - return currentInsets - }() - - collectionView.chatto_setVerticalScrollIndicatorInsets({ - var currentInsets = collectionView.scrollIndicatorInsets - currentInsets.bottom = self.layoutConfiguration.scrollIndicatorInsets.bottom + inputHeightWithKeyboard - currentInsets.top = self.topLayoutGuide.length + self.layoutConfiguration.scrollIndicatorInsets.top - return currentInsets - }()) - - guard shouldUpdateContentOffset else { return } - - let inputIsAtBottom = self.view.bounds.maxY - self.inputBarContainer.frame.maxY <= 0 - if isInteracting && (needToPlaceMessagesAtBottom || needToUpdateContentInset) { - collectionView.contentOffset.y = prevContentOffsetY - } else if self.allContentFits { - collectionView.contentOffset.y = -collectionView.contentInset.top - } else if !isInteracting || inputIsAtBottom { - collectionView.contentOffset.y = newContentOffsetY - } - } - - func rectAtIndexPath(_ indexPath: IndexPath?) -> CGRect? { - guard let collectionView = self.collectionView else { return nil } - guard let indexPath = indexPath else { return nil } - - return collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath)?.frame - } - - var autoLoadingEnabled: Bool = false - var cellPanGestureHandler: CellPanGestureHandler! - public private(set) var inputBarContainer: UIView! - public private(set) var inputContentContainer: UIView! - public internal(set) var presenterFactory: ChatItemPresenterFactoryProtocol! - let presentersByCell = NSMapTable(keyOptions: .weakMemory, valueOptions: .weakMemory) - var visibleCells: [IndexPath: UICollectionViewCell] = [:] // @see visibleCellsAreValid(changes:) - - public internal(set) var updateQueue: SerialTaskQueueProtocol = SerialTaskQueue() - - /** - - You can use a decorator to: - - Provide the ChatCollectionViewLayout with margins between messages - - Provide to your pressenters additional attributes to help them configure their cells (for instance if a bubble should show a tail) - - You can also add new items (for instance time markers or failed cells) - */ - public var chatItemsDecorator: ChatItemsDecoratorProtocol? - - open func createCollectionViewLayout() -> UICollectionViewLayout { - let layout = ChatCollectionViewLayout() - layout.delegate = self - return layout - } - - var layoutModel = ChatCollectionViewLayoutModel.createModel(0, itemsLayoutData: []) - - // MARK: Subclass overrides - - open func createPresenterFactory() -> ChatItemPresenterFactoryProtocol { - // Default implementation - return ChatItemPresenterFactory(presenterBuildersByType: self.createPresenterBuilders()) - } - - open func createPresenterBuilders() -> [ChatItemType: [ChatItemPresenterBuilderProtocol]] { - assert(false, "Override in subclass") - return [ChatItemType: [ChatItemPresenterBuilderProtocol]]() - } - - open func createChatInputView() -> UIView { - assert(false, "Override in subclass") - return UIView() - } - - /** - When paginating up we need to change the scroll position as the content is pushed down. - We take distance to top from beforeUpdate indexPath and then we make afterUpdate indexPath to appear at the same distance - */ - open func referenceIndexPathsToRestoreScrollPositionOnUpdate(itemsBeforeUpdate: ChatItemCompanionCollection, changes: CollectionChanges) -> (beforeUpdate: IndexPath?, afterUpdate: IndexPath?) { - let firstItemMoved = changes.movedIndexPaths.first - return (firstItemMoved?.indexPathOld as IndexPath?, firstItemMoved?.indexPathNew as IndexPath?) - } - - // MARK: ReplyIndicatorRevealerDelegate - - open func didPassThreshold(at: IndexPath) { - self.replyFeedbackGenerator?.generateFeedback() - } - - open func didFinishReplyGesture(at indexPath: IndexPath) { - let item = self.chatItemCompanionCollection[indexPath.item].chatItem - self.replyActionHandler?.handleReply(for: item) - } - - open func didCancelReplyGesture(at: IndexPath) {} - - public final var cellPanGestureHandlerConfig: CellPanGestureHandlerConfig = .defaultConfig() { - didSet { - self.cellPanGestureHandler?.config = self.cellPanGestureHandlerConfig - } - } - - private static func makeReplyFeedbackGenerator() -> ReplyFeedbackGeneratorProtocol? { - if #available(iOS 10, *) { - return ReplyFeedbackGenerator() - } - return nil - } - - // MARK: ChatDataSourceDelegateProtocol - - open func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol, updateType: UpdateType) { - self.enqueueModelUpdate(updateType: updateType) - } - - open func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol) { - self.enqueueModelUpdate(updateType: .normal) - } - - public var keyboardStatus: KeyboardStatus { - return self.keyboardTracker.keyboardStatus - } - - public var maximumInputSize: CGSize { - return self.view.bounds.size - } - - open var inputContentBottomMargin: CGFloat { - return self.inputContainerBottomConstraint.constant - } - - open func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool = false, callback: (() -> Void)? = nil) { - self.changeInputContentBottomMarginTo(newValue, animated: animated, duration: CATransaction.animationDuration(), callback: callback) - } - - open func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool = false, duration: CFTimeInterval, initialSpringVelocity: CGFloat = 0.0, callback: (() -> Void)? = nil) { - guard self.inputContainerBottomConstraint.constant != newValue else { callback?(); return } - if animated { - self.isAdjustingInputContainer = true - self.inputContainerBottomAdditionalOffset = newValue - CATransaction.begin() - UIView.animate( - withDuration: duration, - delay: 0.0, - usingSpringWithDamping: 1.0, - initialSpringVelocity: initialSpringVelocity, - options: .curveLinear, - animations: { self.view.layoutIfNeeded() }, - completion: { _ in }) - CATransaction.setCompletionBlock(callback) // this callback is guaranteed to be called - CATransaction.commit() - self.isAdjustingInputContainer = false - } else { - self.changeInputContentBottomMarginWithoutAnimationTo(newValue, callback: callback) - } - } - - open func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool = false, duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, callback: (() -> Void)? = nil) { - guard self.inputContainerBottomConstraint.constant != newValue else { callback?(); return } - if animated { - self.isAdjustingInputContainer = true - CATransaction.begin() - CATransaction.setAnimationTimingFunction(timingFunction) - self.inputContainerBottomAdditionalOffset = newValue - UIView.animate( - withDuration: duration, - animations: { self.view.layoutIfNeeded() }, - completion: { _ in } - ) - CATransaction.setCompletionBlock(callback) // this callback is guaranteed to be called - CATransaction.commit() - self.isAdjustingInputContainer = false - } else { - self.changeInputContentBottomMarginWithoutAnimationTo(newValue, callback: callback) - } - } - - private func changeInputContentBottomMarginWithoutAnimationTo(_ newValue: CGFloat, callback: (() -> Void)?) { - self.isAdjustingInputContainer = true - self.inputContainerBottomAdditionalOffset = newValue - self.view.layoutIfNeeded() - callback?() - self.isAdjustingInputContainer = false - } -} - -extension BaseChatViewController { // Rotation - - open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { - super.viewWillTransition(to: size, with: coordinator) - guard isViewLoaded else { return } - guard let collectionView = self.collectionView else { return } - let shouldScrollToBottom = self.isScrolledAtBottom() - let referenceIndexPath = collectionView.indexPathsForVisibleItems.first - let oldRect = self.rectAtIndexPath(referenceIndexPath) - coordinator.animate(alongsideTransition: { (_) -> Void in - if shouldScrollToBottom { - self.scrollToBottom(animated: false) - } else { - let newRect = self.rectAtIndexPath(referenceIndexPath) - self.scrollToPreservePosition(oldRefRect: oldRect, newRefRect: newRect) - } - }, completion: nil) - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift deleted file mode 100644 index 11817c74..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/ChatLayoutConfiguration.swift +++ /dev/null @@ -1,49 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public protocol ChatLayoutConfigurationProtocol { - var contentInsets: UIEdgeInsets { get } - var scrollIndicatorInsets: UIEdgeInsets { get } -} - -public struct ChatLayoutConfiguration: ChatLayoutConfigurationProtocol { - public let contentInsets: UIEdgeInsets - public let scrollIndicatorInsets: UIEdgeInsets - - public init(contentInsets: UIEdgeInsets, scrollIndicatorInsets: UIEdgeInsets) { - self.contentInsets = contentInsets - self.scrollIndicatorInsets = scrollIndicatorInsets - } -} - -extension ChatLayoutConfiguration { - static var defaultConfiguration: ChatLayoutConfiguration { - let contentInsets = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) - let scrollIndicatorInsets = UIEdgeInsets.zero - return ChatLayoutConfiguration(contentInsets: contentInsets, - scrollIndicatorInsets: scrollIndicatorInsets) - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift deleted file mode 100644 index 6afe97f2..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift +++ /dev/null @@ -1,45 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -// If you wish to use your custom view instead of BaseChatViewControllerView, you must implement this protocol. -public protocol BaseChatViewControllerViewProtocol: class { - var bmaInputAccessoryView: UIView? { get set } -} - -// http://stackoverflow.com/questions/24596031/uiviewcontroller-with-inputaccessoryview-is-not-deallocated -final class BaseChatViewControllerView: UIView, BaseChatViewControllerViewProtocol { - - var bmaInputAccessoryView: UIView? - - override var inputAccessoryView: UIView? { - get { - return self.bmaInputAccessoryView - } - set { - self.bmaInputAccessoryView = newValue - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift deleted file mode 100644 index 6c07f7e3..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift +++ /dev/null @@ -1,200 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public protocol CellRevealing { - var allowRevealing: Bool { get } -} - -public protocol AccessoryViewRevealable: CellRevealing { - func revealAccessoryView(withOffset offset: CGFloat, animated: Bool) - func preferredOffsetToRevealAccessoryView() -> CGFloat? // This allows to sync size in case cells have different sizes for the accessory view. Nil -> no restriction -} - -public protocol ReplyIndicatorRevealable: CellRevealing { - func canShowReply() -> Bool - func revealReplyIndicator(withOffset offset: CGFloat, animated: Bool) -> Bool -} - -public protocol ReplyIndicatorRevealerDelegate: AnyObject { - func didPassThreshold(at: IndexPath) - func didFinishReplyGesture(at: IndexPath) - func didCancelReplyGesture(at: IndexPath) -} - -public struct CellPanGestureHandlerConfig { - public let angleThresholdInRads: CGFloat - public let threshold: CGFloat - public let accessoryViewTranslationMultiplier: CGFloat - public let replyIndicatorTranslationMultiplier: CGFloat - public var allowReplyRevealing: Bool = false - public var allowTimestampRevealing: Bool = true - - public static func defaultConfig() -> CellPanGestureHandlerConfig { - .init( - angleThresholdInRads: 0.0872665, // ~5 degrees - threshold: 30, - accessoryViewTranslationMultiplier: 1/2, - replyIndicatorTranslationMultiplier: 2/3 - ) - } - - func transformAccessoryViewTranslation(_ translation: CGFloat) -> CGFloat { - (translation - self.threshold) * self.accessoryViewTranslationMultiplier - } - - func transformReplyIndicatorTranslation(_ translation: CGFloat) -> CGFloat { - (translation - self.threshold) * self.replyIndicatorTranslationMultiplier - } -} - -final class CellPanGestureHandler: NSObject, UIGestureRecognizerDelegate { - - private let panRecognizer: UIPanGestureRecognizer = UIPanGestureRecognizer() - private let collectionView: UICollectionView - - init(collectionView: UICollectionView) { - self.collectionView = collectionView - super.init() - self.collectionView.addGestureRecognizer(self.panRecognizer) - self.panRecognizer.addTarget(self, action: #selector(CellPanGestureHandler.handlePan(_:))) - self.panRecognizer.delegate = self - } - - deinit { - self.panRecognizer.delegate = nil - self.collectionView.removeGestureRecognizer(self.panRecognizer) - } - - var isEnabled: Bool = true { - didSet { - self.panRecognizer.isEnabled = self.isEnabled - } - } - - var config = CellPanGestureHandlerConfig.defaultConfig() - - public weak var replyDelegate: ReplyIndicatorRevealerDelegate? - - @objc - private func handlePan(_ panRecognizer: UIPanGestureRecognizer) { - switch panRecognizer.state { - case .began: - break - case .changed: - let translation = panRecognizer.translation(in: self.collectionView) - if translation.x < 0 { - guard self.config.allowTimestampRevealing else { return } - self.revealAccessoryView(atOffset: self.config.transformAccessoryViewTranslation(-translation.x)) - } else { - guard let indexPath = self.collectionView.indexPathForItem(at: panRecognizer.location(in: self.collectionView)), - let cell = self.collectionView.cellForItem(at: indexPath) as? ReplyIndicatorRevealable, - cell.allowRevealing, - self.config.allowReplyRevealing, - cell.canShowReply() else { return } - - if self.replyIndexPath == nil, translation.x > self.config.threshold { - self.replyIndexPath = indexPath - self.collectionView.isScrollEnabled = false - } - self.revealReplyIndicator(atOffset: self.config.transformReplyIndicatorTranslation(translation.x)) - } - case .ended: - self.revealAccessoryView(atOffset: 0) - self.finishRevealingReply() - - case .failed, .cancelled: - self.revealAccessoryView(atOffset: 0) - self.cancelRevealingReply() - default: - break - } - } - - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { - return true - } - - func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - if gestureRecognizer != self.panRecognizer { - return true - } - - let translation = self.panRecognizer.translation(in: self.collectionView) - let x = abs(translation.x), y = abs(translation.y) - let angleRads = atan2(y, x) - return angleRads <= self.config.angleThresholdInRads - } - - private func revealAccessoryView(atOffset offset: CGFloat) { - // Find max offset (cells can have slighlty different timestamp size ( 3.00 am vs 11.37 pm ) - let cells: [AccessoryViewRevealable] = self.collectionView.visibleCells.compactMap({ $0 as? AccessoryViewRevealable }) - let offset = min(offset, cells.reduce(0) { (current, cell) -> CGFloat in - return max(current, cell.preferredOffsetToRevealAccessoryView() ?? 0) - }) - - for cell in self.collectionView.visibleCells { - if let cell = cell as? AccessoryViewRevealable, cell.allowRevealing { - cell.revealAccessoryView(withOffset: offset, animated: offset == 0) - } - } - } - - private var replyIndexPath: IndexPath? - private var overReplyThreshold = false - - private func revealReplyIndicator(atOffset offset: CGFloat) { - guard let indexPath = self.replyIndexPath, - let cell = self.collectionView.cellForItem(at: indexPath) as? ReplyIndicatorRevealable else { return } - let maxOffsetReached = cell.revealReplyIndicator(withOffset: offset, animated: offset == 0) - if maxOffsetReached != overReplyThreshold { - self.replyDelegate?.didPassThreshold(at: indexPath) - self.overReplyThreshold = maxOffsetReached - } - } - - private func finishRevealingReply() { - defer { self.cleanUpRevealingReply() } - guard let indexPath = self.replyIndexPath else { return } - if self.overReplyThreshold { - self.replyDelegate?.didFinishReplyGesture(at: indexPath) - } else { - self.replyDelegate?.didCancelReplyGesture(at: indexPath) - } - } - - private func cancelRevealingReply() { - defer { self.cleanUpRevealingReply() } - guard let indexPath = self.replyIndexPath else { return } - self.replyDelegate?.didCancelReplyGesture(at: indexPath) - } - - private func cleanUpRevealingReply() { - self.overReplyThreshold = false - self.revealReplyIndicator(atOffset: 0) - self.collectionView.isScrollEnabled = true - self.replyIndexPath = nil - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift deleted file mode 100644 index 45535684..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift +++ /dev/null @@ -1,170 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public protocol ChatCollectionViewLayoutDelegate: class { - func chatCollectionViewLayoutModel() -> ChatCollectionViewLayoutModel -} - -public struct ChatCollectionViewLayoutModel { - let contentSize: CGSize - let layoutAttributes: [UICollectionViewLayoutAttributes] - let layoutAttributesBySectionAndItem: [[UICollectionViewLayoutAttributes]] - let calculatedForWidth: CGFloat - - public static func createModel(_ collectionViewWidth: CGFloat, itemsLayoutData: [(height: CGFloat, bottomMargin: CGFloat)]) -> ChatCollectionViewLayoutModel { - var layoutAttributes = [UICollectionViewLayoutAttributes]() - var layoutAttributesBySectionAndItem = [[UICollectionViewLayoutAttributes]]() - layoutAttributesBySectionAndItem.append([UICollectionViewLayoutAttributes]()) - - var verticalOffset: CGFloat = 0 - for (index, layoutData) in itemsLayoutData.enumerated() { - let indexPath = IndexPath(item: index, section: 0) - let (height, bottomMargin) = layoutData - let itemSize = CGSize(width: collectionViewWidth, height: height) - let frame = CGRect(origin: CGPoint(x: 0, y: verticalOffset), size: itemSize) - let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) - attributes.frame = frame - layoutAttributes.append(attributes) - layoutAttributesBySectionAndItem[0].append(attributes) - verticalOffset += itemSize.height - verticalOffset += bottomMargin - } - - return ChatCollectionViewLayoutModel( - contentSize: CGSize(width: collectionViewWidth, height: verticalOffset), - layoutAttributes: layoutAttributes, - layoutAttributesBySectionAndItem: layoutAttributesBySectionAndItem, - calculatedForWidth: collectionViewWidth - ) - } - - public static func createEmptyModel() -> ChatCollectionViewLayoutModel { - return ChatCollectionViewLayoutModel( - contentSize: .zero, - layoutAttributes: [], - layoutAttributesBySectionAndItem: [], - calculatedForWidth: 0 - ) - } -} - -open class ChatCollectionViewLayout: UICollectionViewLayout { - var layoutModel: ChatCollectionViewLayoutModel! - public weak var delegate: ChatCollectionViewLayoutDelegate? - - // Optimization: after reloadData we'll get invalidateLayout, but prepareLayout will be delayed until next run loop. - // Client may need to force prepareLayout after reloadData, but we don't want to compute layout again in the next run loop. - private var layoutNeedsUpdate = true - open override func invalidateLayout() { - super.invalidateLayout() - self.layoutNeedsUpdate = true - } - - open override func prepare() { - super.prepare() - guard self.layoutNeedsUpdate else { return } - guard let delegate = self.delegate else { - self.layoutModel = ChatCollectionViewLayoutModel.createEmptyModel() - return - } - var oldLayoutModel = self.layoutModel - self.layoutModel = delegate.chatCollectionViewLayoutModel() - self.layoutNeedsUpdate = false - DispatchQueue.global(qos: .default).async { () -> Void in - // Dealloc of layout with 5000 items take 25 ms on tests on iPhone 4s - // This moves dealloc out of main thread - if oldLayoutModel != nil { - // Use nil check above to remove compiler warning: Variable 'oldLayoutModel' was written to, but never read - oldLayoutModel = nil - } - } - } - - open override var collectionViewContentSize: CGSize { - return self.layoutModel?.contentSize ?? .zero - } - - open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { - var attributesArray = [UICollectionViewLayoutAttributes]() - - // Find any cell that sits within the query rect. - guard let firstMatchIndex = self.layoutModel.layoutAttributes.binarySearch(predicate: { attribute in - if attribute.frame.intersects(rect) { - return .orderedSame - } - if attribute.frame.minY > rect.maxY { - return .orderedDescending - } - return .orderedAscending - }) else { return attributesArray } - - // Starting from the match, loop up and down through the array until all the attributes - // have been added within the query rect. - for attributes in self.layoutModel.layoutAttributes[..= rect.minY else { break } - attributesArray.append(attributes) - } - - for attributes in self.layoutModel.layoutAttributes[firstMatchIndex...] { - guard attributes.frame.minY <= rect.maxY else { break } - attributesArray.append(attributes) - } - - return attributesArray - } - - open override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { - if indexPath.section < self.layoutModel.layoutAttributesBySectionAndItem.count && indexPath.item < self.layoutModel.layoutAttributesBySectionAndItem[indexPath.section].count { - return self.layoutModel.layoutAttributesBySectionAndItem[indexPath.section][indexPath.item] - } - assert(false, "Unexpected indexPath requested:\(indexPath)") - return nil - } - - open override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { - return self.layoutModel.calculatedForWidth != newBounds.width - } -} - -private extension Array { - - func binarySearch(predicate: (Element) -> ComparisonResult) -> Index? { - var lowerBound = startIndex - var upperBound = endIndex - - while lowerBound < upperBound { - let midIndex = lowerBound + (upperBound - lowerBound) / 2 - if predicate(self[midIndex]) == .orderedSame { - return midIndex - } else if predicate(self[midIndex]) == .orderedAscending { - lowerBound = midIndex + 1 - } else { - upperBound = midIndex - } - } - return nil - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift deleted file mode 100644 index 74e4a2b4..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift +++ /dev/null @@ -1,49 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 UpdateType: CaseIterable { - case normal - case firstLoad - case pagination - case reload - case messageCountReduction -} - -public protocol ChatDataSourceDelegateProtocol: class { - func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol) - func chatDataSourceDidUpdate(_ chatDataSource: ChatDataSourceProtocol, updateType: UpdateType) -} - -public protocol ChatDataSourceProtocol: class { - var hasMoreNext: Bool { get } - var hasMorePrevious: Bool { get } - var chatItems: [ChatItemProtocol] { get } - var delegate: ChatDataSourceDelegateProtocol? { get set } - - func loadNext() // Should trigger chatDataSourceDidUpdate with UpdateType.Pagination - func loadPrevious() // Should trigger chatDataSourceDidUpdate with UpdateType.Pagination - func adjustNumberOfMessages(preferredMaxCount: Int?, focusPosition: Double, completion:(_ didAdjust: Bool) -> Void) // If you want, implement message count contention for performance, otherwise just call completion(false) -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift deleted file mode 100644 index ca288fbe..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift +++ /dev/null @@ -1,54 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public protocol ChatItemPresenterFactoryProtocol { - func createChatItemPresenter(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol - func configure(withCollectionView collectionView: UICollectionView) -} - -final class ChatItemPresenterFactory: ChatItemPresenterFactoryProtocol { - var presenterBuildersByType = [ChatItemType: [ChatItemPresenterBuilderProtocol]]() - - init(presenterBuildersByType: [ChatItemType: [ChatItemPresenterBuilderProtocol]]) { - self.presenterBuildersByType = presenterBuildersByType - } - - func createChatItemPresenter(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol { - for builder in self.presenterBuildersByType[chatItem.type] ?? [] { - if builder.canHandleChatItem(chatItem) { - return builder.createPresenterWithChatItem(chatItem) - } - } - return DummyChatItemPresenter() - } - - func configure(withCollectionView collectionView: UICollectionView) { - for presenterBuilder in self.presenterBuildersByType.flatMap({ $0.1 }) { - presenterBuilder.presenterType.registerCells(collectionView) - } - DummyChatItemPresenter.registerCells(collectionView) - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift deleted file mode 100644 index 30ee6c41..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/CollectionChanges.swift +++ /dev/null @@ -1,123 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 protocol UniqueIdentificable { - var uid: String { get } -} - -public struct CollectionChangeMove: Equatable, Hashable { - public let indexPathOld: IndexPath - public let indexPathNew: IndexPath - public init(indexPathOld: IndexPath, indexPathNew: IndexPath) { - self.indexPathOld = indexPathOld - self.indexPathNew = indexPathNew - } -} - -public func == (lhs: CollectionChangeMove, rhs: CollectionChangeMove) -> Bool { - return lhs.indexPathOld == rhs.indexPathOld && lhs.indexPathNew == rhs.indexPathNew -} - -public struct CollectionChanges { - public let insertedIndexPaths: Set - public let deletedIndexPaths: Set - public let movedIndexPaths: [CollectionChangeMove] - - init(insertedIndexPaths: Set, deletedIndexPaths: Set, movedIndexPaths: [CollectionChangeMove]) { - self.insertedIndexPaths = insertedIndexPaths - self.deletedIndexPaths = deletedIndexPaths - self.movedIndexPaths = movedIndexPaths - } -} - -func generateChanges(oldCollection: [T], newCollection: [T]) -> CollectionChanges { - func indexed(_ values: [T]) -> [T: Int] { - var map: [T: Int] = .init(minimumCapacity: values.count) - for (index, value) in values.enumerated() { - map[value] = index - } - return map - } - - let oldIndexedCollection = indexed(oldCollection) - let newIndexedCollection = indexed(newCollection) - var deletedIndexPaths = Set() - var insertedIndexPaths = Set() - var movedIndexPaths = [CollectionChangeMove]() - - // Deletetions - for element in oldCollection { - let isDeleted = newIndexedCollection[element] == nil - if isDeleted { - deletedIndexPaths.insert(IndexPath(item: oldIndexedCollection[element]!, section: 0)) - } - } - - // Insertions and movements - for element in newCollection { - let newIndex = newIndexedCollection[element]! - let newIndexPath = IndexPath(item: newIndex, section: 0) - if let oldIndex = oldIndexedCollection[element] { - if oldIndex != newIndex { - let move = CollectionChangeMove( - indexPathOld: IndexPath(item: oldIndex, section: 0), - indexPathNew: newIndexPath - ) - movedIndexPaths.append(move) - } - } else { - // It's new - insertedIndexPaths.insert(newIndexPath) - } - } - - return CollectionChanges(insertedIndexPaths: insertedIndexPaths, - deletedIndexPaths: deletedIndexPaths, - movedIndexPaths: movedIndexPaths) -} - -func updated(collection: [IndexPath: T], withChanges changes: CollectionChanges) -> [IndexPath: T] { - var result = collection - - changes.deletedIndexPaths.forEach { (indexPath) in - result[indexPath] = nil - } - - var movedDestinations = Set() - changes.movedIndexPaths.forEach { (move) in - result[move.indexPathNew] = collection[move.indexPathOld] - movedDestinations.insert(move.indexPathNew) - if !movedDestinations.contains(move.indexPathOld) { - result[move.indexPathOld] = nil - } - } - - changes.insertedIndexPaths.forEach { (indexPath) in - result[indexPath] = nil - } - - return result -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift deleted file mode 100644 index 85c0628a..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift +++ /dev/null @@ -1,277 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public enum KeyboardStatus { - case hiding - case hidden - case showing - case shown -} - -public typealias KeyboardHeightBlock = (_ height: CGFloat, _ status: KeyboardStatus) -> Void - -class KeyboardTracker { - private(set) var keyboardStatus: KeyboardStatus = .hidden - private let view: UIView - var trackingView: UIView { - return self.keyboardTrackerView - } - private lazy var keyboardTrackerView: KeyboardTrackingView = { - let trackingView = KeyboardTrackingView() - trackingView.positionChangedCallback = { [weak self] in - guard let sSelf = self else { return } - if !sSelf.isPerformingForcedLayout { - sSelf.layoutInputAtTrackingViewIfNeeded() - } - } - return trackingView - }() - - var isTracking = false - var inputBarContainer: UIView - private var notificationCenter: NotificationCenter - - private var heightBlock: KeyboardHeightBlock - - init(viewController: UIViewController, inputBarContainer: UIView, heightBlock: @escaping KeyboardHeightBlock, notificationCenter: NotificationCenter) { - self.view = viewController.view - self.heightBlock = heightBlock - self.inputBarContainer = inputBarContainer - self.notificationCenter = notificationCenter - self.notificationCenter.addObserver( - self, - selector: #selector(KeyboardTracker.keyboardWillShow(_:)), - name: UIResponder.keyboardWillShowNotification, - object: nil - ) - self.notificationCenter.addObserver( - self, - selector: #selector(KeyboardTracker.keyboardDidShow(_:)), - name: UIResponder.keyboardDidShowNotification, - object: nil - ) - self.notificationCenter.addObserver( - self, - selector: #selector(KeyboardTracker.keyboardWillHide(_:)), - name: UIResponder.keyboardWillHideNotification, - object: nil - ) - self.notificationCenter.addObserver( - self, - selector: #selector(KeyboardTracker.keyboardDidHide(_:)), - name: UIResponder.keyboardDidHideNotification, - object: nil - ) - self.notificationCenter.addObserver( - self, - selector: #selector(KeyboardTracker.keyboardWillChangeFrame(_:)), - name: UIResponder.keyboardWillChangeFrameNotification, - object: nil - ) - } - - deinit { - self.notificationCenter.removeObserver(self) - } - - func startTracking() { - self.isTracking = true - } - - func stopTracking() { - self.isTracking = false - } - - @objc - private func keyboardWillShow(_ notification: Notification) { - guard self.isTracking else { return } - guard !self.isPerformingForcedLayout else { return } - let bottomConstraint = self.bottomConstraintFromNotification(notification) - guard bottomConstraint > 0 else { return } // Some keyboards may report initial willShow/DidShow notifications with invalid positions - self.keyboardStatus = .showing - self.layoutInputContainer(withBottomConstraint: bottomConstraint) - } - - @objc - private func keyboardDidShow(_ notification: Notification) { - guard self.isTracking else { return } - guard !self.isPerformingForcedLayout else { return } - - let bottomConstraint = self.bottomConstraintFromNotification(notification) - guard bottomConstraint > 0 else { return } // Some keyboards may report initial willShow/DidShow notifications with invalid positions - self.keyboardStatus = .shown - self.layoutInputContainer(withBottomConstraint: bottomConstraint) - self.adjustTrackingViewSizeIfNeeded() - } - - @objc - private func keyboardWillChangeFrame(_ notification: Notification) { - guard self.isTracking else { return } - let bottomConstraint = self.bottomConstraintFromNotification(notification) - if bottomConstraint == 0 { - self.keyboardStatus = .hiding - self.layoutInputAtBottom() - } - } - - @objc - private func keyboardWillHide(_ notification: Notification) { - guard self.isTracking else { return } - self.keyboardStatus = .hiding - self.layoutInputAtBottom() - } - - @objc - private func keyboardDidHide(_ notification: Notification) { - guard self.isTracking else { return } - self.keyboardStatus = .hidden - self.layoutInputAtBottom() - } - - private func bottomConstraintFromNotification(_ notification: Notification) -> CGFloat { - guard let rect = ((notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return 0 } - guard rect.height > 0 else { return 0 } - let rectInView = self.view.convert(rect, from: nil) - guard rectInView.maxY >=~ self.view.bounds.height else { return 0 } // Undocked keyboard - return max(0, self.view.bounds.height - rectInView.minY - self.keyboardTrackerView.intrinsicContentSize.height) - } - - private func bottomConstraintFromTrackingView() -> CGFloat { - guard self.keyboardTrackerView.superview != nil else { return 0 } - let trackingViewRect = self.view.convert(self.keyboardTrackerView.bounds, from: self.keyboardTrackerView) - return max(0, self.view.bounds.height - trackingViewRect.maxY) - } - - func adjustTrackingViewSizeIfNeeded() { - guard self.isTracking && self.keyboardStatus == .shown else { return } - self.adjustTrackingViewSize() - } - - private func adjustTrackingViewSize() { - let inputContainerHeight = self.inputBarContainer.bounds.height - if self.keyboardTrackerView.preferredSize.height != inputContainerHeight { - self.keyboardTrackerView.preferredSize.height = inputContainerHeight - self.isPerformingForcedLayout = true - - // Sometimes, the autolayout system doesn't finish the layout inside of the input bar container at this point. - // If it happens, then the input bar may have a height different than an input bar container. - // We need to ensure that their heights are the same; otherwise, it would lead to incorrect calculations that in turn affects lastKnownKeyboardHeight. - // Tracking view adjustment changes a keyboard height and triggers an update of lastKnownKeyboardHeight. - self.inputBarContainer.layoutIfNeeded() - self.keyboardTrackerView.window?.layoutIfNeeded() - - self.isPerformingForcedLayout = false - } - } - - private func layoutInputAtBottom() { - self.keyboardTrackerView.bounds.size.height = 0 - self.layoutInputContainer(withBottomConstraint: 0) - } - - var isPerformingForcedLayout: Bool = false - func layoutInputAtTrackingViewIfNeeded() { - guard self.isTracking && self.keyboardStatus == .shown else { return } - self.layoutInputContainer(withBottomConstraint: self.bottomConstraintFromTrackingView()) - } - - private func layoutInputContainer(withBottomConstraint constraint: CGFloat) { - self.isPerformingForcedLayout = true - self.heightBlock(constraint, self.keyboardStatus) - self.isPerformingForcedLayout = false - } -} - -private class KeyboardTrackingView: UIView { - - var positionChangedCallback: (() -> Void)? - var observedView: UIView? - - deinit { - if let observedView = self.observedView { - observedView.removeObserver(self, forKeyPath: "frame") - } - } - - override init(frame: CGRect) { - super.init(frame: frame) - self.commonInit() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - self.commonInit() - } - - func commonInit() { - self.autoresizingMask = .flexibleHeight - self.isUserInteractionEnabled = false - self.backgroundColor = UIColor.clear - self.isHidden = true - } - - var preferredSize: CGSize = .zero { - didSet { - if oldValue != self.preferredSize { - self.invalidateIntrinsicContentSize() - self.window?.setNeedsLayout() - } - } - } - - override var intrinsicContentSize: CGSize { - return self.preferredSize - } - - override func didMoveToSuperview() { - if let observedView = self.observedView { - observedView.removeObserver(self, forKeyPath: "center") - self.observedView = nil - } - - if let newSuperview = self.superview { - newSuperview.addObserver(self, forKeyPath: "center", options: [.new, .old], context: nil) - self.observedView = newSuperview - } - - super.didMoveToSuperview() - } - - override func observeValue(forKeyPath keyPath: String?, - of object: Any?, - change: [NSKeyValueChangeKey: Any]?, - context: UnsafeMutableRawPointer?) { - guard let object = object as? UIView, let superview = self.superview else { return } - if object === superview { - guard let sChange = change else { return } - let oldCenter = (sChange[NSKeyValueChangeKey.oldKey] as! NSValue).cgPointValue - let newCenter = (sChange[NSKeyValueChangeKey.newKey] as! NSValue).cgPointValue - if oldCenter != newCenter { - self.positionChangedCallback?() - } - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift deleted file mode 100644 index 73b09336..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// The MIT License (MIT) -// -// Copyright (c) 2015-present Badoo Trading Limited. -// -// 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 - -public protocol ReplyFeedbackGeneratorProtocol { - func generateFeedback() -} - -@available(iOS 10, *) -struct ReplyFeedbackGenerator: ReplyFeedbackGeneratorProtocol { - - private let impactFeedbackGenerator = UIImpactFeedbackGenerator() - - func generateFeedback() { - self.impactFeedbackGenerator.impactOccurred() - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift deleted file mode 100644 index 6d01868e..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatController/InputPositionControlling.swift +++ /dev/null @@ -1,39 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 - -public protocol InputPositionControlling: AnyObject { - - var keyboardStatus: KeyboardStatus { get } - - var inputBarContainer: UIView! { get } - var maximumInputSize: CGSize { get } - - var inputContentContainer: UIView! { get } - var inputContentBottomMargin: CGFloat { get } - - func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool, callback: (() -> Void)?) - func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool, duration: CFTimeInterval, initialSpringVelocity: CGFloat, callback: (() -> Void)?) - func changeInputContentBottomMarginTo(_ newValue: CGFloat, animated: Bool, duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, callback: (() -> Void)?) -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift deleted file mode 100644 index 623f6b19..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/ChatItemCompanionCollection.swift +++ /dev/null @@ -1,79 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 ChatItemCompanionCollection: Collection { - - private let items: [ChatItemCompanion] - private let itemIndexesById: [String: Int] // Maping to the position in the array instead the item itself for better performance - - public init(items: [ChatItemCompanion]) { - var dictionary = [String: Int](minimumCapacity: items.count) - for (index, item) in items.enumerated() { - dictionary[item.uid] = index - dictionary[item.chatItem.uid] = index - } - self.items = items - self.itemIndexesById = dictionary - } - - public func indexOf(_ uid: String) -> Int? { - return self.itemIndexesById[uid] - } - - public subscript(index: Int) -> ChatItemCompanion { - return self.items[index] - } - - public subscript(uid: String) -> ChatItemCompanion? { - if let index = self.indexOf(uid) { - return self.items[index] - } - return nil - } - - public func makeIterator() -> IndexingIterator<[ChatItemCompanion]> { - return self.items.makeIterator() - } - - public func index(_ i: Int, offsetBy n: Int) -> Int { - return self.items.index(i, offsetBy: n) - } - - public func index(_ i: Int, offsetBy n: Int, limitedBy limit: Int) -> Int? { - return self.items.index(i, offsetBy: n, limitedBy: limit) - } - - public func index(after i: Int) -> Int { - return self.items.index(after: i) - } - - public var startIndex: Int { - return 0 - } - - public var endIndex: Int { - return self.items.count - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift deleted file mode 100644 index d9f728c9..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/SerialTaskQueue.swift +++ /dev/null @@ -1,80 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 typealias TaskClosure = (_ completion: @escaping () -> Void) -> Void - -public protocol SerialTaskQueueProtocol { - func addTask(_ task: @escaping TaskClosure) - func start() - func stop() - func flushQueue() - var isEmpty: Bool { get } - var isStopped: Bool { get } -} - -public final class SerialTaskQueue: SerialTaskQueueProtocol { - public private(set) var isBusy = false - public private(set) var isStopped = true - - private var tasksQueue = [TaskClosure]() - - public init() {} - - public func addTask(_ task: @escaping TaskClosure) { - self.tasksQueue.append(task) - self.maybeExecuteNextTask() - } - - public func start() { - self.isStopped = false - self.maybeExecuteNextTask() - } - - public func stop() { - self.isStopped = true - } - - public func flushQueue() { - self.tasksQueue.removeAll() - } - - public var isEmpty: Bool { - return self.tasksQueue.isEmpty - } - - private func maybeExecuteNextTask() { - if !self.isStopped && !self.isBusy { - if !self.isEmpty { - let firstTask = self.tasksQueue.removeFirst() - self.isBusy = true - firstTask({ [weak self] () -> Void in - self?.isBusy = false - self?.maybeExecuteNextTask() - }) - } - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift b/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift deleted file mode 100644 index e0bdaec7..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/Chatto/Source/Utils.swift +++ /dev/null @@ -1,69 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2015-present Badoo Trading Limited. - - 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 let scale = UIScreen.main.scale - -infix operator >=~ -func >=~ (lhs: CGFloat, rhs: CGFloat) -> Bool { - return round(lhs * scale) >= round(rhs * scale) -} - -extension UIScrollView { - func chatto_setContentInsetAdjustment(enabled: Bool, in viewController: UIViewController) { - #if swift(>=3.2) - if #available(iOS 11.0, *) { - self.contentInsetAdjustmentBehavior = enabled ? .always : .never - } else { - viewController.automaticallyAdjustsScrollViewInsets = enabled - } - #else - viewController.automaticallyAdjustsScrollViewInsets = enabled - #endif - } - - func chatto_setAutomaticallyAdjustsScrollIndicatorInsets(_ adjusts: Bool) { - if #available(iOS 13.0, *) { - self.automaticallyAdjustsScrollIndicatorInsets = adjusts - } - } - - func chatto_setVerticalScrollIndicatorInsets(_ insets: UIEdgeInsets) { - if #available(iOS 11.1, *) { - self.verticalScrollIndicatorInsets = insets - } else { - self.scrollIndicatorInsets = insets - } - } -} - -extension UICollectionView { - func chatto_setIsPrefetchingEnabled(_ isPrefetchingEnabled: Bool) { - if #available(iOS 10.0, *) { - self.isPrefetchingEnabled = isPrefetchingEnabled - } - } -} diff --git a/test/fixtures/cocoapods/Pods/Chatto/LICENSE b/test/fixtures/cocoapods/Pods/Chatto/LICENSE deleted file mode 100644 index 2bef5f8e..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Badoo Development - -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. - diff --git a/test/fixtures/cocoapods/Pods/Chatto/README.md b/test/fixtures/cocoapods/Pods/Chatto/README.md deleted file mode 100644 index 11178631..00000000 --- a/test/fixtures/cocoapods/Pods/Chatto/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Chatto [![Build Status](https://travis-ci.org/badoo/Chatto.svg?branch=master)](https://travis-ci.org/badoo/Chatto) [![codecov.io](https://codecov.io/github/badoo/Chatto/coverage.svg?branch=master)](https://codecov.io/github/badoo/Chatto?branch=master) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Chatto.svg)](https://img.shields.io/cocoapods/v/Chatto.svg) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) - - -`Chatto` is a Swift lightweight framework to build chat applications. It's been designed to be extensible and performant. Along with `Chatto` there is `ChattoAdditions`, a companion framework which includes cells for messages and an extensible input component. You can find more details about how it was implemented in our [blog](https://techblog.badoo.com/blog/2015/12/04/how-we-made-chatto/). See them in action! -
- - - - -
- -## Features -- Calculation of collection view changes and layout in background -- Supports pagination in both directions and autoloading -- Message count contention for fast pagination and rotation with thousands of messsages -- Accessory view revealing by swiping from right -- Interactive keyboard dismissal -- Text bubbles -- Photo bubbles -- Extensible input bar - -## How to use - -Check the [wiki!](https://github.com/badoo/Chatto/wiki) - -## How to contribute - -If you **just have a question**, please reach us in [our gitter room](https://gitter.im/chatto-framework/community) - -If you'd like to file a bug report, suggest changes or submit a pull request, please [check our contribution guide](.github/CONTRIBUTING.md) - -## How to install -### CocoaPods - -1. Make sure `use_frameworks!` is added to your `Podfile`. - -2. Include the following in your `Podfile`: - ``` - # Swift 5 - pod 'Chatto', '= 4.1.0' - pod 'ChattoAdditions', '= 4.1.0' # if you want to use the cells or the input component - ``` - ``` - # Swift 4.2 - pod 'Chatto', '= 3.4.0' - pod 'ChattoAdditions', '= 3.4.0' # if you want to use the cells or the input component - ``` - ``` - # Swift 4 - pod 'Chatto', '= 3.3.1' - pod 'ChattoAdditions', '= 3.3.1' # if you want to use the cells or the input component - ``` - ``` - # Swift 3 - pod 'Chatto', '= 3.2.0' - pod 'ChattoAdditions', '= 3.2.0' # if you want to use the cells or the input component - ``` - ``` - # Swift 2.x - pod 'Chatto', '= 2.1.0' - pod 'ChattoAdditions', '= 2.1.0' # if you want to use the cells or the input component - ``` -If you like living on the bleeding edge, you can use the `master` branch with: - ``` - pod 'Chatto', :git => 'https://github.com/badoo/Chatto', :branch => 'master' - pod 'ChattoAdditions', :git => 'https://github.com/badoo/Chatto', :branch => 'master' - ``` -3. Run `pod install` - -### Carthage - -If you’re using [Carthage](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos), simply add Chatto to your Cartfile: -``` -# Swift 5 -github "badoo/Chatto" -``` -``` -# Swift 2.x -github "badoo/Chatto" "swift-2" -``` - -### Manually - -1. Clone, add as a submodule or [download.](https://github.com/badoo/Chatto/archive/master.zip) -2. Drag and drop `Chatto` and/or `ChattoAdditions` project to your workspace -3. Add `Chatto` and/or `ChattoAdditions` to Embedded binaries - -## License -Source code is distributed under MIT license. - -## Blog -Read more on our [tech blog](https://medium.com/bumble-tech) or explore our other [open source projects](https://github.com/badoo) diff --git a/test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json b/test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json deleted file mode 100644 index 2c8800f2..00000000 --- a/test/fixtures/cocoapods/Pods/Local Podspecs/Chatto.podspec.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Chatto", - "version": "4.1.0", - "summary": "Chat framework in Swift", - "description": "Lightweight chat framework to build Chat apps", - "homepage": "https://github.com/badoo/Chatto", - "license": { - "type": "MIT" - }, - "platforms": { - "ios": "9.0" - }, - "authors": { - "Diego Sanchez": "diego.sanchezr@gmail.com", - "Anton Schukin": "a.p.schukin@gmail.com" - }, - "source": { - "git": "https://github.com/badoo/Chatto.git", - "tag": "4.1.0" - }, - "source_files": "Chatto/Source/**/*.{h,m,swift}", - "public_header_files": "Chatto/Source/**/*.h", - "requires_arc": true, - "swift_versions": "5.3", - "swift_version": "5.3" -} diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE b/test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/README.md b/test/fixtures/cocoapods/Pods/MDFInternationalization/README.md deleted file mode 100644 index ec8f118d..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# MDFInternationalization - -MDFInternationalization assists in internationalizing your iOS app or components' user interface. - -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/material-foundation/material-internationalization-ios/blob/develop/LICENSE) -[![GitHub release](https://img.shields.io/github/release/material-foundation/material-internationalization-ios.svg)](https://github.com/material-foundation/material-internationalization-ios/releases) -[![Build Status](https://travis-ci.org/material-foundation/material-internationalization-ios.svg?branch=stable)](https://travis-ci.org/material-foundation/material-internationalization-ios) -[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/MDFInternationalization.svg)](https://img.shields.io/cocoapods/v/MDFInternationalization.svg) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) - -## Right-to-Left calculations for CGRects and UIEdgeInsets - -A UIView is positioned within its superview in terms of a frame (CGRect) consisting of an -origin and a size. When a device is set to a language that is written from Right-to-Left (RTL), -we often want to mirror the interface around the vertical axis. This library contains -functions to assist in modifying frames and edge insets for RTL. - -``` obj-c -// To flip a subview's frame horizontally, pass in subview.frame and the width of its parent. -CGRect originalFrame = childView.frame; -CGRect flippedFrame = MDFRectFlippedHorizontally(originalFrame, CGRectGetWidth(self.bounds)); -childView.frame = flippedFrame; -``` - -## Mirroring Images - -A category on UIImage backports iOS 10's `[UIImage imageWithHorizontallyFlippedOrientation]` to -earlier versions of iOS. - -``` obj-c -// To mirror on image, invoke mdf_imageWithHorizontallyFlippedOrientation. -UIImage *mirroredImage = [originalImage mdf_imageWithHorizontallyFlippedOrientation]; -``` - -## Adding semantic context - -A category on UIView backports iOS 9's `-[UIView semanticContentAttribute]` and iOS 10's -`-[UIView effectiveUserInterfaceLayoutDirection]` to earlier versions of iOS. - -``` obj-c -// To set a semantic content attribute, set the mdf_semanticContentAttribute property. -lockedLTRView.mdf_semanticContentAttribute = UISemanticContentAttributeForceLeftToRight; - -// mdf_semanticContentAttribute is used to calculate the mdf_effectiveUserInterfaceLayoutDirection -if (customView.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { - // Update customView's layout to be in RTL mode. -} -``` - -## Embedding Bi-directional strings - -A category on NSString offers a simple API to wrap strings in Unicode markers so that LTR -and RTL text can co-exist in the same string. - -``` obj-c -// To embed an RTL string in an existing LTR string we should wrap it in Unicode directionality -// markers to maintain preoper rendering. - -// The name of a restaurant is in Arabic (RTL), but the rest of string is in Latin (LTR). -NSString *wrappedRestaurantName = - [restaurantName mdf_stringWithStereoReset:NSLocaleLanguageDirectionRightToLeft - context:NSLocaleLanguageDirectionLeftToRight]; - -NSString *reservationString = [NSString stringWithFormat:@"%@ : %ld", wrappedRestaurantName, attendees]; -``` - -## Usage - -See Examples/Flags for a detailed example of how to use the functionality provided by this library. - - -## License - -MDFInternationalization is licensed under the [Apache License Version 2.0](LICENSE). diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h deleted file mode 100644 index f9ab12ef..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFInternationalization.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#import "MDFRTL.h" // IWYU pragma: export -#import "NSLocale+MaterialRTL.h" // IWYU pragma: export -#import "NSString+MaterialBidi.h" // IWYU pragma: export -#import "UIImage+MaterialRTL.h" // IWYU pragma: export -#import "UIView+MaterialRTL.h" // IWYU pragma: export - -//! Project version number for MDFInternationalization. -FOUNDATION_EXPORT double MDFInternationalizationVersionNumber; - -//! Project version string for MDFInternationalization. -FOUNDATION_EXPORT const unsigned char MDFInternationalizationVersionString[]; diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h deleted file mode 100644 index 595dc949..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -/** - Leading autoresizing mask based on layoutDirection. 'Leading' is 'Left' in - UIUserInterfaceLayoutDirectionLeftToRight, 'Right' otherwise. - - @param layoutDirection The layout direction to consider when computing the autoresizing mask. - @return The leading margin part of an autoresizing mask. - */ -FOUNDATION_EXPORT UIViewAutoresizing MDFLeadingMarginAutoresizingMaskForLayoutDirection( - UIUserInterfaceLayoutDirection layoutDirection); - -/** - Trailing autoresizing masks based on layoutDirection. 'Trailing' is 'Right' in - UIUserInterfaceLayoutDirectionLeftToRight, 'Left' otherwise. - - @param layoutDirection The layout direction to consider to compute the autoresizing mask. - @return The trailing margin part of an autoresizing mask. - */ -FOUNDATION_EXPORT UIViewAutoresizing MDFTrailingMarginAutoresizingMaskForLayoutDirection( - UIUserInterfaceLayoutDirection layoutDirection); - -/** - The frame to use when actually laying out a view in its superview. - - A view is conceptually positioned within its superview in terms of leading/trailing. When it's time - to actually lay out (i.e. setting frames), you position the frame as you usually would, but if you - are in the opposite layout direction you call this function to return a rect that has been flipped - around the vertical axis. - - @note Example: Flipping the frame of a subview 50pts wide at 10pts from the leading edge of a - bounding view. - - CGRect frame = CGRectMake(10, originY, 50, height); - CGFloat containerWidth = CGRectGetWidth(self.bounds); - if (layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { - frame = MDFRectFlippedHorizontally(frame, containerWidth); - } - - In LTR, frame is { { 10, originY }, { 50, height } } in a 100 wide bounding view. - +----------------------------------------100----------------------------------------+ - | | - | 10 +--------------------50--------------------+ | - | | | | - | +------------------------------------------+ | - | | - +----------------------------------------100----------------------------------------+ - - In RTL, frame is { { 40, originY }, { 50, height } }. - +----------------------------------------100----------------------------------------+ - | | - | 40 +--------------------50--------------------+ | - | | | | - | +------------------------------------------+ | - | | - +----------------------------------------100----------------------------------------+ - - @param frame The frame to convert. - @param containerWidth The superview's bounds's width. - @return The frame mirrored around the vertical axis. - */ -FOUNDATION_EXPORT CGRect MDFRectFlippedHorizontally(CGRect frame, CGFloat containerWidth); - - -/** - Creates a UIEdgeInsets instance with its left and right values exchanged. - - @param insets The insets we are intending to flip horizontally. - @return Insets with the right and left values exchanged. - */ -FOUNDATION_EXPORT UIEdgeInsets MDFInsetsFlippedHorizontally(UIEdgeInsets insets); - -/** - Creates a UIEdgeInsets instance from the parameters while obeying layoutDirection. - - If layoutDirection is UIUserInterfaceLayoutDirectionLeftToRight, then the left inset is leading and - the right inset is trailing, otherwise they are reversed. - - @param top The top inset. - @param leading The leading inset. - @param bottom The bottom inset. - @param trailing The trailing inset. - @return Insets in terms of left/right, already internationalized based on the layout direction. - */ -FOUNDATION_EXPORT UIEdgeInsets MDFInsetsMakeWithLayoutDirection(CGFloat top, - CGFloat leading, - CGFloat bottom, - CGFloat trailing, - UIUserInterfaceLayoutDirection layoutDirection); diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m deleted file mode 100644 index e5df8824..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/MDFRTL.m +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MDFRTL.h" - -UIViewAutoresizing MDFLeadingMarginAutoresizingMaskForLayoutDirection( - UIUserInterfaceLayoutDirection layoutDirection) { - switch (layoutDirection) { - case UIUserInterfaceLayoutDirectionLeftToRight: - return UIViewAutoresizingFlexibleLeftMargin; - case UIUserInterfaceLayoutDirectionRightToLeft: - return UIViewAutoresizingFlexibleRightMargin; - } - NSCAssert(NO, @"Invalid enumeration value %i.", (int)layoutDirection); - return UIViewAutoresizingFlexibleLeftMargin; -} - -UIViewAutoresizing MDFTrailingMarginAutoresizingMaskForLayoutDirection( - UIUserInterfaceLayoutDirection layoutDirection) { - switch (layoutDirection) { - case UIUserInterfaceLayoutDirectionLeftToRight: - return UIViewAutoresizingFlexibleRightMargin; - case UIUserInterfaceLayoutDirectionRightToLeft: - return UIViewAutoresizingFlexibleLeftMargin; - } - NSCAssert(NO, @"Invalid enumeration value %i.", (int)layoutDirection); - return UIViewAutoresizingFlexibleRightMargin; -} - -CGRect MDFRectFlippedHorizontally(CGRect frame, CGFloat containerWidth) { - CGRect flippedRect = CGRectStandardize(frame); - CGFloat leadingInset = CGRectGetMinX(flippedRect); - CGFloat width = CGRectGetWidth(flippedRect); - flippedRect.origin.x = containerWidth - leadingInset - width; - - return flippedRect; -} - -UIEdgeInsets MDFInsetsFlippedHorizontally(UIEdgeInsets insets) { - UIEdgeInsets flippedInsets = insets; - flippedInsets.left = insets.right; - flippedInsets.right = insets.left; - - return flippedInsets; -} - -UIEdgeInsets MDFInsetsMakeWithLayoutDirection(CGFloat top, - CGFloat leading, - CGFloat bottom, - CGFloat trailing, - UIUserInterfaceLayoutDirection layoutDirection) { - switch (layoutDirection) { - case UIUserInterfaceLayoutDirectionLeftToRight: - return UIEdgeInsetsMake(top, leading, bottom, trailing); - case UIUserInterfaceLayoutDirectionRightToLeft: - return UIEdgeInsetsMake(top, trailing, bottom, leading); - } - NSCAssert(NO, @"Invalid enumeration value %i.", (int)layoutDirection); - return UIEdgeInsetsMake(top, leading, bottom, trailing); -} diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h deleted file mode 100644 index f73c95ab..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright 2018-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -@interface NSLocale (MaterialRTL) - -/** - Is the direction of the current locale's default language Left-To-Right? - - @return YES if the language is LTR, NO if the language is any other direction. - */ -+ (BOOL)mdf_isDefaultLanguageLTR; - -/** - Is the direction of the current locale's default language Right-To-Left? - - @return YES if the language is RTL, NO if the language is any other direction. - */ -+ (BOOL)mdf_isDefaultLanguageRTL; - -@end - diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m deleted file mode 100644 index 21256d2d..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSLocale+MaterialRTL.m +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright 2018-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "NSLocale+MaterialRTL.h" - -@implementation NSLocale (MaterialRTL) - -+ (BOOL)mdf_isDefaultLanguageLTR { - NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; - NSLocaleLanguageDirection characterDirection = - [NSLocale characterDirectionForLanguage:languageCode]; - BOOL localeLanguageDirectionIsLTR = (characterDirection == NSLocaleLanguageDirectionLeftToRight); - return localeLanguageDirectionIsLTR; -} - -+ (BOOL)mdf_isDefaultLanguageRTL { - NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; - NSLocaleLanguageDirection characterDirection = - [NSLocale characterDirectionForLanguage:languageCode]; - BOOL localeLanguageDirectionIsRTL = (characterDirection == NSLocaleLanguageDirectionRightToLeft); - return localeLanguageDirectionIsRTL; -} - -@end - diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h deleted file mode 100644 index 5aeec86a..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright 2018-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -@interface NSString (MaterialBidi) - -/** - Uses CFStringTokenizerCopyBestStringLanguage to determine string's language direction. - If the language direction is unknown or vertical returns left-to-right. - - As CFStringTokenizerCopyBestStringLanguage is Apple's API, its result may change if - Apple improves or modifies the implementation. - - @return the direction of the string - */ -- (NSLocaleLanguageDirection)mdf_calculatedLanguageDirection; - -/** - Initializes a copy of the string tagged with the given language direction. This - formatting adds the appropriate Unicode embedding characters at the beginning and end of the - string. - - Only NSLocaleLanguageDirectionLeftToRight and NSLocaleLanguageDirectionRightToLeft - language directions are supported. Other values of NSLocalLanguageDirection will - return a copy of self. - - Returns a string wrapped with Unicode bidi formatting characters by inserting these characters - around the string: - RLE+|string|+PDF for RTL text, or LRE+|string|+PDF for LTR text. - - @returns the new string. - */ -- (nonnull NSString *)mdf_stringWithBidiEmbedding:(NSLocaleLanguageDirection)languageDirection; - -/** - Returns a copy of the string explicitly tagged with a language direction. - - Uses mdf_calculatedLanguageDirection to determine string's language direction then invokes - mdf_stringWithBidiEmbedding:. - - @return the new string. - */ -- (nonnull NSString *)mdf_stringWithBidiEmbedding; - -/** - This method will wrap the string in embedding (LRE/RLE and PDF) characters, based on the string - direction and additionally wrapping the string in marks (LRM and RLM) if the string's direction - is different from the context direction. - - |direction| can be NSLocaleLanguageDirectionLeftToRight, NSLocaleLanguageDirectionRightToLeft, or - NSLocaleLanguageDirectionUnknown. If NSLocaleLanguageDirectionUnknown, the direction of the string - will be calculated with mdf_calculatedLanguageDirection. - - |contextDirection| must be specified and cannot be unknown. Only - NSLocaleLanguageDirectionLeftToRight and NSLocaleLanguageDirectionRightToLeft language directions - are supported. - - @returns the new string. - */ -- (nonnull NSString *)mdf_stringWithStereoReset:(NSLocaleLanguageDirection)direction - context:(NSLocaleLanguageDirection)contextDirection; - -/** - Returns a new string in which all occurrences of Unicode bidirectional format markers are removed. - - @returns the new string. - */ -- (nonnull NSString *)mdf_stringWithBidiMarkersStripped; - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m deleted file mode 100644 index 239baa97..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/NSString+MaterialBidi.m +++ /dev/null @@ -1,165 +0,0 @@ -/* - Copyright 2018-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "NSString+MaterialBidi.h" - -#import - -@implementation NSString (MaterialBidi) - -// https://www.w3.org/International/questions/qa-bidi-unicode-controls -//TODO: Reach out to AAA about the utility of the Isolate markers -// ??? Do we want Embedding or Isolate markers? w3 recommends isolate? -// Add reference : Unicode® Standard Annex #9 UNICODE BIDIRECTIONAL ALGORITHM -// go/android-bidiformatter -// http://unicode.org/reports/tr9/ - -// Mark influences the directionality of neutral characters when the context is opposite of the -// neutral chatacter's desired directionality. -static NSString *kMDFLTRMark = @"\u200e"; // left-to-right mark -static NSString *kMDFRTLMark = @"\u200f"; // right-to-left mark - -// Embedding indicates a text segment is embedded in a larger context with the opposite -// directionality. -static NSString *kMDFLTREmbedding = @"\u202a"; // left-to-right embedding -static NSString *kMDFRTLEmbedding = @"\u202b"; // right-to-left embedding - -// Override reverses the directionality of strongly LTR or RTL characters -static NSString *kMDFLTROverride = @"\u202d"; // left-to-right override -static NSString *kMDFRTLOverride = @"\u202e"; // right-to-left override - -// Pop is used to denote the end of an embedding or override text segment -static NSString *kMDFPopFormatting = @"\u202c"; // pop directional formatting - -// Version 6.3.0 Bidi algorithm additions -// The following only work on iOS 10+ - -// Isolate indicates that the text segment has an internal directionality with no effect on -// surrounding characters. -static NSString *kMDFLTRIsolate = @"\u2066"; // left-to-right isolate -static NSString *kMDFRTLIsolate = @"\u2067"; // right-to-left isolate -static NSString *kMDFFirstStrongIsolate = @"\u2068"; // first strong isolate - -// Pop Isolate is used to denote the end of an isolate text segment -static NSString *kMDFPopIsolate = @"\u2069"; // pop directional isolate - - -- (NSLocaleLanguageDirection)mdf_calculatedLanguageDirection { - // Attempt to determine language of string. - NSLocaleLanguageDirection languageDirection = NSLocaleLanguageDirectionUnknown; - - // Pass string into CoreFoundation's language identifier - CFStringRef text = (__bridge CFStringRef)self; - CFRange range = CFRangeMake(0, (CFIndex)[self length]); - NSString *languageCode = - (NSString *)CFBridgingRelease(CFStringTokenizerCopyBestStringLanguage(text, range)); - if (languageCode) { - // If we identified a language, explicitly set the string direction based on that - languageDirection = [NSLocale characterDirectionForLanguage:languageCode]; - } - - // If the result is not LTR or RTL, fallback to LTR - // ??? Should I be defaulting to NSLocale.NSLocaleLanguageCode.characterDiretion? - if (languageDirection != NSLocaleLanguageDirectionLeftToRight && - languageDirection != NSLocaleLanguageDirectionRightToLeft) { - languageDirection = NSLocaleLanguageDirectionLeftToRight; - } - - return languageDirection; -} - -- (NSString *)mdf_stringWithBidiEmbedding { - NSLocaleLanguageDirection languageDirection = [self mdf_calculatedLanguageDirection]; - - return [self mdf_stringWithBidiEmbedding:languageDirection]; -} - -- (NSString *)mdf_stringWithBidiEmbedding:(NSLocaleLanguageDirection)languageDirection { - if (languageDirection == NSLocaleLanguageDirectionRightToLeft) { - return [NSString stringWithFormat:@"%@%@%@", kMDFRTLEmbedding, self, kMDFPopFormatting]; - } else if (languageDirection == NSLocaleLanguageDirectionLeftToRight) { - return [NSString stringWithFormat:@"%@%@%@", kMDFLTREmbedding, self, kMDFPopFormatting]; - } else { - // Return a copy original string if an unsupported direction is passed in. - return [self copy]; - } -} - -- (nonnull NSString *)mdf_stringWithStereoReset:(NSLocaleLanguageDirection)direction - context:(NSLocaleLanguageDirection)contextDirection { -#if DEBUG - // Disable in release, as a pre-caution in case not everyone defines NS_BLOCK_ASSERTION. - NSCAssert((contextDirection != NSLocaleLanguageDirectionLeftToRight || - contextDirection != NSLocaleLanguageDirectionRightToLeft), - @"contextStringDirection must be passed in and set to either" - "NSLocaleLanguageDirectionLeftToRight or NSLocaleLanguageDirectionRightToLeft."); - - NSCAssert((direction != NSLocaleLanguageDirectionLeftToRight || - direction != NSLocaleLanguageDirectionRightToLeft || - direction != NSLocaleLanguageDirectionUnknown), - @"stringToBeInsertedDirection must be set to either NSLocaleLanguageDirectionUnknown," - "NSLocaleLanguageDirectionLeftToRight, or NSLocaleLanguageDirectionRightToLeft."); -#endif - - if (self.length == 0) { - return [self copy]; - } - - if (direction != NSLocaleLanguageDirectionLeftToRight && - direction != NSLocaleLanguageDirectionRightToLeft) { - direction = [self mdf_calculatedLanguageDirection]; - } - - NSString *bidiEmbeddedString = [self mdf_stringWithBidiEmbedding:direction]; - - NSString *bidiResetString; - if (direction != contextDirection) { - if (contextDirection == NSLocaleLanguageDirectionRightToLeft) { - bidiResetString = - [NSString stringWithFormat:@"%@%@%@", kMDFRTLMark, bidiEmbeddedString, kMDFRTLMark]; - } else { - bidiResetString = - [NSString stringWithFormat:@"%@%@%@", kMDFLTRMark, bidiEmbeddedString, kMDFLTRMark]; - } - } else { - bidiResetString = bidiEmbeddedString; - } - - return bidiResetString; -} - -- (NSString *)mdf_stringWithBidiMarkersStripped { - NSString *strippedString = self; - NSArray *directionalMarkers = @[ kMDFLTRMark, - kMDFRTLMark, - kMDFRTLEmbedding, - kMDFLTREmbedding, - kMDFRTLOverride, - kMDFLTROverride, - kMDFPopFormatting, - kMDFLTRIsolate, - kMDFRTLIsolate, - kMDFFirstStrongIsolate, - kMDFPopIsolate - ]; - for (NSString *markerString in directionalMarkers) { - strippedString = - [strippedString stringByReplacingOccurrencesOfString:markerString withString:@""]; - } - return strippedString; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h deleted file mode 100644 index 36a1e171..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -/** - Backporting of iOS 10's [UIImage imageWithHorizontallyFlippedOrientation]. - */ - -@interface UIImage (MaterialRTL) - -/** - On iOS 10 and above, calls [UIImage imageWithHorizontallyFlippedOrientation]. - Otherwise manually flips the image and returns the result. - - @return A horizontally mirrored version of this image. - */ -- (nonnull UIImage *)mdf_imageWithHorizontallyFlippedOrientation; - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m deleted file mode 100644 index b75d7343..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIImage+MaterialRTL.m +++ /dev/null @@ -1,168 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "UIImage+MaterialRTL.h" - -#import -#import - -/** Returns the horizontally flipped version of the given UIImageOrientation. */ -static UIImageOrientation MDFRTLMirroredOrientation(UIImageOrientation sourceOrientation) { - switch (sourceOrientation) { - case UIImageOrientationUp: - return UIImageOrientationUpMirrored; - case UIImageOrientationDown: - return UIImageOrientationDownMirrored; - case UIImageOrientationLeft: - return UIImageOrientationLeftMirrored; - case UIImageOrientationRight: - return UIImageOrientationRightMirrored; - case UIImageOrientationUpMirrored: - return UIImageOrientationUp; - case UIImageOrientationDownMirrored: - return UIImageOrientationDown; - case UIImageOrientationLeftMirrored: - return UIImageOrientationLeft; - case UIImageOrientationRightMirrored: - return UIImageOrientationRight; - } - NSCAssert(NO, @"Invalid enumeration value %i.", (int)sourceOrientation); - return UIImageOrientationUpMirrored; -} - -/** - Returns a copy of the image actually flipped. The orientation and scale are consumed, while the - rendering mode is ported to the new image. - */ -static UIImage *MDFRTLFlippedImage(UIImage *image) { - CGSize size = image.size; - CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height); - - UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale); - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetShouldAntialias(context, true); - CGContextSetInterpolationQuality(context, kCGInterpolationHigh); - - // Note: UIKit's and CoreGraphics coordinates systems are flipped vertically (UIKit's Y axis goes - // down, while CoreGraphics' goes up). - switch (image.imageOrientation) { - case UIImageOrientationUp: - CGContextScaleCTM(context, -1, -1); - CGContextTranslateCTM(context, -rect.size.width, -rect.size.height); - break; - case UIImageOrientationDown: - // Orientation down is equivalent to a 180º rotation. The difference in coordinates systems is - // thus sufficient and nothing needs to be down to flip the image. - break; - case UIImageOrientationLeft: - CGContextRotateCTM(context, -(CGFloat)M_PI_2); - CGContextTranslateCTM(context, -rect.size.width, 0); - break; - case UIImageOrientationRight: - CGContextRotateCTM(context, (CGFloat)M_PI_2); - CGContextTranslateCTM(context, 0, -rect.size.width); - break; - case UIImageOrientationUpMirrored: - CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -rect.size.height); - break; - case UIImageOrientationDownMirrored: - CGContextScaleCTM(context, -1, 1); - CGContextTranslateCTM(context, -rect.size.width, 0); - break; - case UIImageOrientationLeftMirrored: - CGContextRotateCTM(context, -(CGFloat)M_PI_2); - CGContextTranslateCTM(context, -rect.size.width, 0); - CGContextScaleCTM(context, -1, 1); - CGContextTranslateCTM(context, -rect.size.width, 0); - break; - case UIImageOrientationRightMirrored: - CGContextRotateCTM(context, (CGFloat)M_PI_2); - CGContextTranslateCTM(context, 0, -rect.size.width); - CGContextScaleCTM(context, -1, 1); - CGContextTranslateCTM(context, -rect.size.width, 0); - break; - default: - NSCAssert(NO, @"Invalid enumeration value %i.", (int)image.imageOrientation); - } - - // If the UIImage is not backed by a CGImage, create one from the CIImage - if (image.CGImage) { - CGContextDrawImage(context, rect, image.CGImage); - } else if (image.CIImage) { - CIImage *coreImage = image.CIImage; - CIContext *coreImageContext = [CIContext context]; - CGImageRef coreGraphicsImage = - [coreImageContext createCGImage:coreImage fromRect:coreImage.extent]; - if (coreGraphicsImage) { - CGContextDrawImage(context, rect, coreGraphicsImage); - CFRelease(coreGraphicsImage); - coreGraphicsImage = NULL; - } - } else { - NSCAssert(NO, @"Unable to flip image without a CGImage or CIImage backing store"); - } - - UIImage *drawnImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - // Port the rendering mode. - UIImage *flippedImage = [drawnImage imageWithRenderingMode:image.renderingMode]; - return flippedImage; -} - -@implementation UIImage (MaterialRTL) - -- (UIImage *)mdf_imageWithHorizontallyFlippedOrientation { - // On iOS 10 and above, UIImage supports the imageWithHorizontallyFlippedOrientation method. - // Otherwise, we manually manipulate the image. - if ([self respondsToSelector:@selector(imageWithHorizontallyFlippedOrientation)]) { - //TODO: (#22) Replace with @availability when we adopt Xcode 9 as our minimum supported version. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - return [self imageWithHorizontallyFlippedOrientation]; -#pragma clang diagnostic pop - } else { - UIImage *mirroredImage; - UIImageOrientation mirroredOrientation = MDFRTLMirroredOrientation(self.imageOrientation); - if (self.CGImage) { - CGImageRef _Nonnull image = (CGImageRef _Nonnull)self.CGImage; - mirroredImage = [[self class] imageWithCGImage:image - scale:self.scale - orientation:mirroredOrientation]; - } else if (self.CIImage) { - CIImage * _Nonnull image = (CIImage * _Nonnull)self.CIImage; - mirroredImage = [[self class] imageWithCIImage:image - scale:self.scale - orientation:mirroredOrientation]; - } - - // If we were unsuccessful, manually flip the image using a Core Graphics context - if (!mirroredImage) { - mirroredImage = MDFRTLFlippedImage(self); - } - - // On iOS9- [UIImage imageWithCGImage:scale:orientation:] loses the rendering mode. - // Restore it if the new renderingMode does not match the current renderingMode. - if (mirroredImage.renderingMode != self.renderingMode) { - mirroredImage = [mirroredImage imageWithRenderingMode:self.renderingMode]; - } - - return mirroredImage; - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h deleted file mode 100644 index fbf11555..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -/** - Complete backporting of iOS 9's `-[UIView semanticContentAttribute]` and - `+[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:]`, and iOS 10's - `-[UIView effectiveUserInterfaceLayoutDirection]` and - `+[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:relativeToLayoutDirection:]`. - */ - - - -@interface UIView (MaterialRTL) - -// UISemanticContentAttribute was added in iOS SDK 9.0 but is available on devices running earlier -// version of iOS. We ignore the partial-availability warning that gets thrown on our use of this -// symbol. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - -/** - A semantic description of the view's contents, used to determine whether the view should be flipped - when switching between left-to-right and right-to-left layouts. - - @note Default: - - iOS 8 and below: UISemanticContentAttributeUnspecified. - - iOS 9 and above: same as -[UIView semanticContentAttribute] - */ -@property(nonatomic, setter=mdf_setSemanticContentAttribute:) - UISemanticContentAttribute mdf_semanticContentAttribute; - -/** - The user interface layout direction appropriate for arranging the immediate content of this view. - - Always consult the mdf_effectiveUserInterfaceLayoutDirection of the view whose immediate content is - being arranged or drawn. Do not assume that the value propagates through the view's subtree. - - @note - - iOS 9 and below: same as +[UIView mdf_userInterfaceLayoutDirectionForSemanticContentAttribute:] - - iOS 10 and above: same as -[UIView effectiveUserInterfaceLayoutDirection] - */ -@property(nonatomic, readonly) - UIUserInterfaceLayoutDirection mdf_effectiveUserInterfaceLayoutDirection; - -/** - Returns the layout direction implied by the provided semantic content attribute relative to the - application-wide layout direction (as returned by - UIApplication.sharedApplication.userInterfaceLayoutDirection). However, if it's being called from - an iOS 8 extension, it will return left-to-right every time. - - @param semanticContentAttribute The semantic content attribute. - @return The layout direction. - */ -+ (UIUserInterfaceLayoutDirection)mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: - (UISemanticContentAttribute)semanticContentAttribute; - -/** - Returns the layout direction implied by the provided semantic content attribute relative to the - provided layout direction. For example, when provided a layout direction of - RightToLeft and a semantic content attribute of Playback, this method returns LeftToRight. Layout - and drawing code can use this method to determine how to arrange elements, but might find it easier - to query the container view's mdf_effectiveUserInterfaceLayoutDirection property instead. - - @param semanticContentAttribute The semantic content attribute. - @param layoutDirection The layout direction to consider. - @return The implied layout direction. - */ -+ (UIUserInterfaceLayoutDirection) - mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: - (UISemanticContentAttribute)semanticContentAttribute - relativeToLayoutDirection: - (UIUserInterfaceLayoutDirection)layoutDirection; - -#pragma clang diagnostic pop - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m b/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m deleted file mode 100644 index 95126f78..00000000 --- a/test/fixtures/cocoapods/Pods/MDFInternationalization/Sources/UIView+MaterialRTL.m +++ /dev/null @@ -1,167 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "UIView+MaterialRTL.h" - -#import - -#define MDF_BASE_SDK_EQUAL_OR_ABOVE(x) \ - (defined(__IPHONE_##x) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_##x)) - -// UISemanticContentAttribute was added in iOS SDK 9.0 but is available on devices running earlier -// version of iOS. We ignore the partial-availability warning that gets thrown on our use of this -// symbol. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - -static inline UIUserInterfaceLayoutDirection - MDFUserInterfaceLayoutDirectionForSemanticContentAttributeRelativeToLayoutDirection( - UISemanticContentAttribute semanticContentAttribute, - UIUserInterfaceLayoutDirection userInterfaceLayoutDirection) { - switch (semanticContentAttribute) { - case UISemanticContentAttributeUnspecified: - return userInterfaceLayoutDirection; - case UISemanticContentAttributePlayback: - case UISemanticContentAttributeSpatial: - case UISemanticContentAttributeForceLeftToRight: - return UIUserInterfaceLayoutDirectionLeftToRight; - case UISemanticContentAttributeForceRightToLeft: - return UIUserInterfaceLayoutDirectionRightToLeft; - } - NSCAssert(NO, @"Invalid enumeration value %i.", (int)semanticContentAttribute); - return userInterfaceLayoutDirection; -} - -@interface UIView (MaterialRTLPrivate) - -// On iOS 9 and above, mdf_semanticContentAttribute is backed by UIKit's semanticContentAttribute. -// On iOS 8 and below, mdf_semanticContentAttribute is backed by an associated object. -@property(nonatomic, setter=mdf_setAssociatedSemanticContentAttribute:) - UISemanticContentAttribute mdf_associatedSemanticContentAttribute; - -@end - -@implementation UIView (MaterialRTL) - -- (UISemanticContentAttribute)mdf_semanticContentAttribute { -#if MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) - if ([self respondsToSelector:@selector(semanticContentAttribute)]) { - return self.semanticContentAttribute; - } else -#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) - { - return self.mdf_associatedSemanticContentAttribute; - } -} - -- (void)mdf_setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute { -#if MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) - if ([self respondsToSelector:@selector(semanticContentAttribute)]) { - self.semanticContentAttribute = semanticContentAttribute; - } else -#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) - { - self.mdf_associatedSemanticContentAttribute = semanticContentAttribute; - } - - // Invalidate the layout. - [self setNeedsLayout]; -} - -- (UIUserInterfaceLayoutDirection)mdf_effectiveUserInterfaceLayoutDirection { -#if MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) - if ([self respondsToSelector:@selector(effectiveUserInterfaceLayoutDirection)]) { - return self.effectiveUserInterfaceLayoutDirection; - } else { - return [UIView mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: - self.mdf_semanticContentAttribute]; - } -#else - return [UIView mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: - self.mdf_semanticContentAttribute]; -#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) -} - -+ (UIUserInterfaceLayoutDirection)mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: - (UISemanticContentAttribute)attribute { -#if MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) - if ([self - respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute:)]) { - return [self userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; - } else -#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(9_0) - { - // If we are running in the context of an app, we query [UIApplication sharedApplication]. - // Otherwise use a default of Left-to-Right. - UIUserInterfaceLayoutDirection applicationLayoutDirection = - UIUserInterfaceLayoutDirectionLeftToRight; - NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; - // Can I use kAppBundleIdentifier ? - if ([bundlePath hasSuffix:@".app"]) { - // We can't call sharedApplication directly or an error gets thrown for app extensions. - UIApplication *application = - [[UIApplication class] performSelector:@selector(sharedApplication)]; - applicationLayoutDirection = application.userInterfaceLayoutDirection; - } - return [self - mdf_userInterfaceLayoutDirectionForSemanticContentAttribute:attribute - relativeToLayoutDirection:applicationLayoutDirection]; - } -} - -+ (UIUserInterfaceLayoutDirection) - mdf_userInterfaceLayoutDirectionForSemanticContentAttribute: - (UISemanticContentAttribute)semanticContentAttribute - relativeToLayoutDirection: - (UIUserInterfaceLayoutDirection)layoutDirection { -#if MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) - if ([self - respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute: - relativeToLayoutDirection:)]) { - return [self userInterfaceLayoutDirectionForSemanticContentAttribute:semanticContentAttribute - relativeToLayoutDirection:layoutDirection]; - } else { - return MDFUserInterfaceLayoutDirectionForSemanticContentAttributeRelativeToLayoutDirection( - semanticContentAttribute, layoutDirection); - } -#else - return MDFUserInterfaceLayoutDirectionForSemanticContentAttributeRelativeToLayoutDirection( - semanticContentAttribute, layoutDirection); -#endif // MDF_BASE_SDK_EQUAL_OR_ABOVE(10_0) -} - -@end - -@implementation UIView (MaterialRTLPrivate) - -- (UISemanticContentAttribute)mdf_associatedSemanticContentAttribute { - NSNumber *semanticContentAttributeNumber = - objc_getAssociatedObject(self, @selector(mdf_semanticContentAttribute)); - if (semanticContentAttributeNumber != nil) { - return [semanticContentAttributeNumber integerValue]; - } - return UISemanticContentAttributeUnspecified; -} - -- (void)mdf_setAssociatedSemanticContentAttribute: - (UISemanticContentAttribute)semanticContentAttribute { - objc_setAssociatedObject(self, @selector(mdf_semanticContentAttribute), - @(semanticContentAttribute), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -#pragma clang diagnostic pop diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md deleted file mode 100644 index 92102e79..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/README.md +++ /dev/null @@ -1,101 +0,0 @@ -MDFTextAccessibility assists in selecting text colors that will meet the -[W3C standards for accessibility](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html). - -[![Build Status](https://travis-ci.org/material-foundation/material-text-accessibility-ios.svg?branch=develop)](https://travis-ci.org/material-foundation/material-text-accessibility-ios) -[![Code Coverage](http://codecov.io/github/material-foundation/material-text-accessibility-ios/coverage.svg?branch=develop)](http://codecov.io/github/material-foundation/material-text-accessibility-ios?branch=develop) - -## Accessibility of text - -Apps created for the widest range of users should ensure that users can read -text presented over any background. While the legibility of text depends on many -things, a great start is to provide a sufficiently large *contrast ratio* -between foreground text colors and their background colors. The contrast ratio -of two colors is a measurement of how much the brightness of two colors differ. -For example, white on dark grey might have a contrast ratio of 9:1, while white -on medium grey might only have a contrast ratio of 4:1. In general, larger -contrast ratios are better and will ensure the widest range of users can easily -read the text in your app. - -The [W3C](https://www.w3.org)'s -[Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG/#visual-audio-contrast) -contains two recommendations for text contrast ratios: - -1. Minimum contrast: text should have a contrast ratio of at least 4.5:1, - except for "large" text, which can have a contrast ratio of 3:1. -2. Enhanced contrast: text should have a contrast ratio of at least 7:1, except - for large text, which can have a contrast ratio of 4.5:1. - -"Large" text is nominally defined as at least 18pt in a normal font face or at -least 14pt in a bold font face. For more information (including some important -exceptions), see the -[Guidelines](https://www.w3.org/TR/WCAG/#visual-audio-contrast). - -## Computing contrast ratios - -Computing acceptable contrast ratios involves the foreground color, the -background color, the text size, and the transparency of the foreground color, -if any. MDFTextAccessibility provides convenient access to colors that will -automatically give acceptable contrast ratios. - -For methods that return a UIColor, the color along with its alpha is guaranteed -to provide a contrast ratio meeting the minimum ratios recommended by the W3C. -The returned alpha may be greater than the requested alpha to ensure acceptable -contrast ratios, that is, the returned color may be more opaque than requested -to ensure that the text remains legible. - -## Legible text on images - -Displaying text legibly on arbitrary images is difficult because the image -content can conflict with the text. Images with smooth gradients or blurred -regions are likely to result in more legible text; images with many details and -high contrast are less likely to result in legible text. - -MDFTextAccessibility provides methods that attempt to select a good color for -displaying text on a particular image, but the quality of the results will -depend on the contents of the image. If the content of the image is not known -(for example, when images provided by the user), then consider using a -semi-transparent shim between the image and the text to increase contrast. - -## Usage - -### Basic usage - -The simplest usage is to select between black and white text depending on the -background color, irrespective of the font: - -```objective-c -label.textColor = [MDFTextAccessibility textColorOnBackgroundColor:label.backgroundColor - textAlpha:1 - font:nil]; -``` - -Many design standards for user interfaces use text colors that are not fully -opaque. However, transparent text can reduce legibility, so you can request a -color that is as close as possible to a particular alpha value while still being -legible: - -```objective-c -label.textColor = [MDFTextAccessibility textColorOnBackgroundColor:label.backgroundColor - targetTextAlpha:0.85 - font:nil]; -``` - -Since the W3C recommends different contrast ratios for "normal" and "large" -text, including the font can result in a text color closer to your target alpha -when appropriate: - -```objective-c -label.textColor = [MDFTextAccessibility textColorOnBackgroundColor:label.backgroundColor - textAlpha:0.85 - font:label.font]; -``` - -### Advanced usage - -For more advanced usage, such as selecting from a set of colors other than white -and black, see MDFTextAccessibility's -`textColorFromChoices:onBackgroundColor:options:`. - -## License - -MDFTextAccessiblity is licensed under the [Apache License Version 2.0](LICENSE). diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h deleted file mode 100644 index ea88e561..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility-Bridging-Header.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MDFTextAccessibility.h" diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h deleted file mode 100644 index 34a13d1b..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -/** Options for selecting text colors that provide acceptable contrast ratios. */ -typedef NS_OPTIONS(NSUInteger, MDFTextAccessibilityOptions) { - /** No options. */ - MDFTextAccessibilityOptionsNone = 0, - - /** Font size is at least 14pt bold or 18pt normal. */ - MDFTextAccessibilityOptionsLargeFont = 1 << 0, - - /** Do not modify alpha values to find good colors. */ - MDFTextAccessibilityOptionsPreserveAlpha = 1 << 1, - - /** Prefer darker colors to lighter colors. */ - MDFTextAccessibilityOptionsPreferDarker = 1 << 2, - - /** Prefer lighter colors to darker colors. */ - MDFTextAccessibilityOptionsPreferLighter = 1 << 3, - - /** Use enhanced contrast ratios (level AAA) instead of minimum ratios (level AA). */ - MDFTextAccessibilityOptionsEnhancedContrast = 1 << 4, -}; - -/** - MDFTextAccessiblity provides methods to compute accessible text colors. - */ -@interface MDFTextAccessibility : NSObject - -/** - An optionally transparent text color suitable for displaying on a background color with a - particular font. - - The color returned will be white or black with an alpha value of targetTextAlpha, unless the - contrast ratio is insufficient, in which case the alpha is increased (made more opaque). - - If the passed font is nil, then a conservative guess is used. - - @param backgroundColor The opaque background color the text will be displayed on. - @param targetTextAlpha The target alpha of the text. - @param font The font the text will use or nil. - @return A color with acceptable contrast ratio for displaying text on |color|. - */ -+ (nonnull UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor - targetTextAlpha:(CGFloat)targetTextAlpha - font:(nullable UIFont *)font; - -/** - An optionally transparent text color suitable for displaying on a background image with a - particular font. - - The color returned will be white or black with an alpha value of targetTextAlpha, unless the - contrast ratio is insufficient, in which case the alpha is increased (made more opaque). - - If the passed font is nil, then a conservative guess is used. - - The content of the background image is simply averaged to make an average color, which is then used - as if it were the background color of the text. Depending on the contents of the image, this - approximation may or may not result in legible text. - - The supplied image region will be intersected with the image's bounds. If the resulting region is - null or empty then this method returns nil. - - @param backgroundImage The opaque background image the text will be displayed on. - @param region The region in which the text will be displayed. Can be conservatively large. - @param targetTextAlpha The target alpha of the text. - @param font The font to be used to display the text. Can be nil. - @return A color with acceptable contrast ratio, or nil if the region is out of bounds of the image. - */ -+ (nullable UIColor *)textColorOnBackgroundImage:(nonnull UIImage *)backgroundImage - inRegion:(CGRect)region - targetTextAlpha:(CGFloat)targetTextAlpha - font:(nullable UIFont *)font; - -#pragma mark Advanced methods - -/** - An optionally transparent text color suitable for displaying text on a given opaque background - color. - - This method calls textColorFromChoices:onColor:options: with the choices [white, black], both - with their alpha set to targetTextAlpha. - - If MDFTextAccessibilityOptionsPreserveAlpha is included in the options, then the algorithm will not - modify the alpha values, which may result in no color being returned at all. - - @param backgroundColor The opaque background color the text will be displayed on. - @param targetTextAlpha The target alpha of the text. - @param options A combination of MDFTextAccessibilityOptions values. - @return A color with acceptable contrast ratio or nil if no such color exists. - */ -+ (nullable UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor - targetTextAlpha:(CGFloat)targetTextAlpha - options:(MDFTextAccessibilityOptions)options; - -/** - A color selected from a set which is suitable for displaying text on a given opaque background - color. - - Since the minimum ratio for "large" text is less stringent, set - the MDFTextAccessibilityOptionsLargeFont bit if the font size will be greater than or - equal to 18pt normal or 14pt bold. If in doubt or if the text size can vary, be conservative and do - not specify MDFTextAccessibilityOptionsLargeFont in the options. - - By default, the first acceptable color in |choices| will be returned. If - MDFTextAccessibilityOptionsPreferLighter is set, then the lightest acceptable color will be - returned, and if MDFTextAccessibilityOptionsPreferDarker is set, then the darkest acceptable color - will be returned. This allows for a standard set of text colors to be used in different situations. - - By default, the algorithm will attempt to modify the alpha value of colors in |choices| instead - of switching to an alternate color, under the assumption that text with slightly different - alpha values is less noticible than, for example, black text where white text is usually used. - If MDFTextAccessibilityOptionsPreserveAlpha is included in the options, then the algorithm will not - modify the alpha values, which may result in no color being returned at all. - - @param choices An array of text color UIColor objects with optional alpha values. - @param backgroundColor The opaque background color the text will be displayed on. - @param options A combination of MDFTextAccessibilityOptions values. - @return A color with acceptable contrast ratio or nil if no such color exists. - */ -+ (nullable UIColor *)textColorFromChoices:(nonnull NSArray *)choices - onBackgroundColor:(nonnull UIColor *)backgroundColor - options:(MDFTextAccessibilityOptions)options; - -/** - The minimum alpha that text can have and still have an acceptable contrast ratio. Depending on - color combinations, the minimum useable alpha can vary. - - Since the minimum ratio for "large" text is less stringent, set - the MDFTextAccessibilityOptionsLargeFont bit if the font size will be greater than or - equal to 18pt normal or 14pt bold. If in doubt or if the text size can vary, be conservative and do - not specify MDFTextAccessibilityOptionsLargeFont in the options. - - @note There are some color combinations (white on white) for which an acceptable alpha value - doesn't exist. - - @param textColor The text color (alpha is ignored). - @param backgroundColor The opaque background color the text will be displayed on. - @param options A combination of MDFTextAccessibilityOptions values. - @return The minimum alpha value to use in this situation, or -1 if there is no such alpha. - */ -+ (CGFloat)minAlphaOfTextColor:(nonnull UIColor *)textColor - onBackgroundColor:(nonnull UIColor *)backgroundColor - options:(MDFTextAccessibilityOptions)options; - -/** - The contrast ratio of a text color when displayed on an opaque background color. - - @param textColor A text color with optional transparency. - @param backgroundColor The opaque background color the text will be displayed on. - @return The contrast ratio of the text color on the background color. - */ -+ (CGFloat)contrastRatioForTextColor:(nonnull UIColor *)textColor - onBackgroundColor:(nonnull UIColor *)backgroundColor; - -/** - Whether a text color passes accessibility standards when displayed on an opaque background color. - - MDFTextAccessibilityOptionsLargeFont and MDFTextAccessibilityOptionsEnhancedContrast are relevant - options for this method. - - @param textColor A text color with optional transparency. - @param backgroundColor The opaque background color the text will be displayed on. - @param options A combination of MDFTextAccessibilityOptions values. - @return YES if the text color would meet accessibility standards. - */ -+ (BOOL)textColor:(nonnull UIColor *)textColor - passesOnBackgroundColor:(nonnull UIColor *)backgroundColor - options:(MDFTextAccessibilityOptions)options; - -/** - Whether a particular font would be considered "large" for the purposes of calculating - contrast ratios. - - Large fonts are defined as greater than 18pt normal or 14pt bold. If the passed font is nil, then - this method returns NO. - For more see: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html - - @param font The font to examine, or nil. - @return YES if the font is non-nil and is considered "large". - */ -+ (BOOL)isLargeForContrastRatios:(nullable UIFont *)font; - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m deleted file mode 100644 index 38cf89b4..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/MDFTextAccessibility.m +++ /dev/null @@ -1,188 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MDFTextAccessibility.h" - -#import "MDFColorCalculations.h" -#import "MDFImageCalculations.h" -#import "NSArray+MDFUtils.h" - -static const CGFloat kMinContrastRatioNormalText = 4.5f; -static const CGFloat kMinContrastRatioLargeText = 3.0f; -static const CGFloat kMinContrastRatioNormalTextEnhanced = 7.0f; -static const CGFloat kMinContrastRatioLargeTextEnhanced = 4.5f; - -@implementation MDFTextAccessibility - -+ (nonnull UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor - targetTextAlpha:(CGFloat)targetTextAlpha - font:(nullable UIFont *)font { - MDFTextAccessibilityOptions options = 0; - if ([self isLargeForContrastRatios:font]) { - options |= MDFTextAccessibilityOptionsLargeFont; - } - return [self textColorOnBackgroundColor:backgroundColor - targetTextAlpha:targetTextAlpha - options:options]; -} - -+ (nullable UIColor *)textColorOnBackgroundImage:(nonnull UIImage *)backgroundImage - inRegion:(CGRect)region - targetTextAlpha:(CGFloat)targetTextAlpha - font:(nullable UIFont *)font { - UIColor *backgroundColor = MDFAverageColorOfOpaqueImage(backgroundImage, region); - if (!backgroundColor) { - return nil; - } - - return [self textColorOnBackgroundColor:backgroundColor - targetTextAlpha:targetTextAlpha - font:font]; -} - -+ (nullable UIColor *)textColorOnBackgroundColor:(nonnull UIColor *)backgroundColor - targetTextAlpha:(CGFloat)targetTextAlpha - options:(MDFTextAccessibilityOptions)options { - NSArray *colors = @[ - [UIColor colorWithWhite:1 alpha:targetTextAlpha], [UIColor colorWithWhite:0 - alpha:targetTextAlpha] - ]; - UIColor *textColor = [self textColorFromChoices:colors - onBackgroundColor:backgroundColor - options:options]; - return textColor; -} - -+ (nullable UIColor *)textColorFromChoices:(nonnull NSArray *)choices - onBackgroundColor:(nonnull UIColor *)backgroundColor - options:(MDFTextAccessibilityOptions)options { - [choices enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - NSAssert([obj isKindOfClass:[UIColor class]], @"Choices must be UIColors."); - }]; - - // Sort by luminance if requested. - if ((options & MDFTextAccessibilityOptionsPreferLighter) || - (options & MDFTextAccessibilityOptionsPreferDarker)) { - NSArray *luminances = [choices mdf_arrayByMappingObjects:^id(id object) { - return @([self luminanceOfColor:object]); - }]; - - BOOL inverse = (options & MDFTextAccessibilityOptionsPreferDarker) ? YES : NO; - choices = [luminances mdf_sortArray:choices - usingComparator:^NSComparisonResult(id obj1, id obj2) { - float first = inverse ? [obj1 floatValue] : [obj2 floatValue]; - float second = inverse ? [obj2 floatValue] : [obj1 floatValue]; - - if (first < second) { - return NSOrderedAscending; - } else if (first > second) { - return NSOrderedDescending; - } else { - return NSOrderedSame; - } - }]; - } - - // Search the array for a color that can be used, adjusting alpha values upwards if requested. - // The first acceptable color (adjusted or not) is returned. - BOOL adjustAlphas = (options & MDFTextAccessibilityOptionsPreserveAlpha) ? NO : YES; - for (UIColor *choice in choices) { - if ([self textColor:choice passesOnBackgroundColor:backgroundColor options:options]) { - return choice; - } - - if (!adjustAlphas) { - continue; - } - - CGFloat alpha = CGColorGetAlpha(choice.CGColor); - CGFloat minAlpha = [self minAlphaOfTextColor:choice - onBackgroundColor:backgroundColor - options:options]; - if (minAlpha > 0) { - if (alpha > minAlpha) { - NSAssert(NO, - @"Logic error: computed an acceptable minimum alpha (%f) that is *less* than the " - @"unacceptable current alpha (%f).", - minAlpha, alpha); - continue; - } - return [choice colorWithAlphaComponent:minAlpha]; - } - } - - return nil; -} - -+ (CGFloat)minAlphaOfTextColor:(nonnull UIColor *)textColor - onBackgroundColor:(nonnull UIColor *)backgroundColor - options:(MDFTextAccessibilityOptions)options { - CGFloat minContrastRatio = [self minContrastRatioForOptions:options]; - return MDFMinAlphaOfColorOnBackgroundColor(textColor, backgroundColor, minContrastRatio); -} - -+ (CGFloat)contrastRatioForTextColor:(UIColor *)textColor - onBackgroundColor:(UIColor *)backgroundColor { - CGFloat colorComponents[4]; - CGFloat backgroundColorComponents[4]; - MDFCopyRGBAComponents(textColor.CGColor, colorComponents); - MDFCopyRGBAComponents(backgroundColor.CGColor, backgroundColorComponents); - - NSAssert(backgroundColorComponents[3] == 1, - @"Background color %@ must be opaque for a valid contrast ratio calculation.", - backgroundColor); - backgroundColorComponents[3] = 1; - - return MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); -} - -+ (BOOL)textColor:(nonnull UIColor *)textColor - passesOnBackgroundColor:(nonnull UIColor *)backgroundColor - options:(MDFTextAccessibilityOptions)options { - CGFloat minContrastRatio = [self minContrastRatioForOptions:options]; - CGFloat ratio = [self contrastRatioForTextColor:textColor onBackgroundColor:backgroundColor]; - return ratio >= minContrastRatio ? YES : NO; -} - -+ (BOOL)isLargeForContrastRatios:(nullable UIFont *)font { - UIFontDescriptor *fontDescriptor = font.fontDescriptor; - BOOL isBold = - (fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) == UIFontDescriptorTraitBold; - return font.pointSize >= 18 || (isBold && font.pointSize >= 14); -} - -#pragma mark - Private methods - -+ (CGFloat)luminanceOfColor:(UIColor *)color { - CGFloat colorComponents[4]; - MDFCopyRGBAComponents(color.CGColor, colorComponents); - return MDFRelativeLuminanceOfRGBComponents(colorComponents); -} - -+ (CGFloat)minContrastRatioForOptions:(MDFTextAccessibilityOptions)options { - BOOL isLarge = - (options & MDFTextAccessibilityOptionsLargeFont) == MDFTextAccessibilityOptionsLargeFont; - BOOL isEnhanced = (options & MDFTextAccessibilityOptionsEnhancedContrast) == - MDFTextAccessibilityOptionsEnhancedContrast; - - if (isEnhanced) { - return isLarge ? kMinContrastRatioLargeTextEnhanced : kMinContrastRatioNormalTextEnhanced; - } else { - return isLarge ? kMinContrastRatioLargeText : kMinContrastRatioNormalText; - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h deleted file mode 100644 index 3e2f78ff..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#if defined(__cplusplus) -extern "C" { -#endif - -/** - Copies the RGBA components of color, expanding greyscale colors out to RGBA if necessary. - - @param color The color to extract the components from. - @param rgbaComponents A pointer to four CGFloat values. - */ -void MDFCopyRGBAComponents(CGColorRef color, CGFloat *rgbaComponents); - -/** - Returns the contrast ratio of a foreground color blended on top of a background color. - - @param foregroundColorComponents A pointer to four RGBA CGFloats of the foreground color. - @param backgroundColorComponents A pointer to four RGBA CGFloats of the background color. - @return The contrast ratio of the two colors after blending. - */ -CGFloat MDFContrastRatioOfRGBAComponents(const CGFloat *foregroundColorComponents, - const CGFloat *backgroundColorComponents); - -/** - Calculates the relative luminance of a sRGB color from its components (ignores alpha). - @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef - - @param components A pointer to three RGB CGFloat values of the color. - @return The relative luminance of the color. - */ -CGFloat MDFRelativeLuminanceOfRGBComponents(const CGFloat *components); - -/** - Calculates the minimum alpha that a foreground color can have such that, when it's blended on top - of an opaque background color, the resulting contrast ratio is higher than a minimum contrast - ratio. - - If there is no such acceptable contrast ratio, then -1 is returned. This is the case when even a - completely opaque foreground color can't produce a high enough contrast ratio. For example, light - grey on white will have a low contrast ratio for any alpha value assigned to the light grey. - - @param color The foreground color (alpha ignored). - @param backgroundColor The background color (assumed to be opaque). - @param minContrastRatio The minimum allowable contrast ratio. - @return The minimum acceptable alpha of the foreground color, or -1 if no such alpha exists. - */ -CGFloat MDFMinAlphaOfColorOnBackgroundColor(UIColor *color, - UIColor *backgroundColor, - CGFloat minContrastRatio); - -#if defined __cplusplus -} // extern "C" -#endif diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m deleted file mode 100644 index 4e2699e9..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFColorCalculations.m +++ /dev/null @@ -1,184 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MDFColorCalculations.h" - -/** - The number of iterations required to find the minimum acceptable alpha is - ceil(log2(1/kMinAlphaSearchPrecision)), or 7 for a precision of 0.01. If you adjust the precision - then also adjust the max iterations, which is used as a safety check. - */ -static const CGFloat kMinAlphaSearchPrecision = 0.01f; -static const NSUInteger kMinAlphaSearchMaxIterations = 10; - -/** Returns value raised to exponent. */ -static inline CGFloat MDFPow(CGFloat value, CGFloat exponent) { -#if CGFLOAT_IS_DOUBLE - return pow(value, exponent); -#else - return powf(value, exponent); -#endif -} - -/** - Calculate a linear RGB component from a sRGB component for calculating luminance. - @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef - */ -static inline CGFloat LinearRGBComponent(CGFloat component) { - if (component <= 0.03928f) { - return component / 12.92f; - } else { - return MDFPow(((component + 0.055f) / 1.055f), 2.4f); - } -} - -/** - Blend a foreground color with alpha on a opaque background color. - @note The background color must be opaque for this to be valid. - @see http://en.wikipedia.org/wiki/Alpha_compositing - */ -static inline void BlendColorOnOpaqueBackground(const CGFloat *foregroundColor, - const CGFloat *backgroundColor, - CGFloat *blendedColor) { - for (int i = 0; i < 3; ++i) { - blendedColor[i] = - foregroundColor[i] * foregroundColor[3] + backgroundColor[i] * (1 - foregroundColor[3]); - } - blendedColor[3] = 1.0f; -} - -static inline CGFloat ContrastRatioOfRGBComponents(const CGFloat *firstComponents, - const CGFloat *secondComponents) { - CGFloat firstLuminance = MDFRelativeLuminanceOfRGBComponents(firstComponents); - CGFloat secondLuminance = MDFRelativeLuminanceOfRGBComponents(secondComponents); - - if (secondLuminance > firstLuminance) { - CGFloat temp = firstLuminance; - firstLuminance = secondLuminance; - secondLuminance = temp; - } - - return (firstLuminance + 0.05f) / (secondLuminance + 0.05f); -} - -CGFloat MDFContrastRatioOfRGBAComponents(const CGFloat *foregroundColorComponents, - const CGFloat *backgroundColorComponents) { - CGFloat blendedColor[4]; - BlendColorOnOpaqueBackground(foregroundColorComponents, backgroundColorComponents, blendedColor); - return ContrastRatioOfRGBComponents(blendedColor, backgroundColorComponents); -} - -void MDFCopyRGBAComponents(CGColorRef color, CGFloat *rgbaComponents) { - CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(CGColorGetColorSpace(color)); - if (!(colorSpaceModel == kCGColorSpaceModelRGB || - colorSpaceModel == kCGColorSpaceModelMonochrome)) { - NSCAssert(NO, @"Can't compute luminance for non-RGB color space model %i.", colorSpaceModel); - for (int i = 0; i < 4; ++i) { - rgbaComponents[i] = 0; - } - return; - } - - size_t numComponents = CGColorGetNumberOfComponents(color); - const CGFloat *components = CGColorGetComponents(color); - switch (numComponents) { - // Greyscale + alpha - case 2: - for (int i = 0; i < 3; ++i) { - rgbaComponents[i] = components[0]; - } - rgbaComponents[3] = components[1]; - break; - - // RGB + alpha - case 4: - for (int i = 0; i < 4; ++i) { - rgbaComponents[i] = components[i]; - } - break; - - default: - NSCAssert(NO, @"Unexpected number of color components: %zu.", numComponents); - } -} - -CGFloat MDFRelativeLuminanceOfRGBComponents(const CGFloat *components) { - CGFloat linearRGB[3]; - for (int i = 0; i < 3; ++i) { - linearRGB[i] = LinearRGBComponent(components[i]); - } - return 0.2126f * linearRGB[0] + 0.7152f * linearRGB[1] + 0.0722f * linearRGB[2]; -} - -CGFloat MDFMinAlphaOfColorOnBackgroundColor(UIColor *color, UIColor *backgroundColor, - CGFloat minContrastRatio) { - CGFloat colorComponents[4]; - CGFloat backgroundColorComponents[4]; - MDFCopyRGBAComponents(color.CGColor, colorComponents); - MDFCopyRGBAComponents(backgroundColor.CGColor, backgroundColorComponents); - - NSCAssert(backgroundColorComponents[3] == 1, - @"Background color %@ must be opaque for a valid contrast ratio calculation.", - backgroundColor); - backgroundColorComponents[3] = 1; - - NSCAssert(minContrastRatio > 0, @"Invalid min contrast ratio %g.", (double)minContrastRatio); - CGFloat minAlpha = 0; - CGFloat maxAlpha = 1; - -#if DEBUG && !defined(NS_BLOCK_ASSERTIONS) - colorComponents[3] = minAlpha; - CGFloat minAlphaRatio = - MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); - NSCAssert(minAlphaRatio < minContrastRatio, @"Transparent color cannot be a valid color."); -#endif // !defined(NS_BLOCK_ASSERTIONS) - - // maxAlphaRatio is the best contrast ratio we can acheive by modifying the alpha of this color. - // If it's not good enough, then return now and inform the caller. - colorComponents[3] = maxAlpha; - CGFloat maxAlphaRatio = - MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); - if (maxAlphaRatio < minContrastRatio) { - return -1; - } - - // Classic binary search of a range. - NSUInteger numIterations = 0; - while (numIterations <= kMinAlphaSearchMaxIterations && - (maxAlpha - minAlpha) > kMinAlphaSearchPrecision) { - CGFloat testAlpha = (minAlpha + maxAlpha) / 2; - colorComponents[3] = testAlpha; - CGFloat testRatio = - MDFContrastRatioOfRGBAComponents(colorComponents, backgroundColorComponents); - - if (testRatio < minContrastRatio) { - minAlpha = testAlpha; - } else { - maxAlpha = testAlpha; - } - - ++numIterations; - } - - if (numIterations > kMinAlphaSearchMaxIterations) { - NSCAssert(NO, @"Too many iterations (%i) while finding min alpha of text color %@ on %@.", - (int)numIterations, color, backgroundColor); - return -1; - } - - // Conservatively return the max of the range of possible alphas, which is known to pass. - return maxAlpha; -} diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h deleted file mode 100644 index 7cdb664b..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#if defined(__cplusplus) -extern "C" { -#endif - -/** - Return the average color of an image in a particular region. - - The region will be intersected with the image's bounds. If the resulting region is empty (or the - input region was null) then this function returns nil. - - @param image The image to examine. - @param region The region of the image to average, or CGRectInfinite for the entire image. - @return The average color, or nil if the region was invalid. - */ -UIColor *MDFAverageColorOfOpaqueImage(UIImage *image, CGRect region); - -#if defined __cplusplus -} // extern "C" -#endif diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m deleted file mode 100644 index d5546148..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/MDFImageCalculations.m +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MDFImageCalculations.h" - -UIColor *MDFAverageColorOfOpaqueImage(UIImage *image, CGRect region) { - CGImageRef imageRef = image.CGImage; - CGImageRef cropped = CGImageCreateWithImageInRect(imageRef, region); - - // Empty/null regions will cause cropped to be nil. - if (!cropped) { - return nil; - } - - UIGraphicsBeginImageContext(CGSizeMake(1, 1)); - - uint8_t argb[4]; - CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = - CGBitmapContextCreate(argb, // data - 1, // width - 1, // height - 8, // Bits per component - 4, // Bytes per row - colorspace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big); - CGColorSpaceRelease(colorspace); - CGContextSetInterpolationQuality(context, kCGInterpolationMedium); - CGContextSetBlendMode(context, kCGBlendModeCopy); - CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), cropped); - CGContextRelease(context); - CGImageRelease(cropped); - - CGFloat alpha = argb[0] / (CGFloat)255; - CGFloat scale = alpha > 0 ? 1 / (alpha * 255) : 0; - UIColor *color = [UIColor colorWithRed:scale * argb[1] - green:scale * argb[2] - blue:scale * argb[3] - alpha:alpha]; - UIGraphicsEndImageContext(); - return color; -} diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h deleted file mode 100644 index 57784b3b..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -typedef id (^MDFMappingFunction)(id object); - -/** Functional extensions to NSArray. */ -@interface NSArray (MDFUtils) - -/** - * Returns an array consisting of applying |function| to each element of self in order. - * - * @param function A block mapping an input element to an output element. - * @return An array of the same size as self containing elements mapped through the function. - */ -- (NSArray *)mdf_arrayByMappingObjects:(MDFMappingFunction)function; - -/** - * Returns a sorted version of |array| by using the passed comparator on self. - * - * @note The comparator acts of elements of self, @em not |array|. - * - * Example: - * @code - * NSArray *weights = @[ 100, 200, 50 ]; - * NSArray *dogs = @[ @"Bruno", @"Tiger", @"Spot" ]; - * NSComparator *ascending = ... NSString comparator ... - * NSArray *sortedDogs = [weights mdf_sortArray:dogs - * usingComparator:ascending]; - * // sortedDogs is @[ @"Spot", @"Bruno", @"Tiger" ]. - * @endcode - * - * @param array The array to sort. - * @param comparator A comparator acting on elements of self. - * @return A sorted copy of |array|. - */ -- (NSArray *)mdf_sortArray:(NSArray *)array usingComparator:(NSComparator)comparator; - -@end diff --git a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m b/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m deleted file mode 100644 index 9758611a..00000000 --- a/test/fixtures/cocoapods/Pods/MDFTextAccessibility/src/private/NSArray+MDFUtils.m +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright 2016-present Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "NSArray+MDFUtils.h" - -@implementation NSArray (MDFUtils) - -- (NSArray *)mdf_arrayByMappingObjects:(MDFMappingFunction)function { - NSAssert(function, @"Mapping block must not be NULL."); - NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:[self count]]; - [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [array addObject:function(obj)]; - }]; - return [array copy]; -} - -- (BOOL)mdf_anyObjectPassesTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { - NSIndexSet *indices = [self indexesOfObjectsPassingTest:predicate]; - return [indices count] > 0; -} - -- (BOOL)mdf_allObjectsPassTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { - NSIndexSet *indices = [self indexesOfObjectsPassingTest:predicate]; - return [indices count] == [self count]; -} - -- (NSArray *)mdf_sortArray:(NSArray *)array usingComparator:(NSComparator)comparator { - NSAssert(comparator, @"Comparator block must not be NULL."); - - NSUInteger numElements = [self count]; - NSAssert([array count] == numElements, @"Array %@ must have length %lu.", array, - (unsigned long)numElements); - - // Create a permutation array by sorting self with comparator. - NSMutableArray *permutation = [[NSMutableArray alloc] initWithCapacity:numElements]; - for (NSUInteger i = 0; i < numElements; ++i) { - [permutation addObject:@(i)]; - } - - NSArray *sortedPermutation = [permutation sortedArrayUsingComparator:^(id a, id b) { - NSUInteger firstIndex = [a unsignedIntegerValue]; - NSUInteger secondIndex = [b unsignedIntegerValue]; - return comparator(self[firstIndex], self[secondIndex]); - }]; - - // Permute array into order. - NSMutableArray *sorted = [[NSMutableArray alloc] initWithCapacity:numElements]; - for (NSUInteger i = 0; i < numElements; ++i) { - NSUInteger index = [sortedPermutation[i] unsignedIntegerValue]; - [sorted addObject:array[index]]; - } - return [sorted copy]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/Manifest.lock b/test/fixtures/cocoapods/Pods/Manifest.lock deleted file mode 100644 index dd84759f..00000000 --- a/test/fixtures/cocoapods/Pods/Manifest.lock +++ /dev/null @@ -1,102 +0,0 @@ -PODS: - - Alamofire (5.4.3) - - Chatto (4.1.0) - - lottie-ios (3.3.0) - - MaterialComponents/AnimationTiming (124.2.0) - - MaterialComponents/Availability (124.2.0) - - MaterialComponents/Buttons (124.2.0): - - MaterialComponents/Elevation - - MaterialComponents/Ink - - MaterialComponents/private/Math - - MaterialComponents/Ripple - - MaterialComponents/Shadow - - MaterialComponents/ShadowElevations - - MaterialComponents/ShadowLayer - - MaterialComponents/ShapeLibrary - - MaterialComponents/Shapes - - MaterialComponents/Typography - - MDFInternationalization - - MDFTextAccessibility - - MaterialComponents/Cards (124.2.0): - - MaterialComponents/Elevation - - MaterialComponents/Ink - - MaterialComponents/private/Icons/ic_check_circle - - MaterialComponents/private/Math - - MaterialComponents/Ripple - - MaterialComponents/ShadowLayer - - MaterialComponents/Shapes - - MaterialComponents/Elevation (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/Ink (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/private/Application (124.2.0) - - MaterialComponents/private/Color (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/private/Icons/Base (124.2.0) - - MaterialComponents/private/Icons/ic_check_circle (124.2.0): - - MaterialComponents/private/Icons/Base - - MaterialComponents/private/Math (124.2.0) - - MaterialComponents/Ripple (124.2.0): - - MaterialComponents/AnimationTiming - - MaterialComponents/Availability - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/Shadow (124.2.0): - - MaterialComponents/Availability - - MaterialComponents/ShadowElevations (124.2.0) - - MaterialComponents/ShadowLayer (124.2.0): - - MaterialComponents/ShadowElevations - - MaterialComponents/ShapeLibrary (124.2.0): - - MaterialComponents/private/Math - - MaterialComponents/Shapes - - MaterialComponents/Shapes (124.2.0): - - MaterialComponents/private/Color - - MaterialComponents/private/Math - - MaterialComponents/ShadowLayer - - MaterialComponents/Typography (124.2.0): - - MaterialComponents/private/Application - - MaterialComponents/private/Math - - MDFTextAccessibility - - MDFInternationalization (3.0.0) - - MDFTextAccessibility (2.0.1) - -DEPENDENCIES: - - Alamofire (= 5.4.3) - - Chatto (from `https://github.com/badoo/Chatto`, tag `4.1.0`) - - lottie-ios (= 3.3.0) - - MaterialComponents/Buttons (= 124.2.0) - - MaterialComponents/Cards (= 124.2.0) - -SPEC REPOS: - trunk: - - Alamofire - - lottie-ios - - MaterialComponents - - MDFInternationalization - - MDFTextAccessibility - -EXTERNAL SOURCES: - Chatto: - :git: https://github.com/badoo/Chatto - :tag: 4.1.0 - -CHECKOUT OPTIONS: - Chatto: - :git: https://github.com/badoo/Chatto - :tag: 4.1.0 - -SPEC CHECKSUMS: - Alamofire: e447a2774a40c996748296fa2c55112fdbbc42f9 - Chatto: dae34c377e0eca8bdcfe7a05a4d79d55eaccec3e - lottie-ios: 6ac74dcc09904798f59b18cb3075c089d76be9ae - MaterialComponents: 1a9b2d9d45b1601ae544de85089adc4c464306d4 - MDFInternationalization: d697c55307816222a55685c4ccb1044ffb030c12 - MDFTextAccessibility: f4bb4cc2194286651b59a215fdeaa0e05dc90ba5 - -PODFILE CHECKSUM: 5288d14d2586d983b7a18fae6fc10abda0f306a9 - -COCOAPODS: 1.11.2 diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE b/test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/README.md b/test/fixtures/cocoapods/Pods/MaterialComponents/README.md deleted file mode 100644 index 82c914f9..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Material Components for iOS - - - -[![Code coverage](https://img.shields.io/codecov/c/github/material-components/material-components-ios/develop.svg)](https://codecov.io/gh/material-components/material-components-ios/branch/develop) - -Material Components for iOS (MDC-iOS) helps developers execute [Material Design](https://material.io). Developed by a core team of engineers and UX designers at Google, these components enable a reliable development workflow to build beautiful and functional iOS apps. Learn more about how Material Components for iOS supports design and usability best practices across platforms in the [Material Design Platform Adaptation guidelines](https://material.io/guidelines/platforms/platform-adaptation.html). - -Material Components for iOS are written in Objective-C and support Swift and Interface Builder. - -## Useful Links - -- [Documentation](https://material.io/components/ios/) (external site) -- [How To Use MDC-iOS](docs/) -- [All Components](components/) -- [Demo Apps](demos/) -- [Contributing](contributing/) -- [MDC-iOS on Stack Overflow](https://www.stackoverflow.com/questions/tagged/material-components+ios) (external site) -- [Material.io](https://material.io) (external site) -- [Material Design Guidelines](https://material.io/guidelines) (external site) -- [Checklist status spreadsheet](https://docs.google.com/spreadsheets/d/e/2PACX-1vRQLFMuo0Q3xsJp1_TdWvImtfdc8dU0lqX2DTct5pOPAEUIrN9OsuPquvv4aKRAwKK_KItpGs7c4Fok/pubhtml) - -## Trying out Material Components - -[CocoaPods](https://cocoapods.org/) is the easiest way to get started (if you're new to CocoaPods, -check out their [getting started documentation](https://guides.cocoapods.org/using/getting-started.html).) - -To install CocoaPods, run the following commands: - -```bash -sudo gem install cocoapods -``` - -Our [catalog](catalog/) showcases Material Components. You can use the `pod try` command from anywhere on your machine to try the components, even if you haven't checked out the repo yet: - -``` bash -pod try MaterialComponents -``` - -In case you have already checked out the repo, run the following command: - -``` bash -pod install --project-directory=catalog/ -``` - -The component implementations can be found in Xcode within `Pods > Development Pods > MaterialComponents`. - -## Requirements - -- Xcode 10 or higher -- Minimum iOS deployment target of 10.0 or higher -- CocoaPods 1.5 or higher - -## Attributions - -Material Components for iOS uses -[Material Design icons](https://github.com/google/material-design-icons), -copyright Google Inc. and licensed under -[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). - -Several components use -[MDFTextAccessibility](https://github.com/material-foundation/material-text-accessibility-ios), -copyright Google Inc. and licensed under -[Apache 2.0](https://github.com/material-foundation/material-text-accessibility-ios/blob/master/LICENSE) -without a NOTICE file. - -MDCCatalog uses the -[Roboto font](https://github.com/google/fonts/tree/master/apache/roboto), -copyright 2011 Google Inc. and licensed under -[Apache 2.0](https://github.com/google/fonts/blob/master/apache/roboto/LICENSE.txt) -without a NOTICE file. diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h deleted file mode 100644 index d7b717a9..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -/** - Material Design easing curve animation values. - - Use these easing curves to create smooth and consistent motion that conforms to Material Design. - */ -typedef NS_ENUM(NSUInteger, MDCAnimationTimingFunction) { - /** - This is the most frequently used interpolation curve for Material Design animations. This curve - is slow both at the beginning and end. It has similar characteristics to the system's EaseInOut. - This is known as Standard in the Material Design spec. - */ - MDCAnimationTimingFunctionStandard, - - /** - This curve should be used for motion when entering frame or when fading in from 0% opacity. This - curve is slow at the end. It has similar characteristics to the system's EaseOut. This is known - as Deceleration in the Material Design spec. - */ - MDCAnimationTimingFunctionDeceleration, - - /** - This curve should be used for motion when exiting frame or when fading out to 0% opacity. This - curve is slow at the beginning. It has similar characteristics to the system's EaseIn. This - is known as Acceleration in the Material Design spec. - */ - MDCAnimationTimingFunctionAcceleration, - - /** - This curve should be used for motion when elements quickly accelerate and decelerate. It is - used by exiting elements that may return to the screen at any time. The deceleration is - faster than the standard curve since it doesn't follow an exact path to the off-screen point. - */ - MDCAnimationTimingFunctionSharp, - - /** - Aliases for depreciated names - */ - MDCAnimationTimingFunctionEaseInOut = MDCAnimationTimingFunctionStandard, - MDCAnimationTimingFunctionEaseOut = MDCAnimationTimingFunctionDeceleration, - MDCAnimationTimingFunctionEaseIn = MDCAnimationTimingFunctionAcceleration, - - /** - Aliases for various specific timing curve recommendations. - */ - MDCAnimationTimingFunctionTranslate = MDCAnimationTimingFunctionStandard, - MDCAnimationTimingFunctionTranslateOnScreen = MDCAnimationTimingFunctionDeceleration, - MDCAnimationTimingFunctionTranslateOffScreen = MDCAnimationTimingFunctionAcceleration, - MDCAnimationTimingFunctionFadeIn = MDCAnimationTimingFunctionDeceleration, - MDCAnimationTimingFunctionFadeOut = MDCAnimationTimingFunctionAcceleration, -}; - -/** - Material Design animation curves. - */ -@interface CAMediaTimingFunction (MDCAnimationTiming) - -/** - Returns the corresponding CAMediaTimingFunction for the given curve specified by an enum. The most - common curve is MDCAnimationTimingFunctionEaseInOut. - - @param type A Material Design media timing function. - */ -+ (nullable CAMediaTimingFunction *)mdc_functionWithType:(MDCAnimationTimingFunction)type; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m deleted file mode 100644 index b2f43eea..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "CAMediaTimingFunction+MDCAnimationTiming.h" - -@implementation CAMediaTimingFunction (MDCAnimationTiming) - -+ (CAMediaTimingFunction *)mdc_functionWithType:(MDCAnimationTimingFunction)type { - switch (type) { - // clang-format off - case MDCAnimationTimingFunctionStandard: - return [[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:(float)0.2:1]; - case MDCAnimationTimingFunctionDeceleration: - return [[CAMediaTimingFunction alloc] initWithControlPoints:0:0:(float)0.2:1]; - case MDCAnimationTimingFunctionAcceleration: - return [[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:1:1]; - case MDCAnimationTimingFunctionSharp: - return [[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:(float)0.6:1]; - // clang-format on - } - NSAssert(NO, @"Invalid MDCAnimationTimingFunction value %i.", (int)type); - // Reasonable default to use in Release mode for garbage input. - return nil; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h deleted file mode 100644 index fa2b8798..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/MaterialAnimationTiming.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "CAMediaTimingFunction+MDCAnimationTiming.h" -#import "UIView+MDCTimingFunction.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h deleted file mode 100644 index ce503185..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -@interface UIView (MDCTimingFunction) - -/** - A convienence method for applying a timing function to animations. - - @param timingFunction A timing function for the easing curve animation. - @param duration The time the animation takes. - @param delay The time to wait before the animation begins. - @param options Configuration options for the timing function. - @param animations Animations to which the timing function will apply. - @param completion A completion block fired after the animations complete. - */ -+ (void)mdc_animateWithTimingFunction:(nullable CAMediaTimingFunction *)timingFunction - duration:(NSTimeInterval)duration - delay:(NSTimeInterval)delay - options:(UIViewAnimationOptions)options - animations:(void (^__nonnull)(void))animations - completion:(void (^__nullable)(BOOL finished))completion; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m deleted file mode 100644 index bdb7ad64..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/AnimationTiming/src/UIView+MDCTimingFunction.m +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIView+MDCTimingFunction.h" - -@implementation UIView (MDCTimingFunction) - -+ (void)mdc_animateWithTimingFunction:(CAMediaTimingFunction *)timingFunction - duration:(NSTimeInterval)duration - delay:(NSTimeInterval)delay - options:(UIViewAnimationOptions)options - animations:(void (^)(void))animations - completion:(void (^)(BOOL finished))completion { - [CATransaction begin]; - [CATransaction setAnimationTimingFunction:timingFunction]; - [UIView animateWithDuration:duration - delay:delay - options:options - animations:animations - completion:completion]; - [CATransaction commit]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h deleted file mode 100644 index 03b116d4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MDCAvailability.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights -// Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef MDC_AVAILABILITY -#define MDC_AVAILABILITY - -#include - -/* - A header to be used in conjunction with Availability.h to conditionally - compile OS sensitive code. - - MDC_AVAILABLE_SDK_IOS(_ios) evaluates to true when the maximum target SDK is - greater than equal to the given value. This will only prevent code from - compiling when built with a maximum SDK version lower than the version - specified. A runtime check may still be necessary. Example: - - #if MDC_AVAILABLE_SDK_IOS(13_0) - // iOS 13 specific code. - #endif - */ - -#define MDC_AVAILABLE_SDK_IOS(_ios) \ - ((__IPHONE_##_ios != 0) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_##_ios)) - -#endif // MDC_AVAILABILITY diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h deleted file mode 100644 index e1726c82..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Availability/src/MaterialAvailability.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights -// Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCAvailability.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h deleted file mode 100644 index 2440c279..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.h +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#import "MaterialElevation.h" -#import "MaterialInk.h" -#import "MaterialRipple.h" -#import "MaterialShadow.h" -#import "MaterialShadowElevations.h" -#import "MaterialShapes.h" - -/** - A Material flat, raised or floating button. - - All buttons display animated ink splashes when the user interacts with the button. - - The title color of the button set to have an accessible contrast ratio with the button's - background color. To ensure this works for flat buttons (with transparent background), the caller - is responsible for setting (and updating, if necessary) the button's underlyingColor property. - - All buttons set the exclusiveTouch property to YES by default, which prevents users from - simultaneously interacting with a button and other UI elements. - - @see https://material.io/go/design-buttons - */ -@interface MDCButton : UIButton - -/** The ripple style of the button. */ -@property(nonatomic, assign) MDCRippleStyle rippleStyle; - -/** - The color of the ripple. - - @note Defaults to a transparent black. - */ -@property(nonatomic, strong, null_resettable) UIColor *rippleColor; - -/** - The maximum radius the ripple can expand to. - - @note This property is ignored if @c rippleStyle is set to @c MDCRippleStyleBounded. - - @note Defaults to 0. - */ -@property(nonatomic, assign) CGFloat rippleMaximumRadius; - -/** - This property determines if an @c MDCButton should use the @c MDCInkView behavior or not. - - By setting this property to @c YES, @c MDCStatefulRippleView is used to provide the user visual - touch feedback, instead of the legacy @c MDCInkView. - - @note Defaults to @c NO. - */ -@property(nonatomic, assign) BOOL enableRippleBehavior; - -/** - The alpha value that will be applied when the button is disabled. Most clients can leave this as - the default value to get a semi-transparent button automatically. - */ -@property(nonatomic) CGFloat disabledAlpha; - -/** - If true, converts the button title to uppercase. Changing this property to NO will update the - current title string. - - Default is YES. - */ -@property(nonatomic, getter=isUppercaseTitle) BOOL uppercaseTitle UI_APPEARANCE_SELECTOR; - -/** - A Boolean value that determines whether the visible area is centered in the bounds of the view. - - If set to YES, the visible area is centered in the bounds of the view, which is often used to - configure invisible tappable area. If set to NO, the visible area fills its bounds. This property - doesn't affect the result of @c sizeThatFits:. - - The default value is @c NO. -*/ -@property(nonatomic, assign) BOOL centerVisibleArea; - -/** - The edges of this guide are constrained to equal the edges of the visible area - when @c centerVisibleArea is @c YES. - - @note If centerVisibleArea is @c NO then visibleAreaLayoutGuide is nil. -*/ -@property(nonatomic, readonly, strong, nullable) UILayoutGuide *visibleAreaLayoutGuide; - -/** - The default content edge insets of the button. They are set at initialization time. - */ -@property(nonatomic, readonly) UIEdgeInsets defaultContentEdgeInsets; - -/** - The offset (in points) of the button's inkView or rippleView (depending on which is being used - - see @c enableRippleBehavior) - - Default is CGSizeZero. - */ -@property(nonatomic) CGSize inkViewOffset; - -/** - The inset or outset margins for the rectangle surrounding the button’s ripple. - */ -@property(nonatomic, assign) UIEdgeInsets rippleEdgeInsets; - -/** - The minimum size of the button’s alignment rect. If either the height or width are non-positive - (negative or zero), they will be ignored and that axis will adjust to its content size. - - Defaults to CGSizeZero. - */ -@property(nonatomic, assign) CGSize minimumSize UI_APPEARANCE_SELECTOR; - -/** - The maximum size of the button’s alignment rect. If either the height or width are non-positive - (negative or zero), they will be ignored and that axis will adjust to its content size. Setting a - maximum size may result in image clipping or text truncation. - - Defaults to CGSizeZero. - */ -@property(nonatomic, assign) CGSize maximumSize UI_APPEARANCE_SELECTOR; - -/** - Setting this property to @c YES when the button's @c titleLabel is multi-line (i.e. when @c - numberOfLines is not equal to 1) will result in the button inferring what its size should be and - then setting both the @c minimumSize and @c maximumSize to that value. Setting this property back - to @c NO will result in @c maximumSize and @c minimumSize being reset to @c CGSizeZero. - - In both Manual Layout and Auto Layout environments the inferred height is a function of the width. - In an Auto Layout environment the width will depend on the constraints placed on the view. In a - Manual Layout environment the current width will be assumed to be the preferred width, so it is - important to make sure the button's width is set to an appropriate value before turning this flag - on. In an Auto Layout environment, the view will likely resize itself as needed when this flag is - turned on. In a Manual Layout environment, you will likely have to call @c -sizeToFit after turning - this flag on. - - Defaults to NO. - */ -@property(nonatomic, assign) BOOL inferMinimumAndMaximumSizeWhenMultiline; - -/** - The apparent background color as seen by the user, i.e. the color of the view behind the button. - - The underlying color hint is used by buttons to calculate accessible title text colors when in - states with transparent background colors. The hint is used whenever the button changes state such - that the background color changes, for example, setting the background color or disabling the - button. - - For flat buttons, this is the color of both the surrounding area and the button's background. - For raised and floating buttons, this is the color of view underneath the button. - - The default is nil. If left unset, buttons will likely have an incorrect appearance when - disabled. Additionally, flat buttons might have text colors with low accessibility. - */ -@property(nonatomic, strong, nullable) UIColor *underlyingColorHint; - -/* - Indicates whether the button should automatically update its font when the device’s - UIContentSizeCategory is changed. - - This property is modeled after the adjustsFontForContentSizeCategory property in the - UIContentSizeCategoryAdjusting protocol added by Apple in iOS 10.0. - - If set to YES, this button will base its text font on MDCFontTextStyleButton. - - Defaults value is NO. - */ -@property(nonatomic, readwrite, setter=mdc_setAdjustsFontForContentSizeCategory:) - BOOL mdc_adjustsFontForContentSizeCategory UI_APPEARANCE_SELECTOR; - -/** - Affects the fallback behavior for when a scaled font is not provided. - - If @c YES, the font size will adjust even if a scaled font has not been provided for - a given @c UIFont property on this component. - - If @c NO, the font size will only be adjusted if a scaled font has been provided. - - Default value is @c YES. - */ -@property(nonatomic, assign) BOOL adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable; - -/** - The shape generator used to define the button's shape. - - When the shapeGenerator is nil, MDCButton will use the default underlying layer with - its default settings. - - @note If a layer property is explicitly set after the shapeGenerator has been set, - it can lead to unexpected behavior. - - @note When @c centerVisibleArea is set to YES, this property can no longer be set. - - Default value for shapeGenerator is nil. - */ -@property(nullable, nonatomic, strong) id shapeGenerator; - -/** - A block that is invoked when the MDCButton receives a call to @c - traitCollectionDidChange:. The block is called after the call to the superclass. - */ -@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) - (MDCButton *_Nonnull button, UITraitCollection *_Nullable previousTraitCollection); - -/** - A color used as the button's @c backgroundColor for @c state. - - @param state The state. - @return The background color. - */ -- (nullable UIColor *)backgroundColorForState:(UIControlState)state; - -/** - A color used as the button's @c backgroundColor. - - If left unset or reset to nil for a given state, then a default blue color is used. - - @param backgroundColor The background color. - @param state The state. - */ -- (void)setBackgroundColor:(nullable UIColor *)backgroundColor - forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/* Convenience for `setBackgroundColor:backgroundColor forState:UIControlStateNormal`. */ -- (void)setBackgroundColor:(nullable UIColor *)backgroundColor; - -/** Sets the enabled state with optional animation. */ -- (void)setEnabled:(BOOL)enabled animated:(BOOL)animated; - -/** - Returns the elevation for a particular control state. - - The default values depend on the kind of button, for example, flat buttons in the - UIControlStateNormal state have zero elevation. - - @param state The control state to retrieve the elevation. - @return The elevation for the requested state. - */ -- (MDCShadowElevation)elevationForState:(UIControlState)state; - -/** - Sets the elevation for a particular control state. - - @param elevation The elevation to set. - @param state The state to set. - */ -- (void)setElevation:(MDCShadowElevation)elevation forState:(UIControlState)state; - -/** - A color used as the button's @c borderColor for @c state. - - @param state The state. - @return The border color. - */ -- (nullable UIColor *)borderColorForState:(UIControlState)state; - -/** - Sets the border color for a particular control state. Sets the @c borderColor of the layer. - - @param borderColor The border color to set. - @param state The state to set. - */ -- (void)setBorderColor:(nullable UIColor *)borderColor - forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - A color used as the button's imageView tint color @c imageTintColor for @c state. - - If no image tint color has been set for a given state, the returned value will fall back to the - value set for UIControlStateNormal. - - @param state The state. - @return The tint color. - */ -- (nullable UIColor *)imageTintColorForState:(UIControlState)state; - -/** - Sets the image view tint color for a particular control state. - - If left unset or reset to nil for a given state, it falls back to UIControlStateNormal setting. - - @param imageTintColor The imageView tint color to set. - @param state The state to set. - */ -- (void)setImageTintColor:(nullable UIColor *)imageTintColor forState:(UIControlState)state; - -/** - The value set for the button's @c borderWidth for @c state. - - @param state The state. - @return The border width. - */ -- (CGFloat)borderWidthForState:(UIControlState)state; - -/** - Sets the border width for a particular control state. Sets the @c borderWidth of the layer. - - @param borderWidth The border width to set. - @param state The state to set. - */ -- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Sets this button's layer's shadowColor for the specified control state. - - During initialization, the value for @c UIControlStateNormal is set to the value of this button's - layer's @c shadowColor. Providing a @c nil value for @c shadowColor will remove the shadow color - mapping for the specified state. - - If the color is not set for a specified state, the default behavior is to use the color associated - with @c UIControlStateNormal. If the color for @c UIControlStateNormal is not set, then @c nil will - be used. - - @param shadowColor The shadow color to use for the specified state. - @param state The state that uses the specified color. The possible values are described in - @c UIControlState. - */ -- (void)setShadowColor:(nullable UIColor *)shadowColor - forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - The color used as this button's layer's @c shadowColor for the specified control state. - - During initialization, the value for @c UIControlStateNormal is set to the value of this view's - layer's @c shadowColor. - - If the color is not set for a specified state, the default behavior is to use the color associated - with @c UIControlStateNormal. If the color for @c UIControlStateNormal has not been set, then - @c nil is returned. - - @param state The state that uses the shadow color. The possible values are described in - @c UIControlState. - @return The shadow color for the specified state. If no shadow color has been set for the - specific state, this method returns the shadow color associated with the - @c UIControlStateNormal state. - - @return The shadow color. - */ -- (nullable UIColor *)shadowColorForState:(UIControlState)state; - -#pragma mark - UIButton changes - -/** - From UIButton's documentation: "If you subclass UIButton, this method does not return an instance - of your subclass. If you want to create an instance of a specific subclass, you must alloc/init - the button directly." - */ -+ (nonnull instancetype)buttonWithType:(UIButtonType)buttonType NS_UNAVAILABLE; - -@end - -@interface MDCButton (ToBeDeprecated) - -/** - Enables the state-based font behavior of the receiver. - - If @c NO, then @c titleFont:forState: and @c setTitleFont:forState: have no effect. Defaults to - @c YES. - - @note This API will eventually be deprecated and removed. - */ -@property(nonatomic, assign) BOOL enableTitleFontForState; - -/** - The inset margins for the rectangle surrounding all of the button’s visual representation. - Use this property when you wish to have the touch target (frame) be larger than the - visible content. - - A positive value shrinks the visible area of the button. A negative value expands the visible area - of the button. - - The button uses this property to determine intrinsicContentSize and sizeThatFits:. - - @note This API will be deprecated and removed. Consider using @c centerVisibleArea. - - Default is UIEdgeInsetsZero. -*/ -@property(nonatomic, assign) UIEdgeInsets visibleAreaInsets; - -/** - The font used by the button's @c title. - - If left unset or reset to nil for a given state, then a default font is used. - - @param font The font. - @param state The state. - - @note This API will eventually be deprecated and removed. - */ -- (void)setTitleFont:(nullable UIFont *)font forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - The font used by the button's @c title for @c state. - - @param state The state. - @return The font. - - @note This API will eventually be deprecated and removed. - */ -- (nullable UIFont *)titleFontForState:(UIControlState)state; - -/** - If @c true, @c accessiblityTraits will always include @c UIAccessibilityTraitButton. - If @c false, @c accessibilityTraits will inherit its behavior from @c UIButton. - - @note Defaults to true. - @note This API is intended as a migration flag to restore @c UIButton behavior to @c MDCButton. In - a future version, this API will eventually be deprecated and then deleted. - */ -@property(nonatomic, assign) BOOL accessibilityTraitsIncludesButton; - -/** The ink style of the button. */ -@property(nonatomic, assign) MDCInkStyle inkStyle UI_APPEARANCE_SELECTOR; - -/** The ink color of the button. */ -@property(nonatomic, strong, null_resettable) UIColor *inkColor UI_APPEARANCE_SELECTOR; - -/* - Maximum radius of the button's ink. If the radius <= 0 then half the length of the diagonal of - self.bounds is used. This value is ignored if button's @c inkStyle is set to |MDCInkStyleBounded|. - */ -@property(nonatomic, assign) CGFloat inkMaxRippleRadius UI_APPEARANCE_SELECTOR; - -@end - -@interface MDCButton (Deprecated) - -/** - Insets to apply to the button’s hit area. - - Allows the button to detect touches outside of its bounds. A negative value indicates an - extension past the bounds. - - Default is UIEdgeInsetsZero. - */ -@property(nonatomic) UIEdgeInsets hitAreaInsets __deprecated_msg("Use centerVisibleArea instead."); - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m deleted file mode 100644 index 6bee2b3b..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCButton.m +++ /dev/null @@ -1,1452 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCButton.h" - -#import "private/MDCButton+Subclassing.h" -#import "MaterialElevation.h" -#import "MaterialInk.h" -#import "MaterialRipple.h" -#import "MaterialShadow.h" -#import "MaterialShadowElevations.h" -#import "MaterialShapeLibrary.h" -#import "MaterialShapes.h" -#import "MaterialTypography.h" -#import "MaterialMath.h" - -// TODO(ajsecord): Animate title color when animating between enabled/disabled states. -// Non-trivial: http://corecocoa.wordpress.com/2011/10/04/animatable-text-color-of-uilabel/ - -// Specified in Material Guidelines -// https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-touch-target-size -static const CGFloat MDCButtonMinimumTouchTargetHeight = 48; -static const CGFloat MDCButtonMinimumTouchTargetWidth = 48; -static const CGFloat MDCButtonDefaultCornerRadius = 2.0; -static const CGFloat kDefaultRippleAlpha = (CGFloat)0.12; - -static const NSTimeInterval MDCButtonAnimationDuration = 0.2; - -// https://material.io/go/design-buttons#buttons-main-buttons -static const CGFloat MDCButtonDisabledAlpha = (CGFloat)0.12; - -// Blue 500 from https://material.io/go/design-color-theming#color-color-palette . -static const uint32_t MDCButtonDefaultBackgroundColor = 0x191919; - -// KVO contexts -static char *const kKVOContextCornerRadius = "kKVOContextCornerRadius"; - -// Creates a UIColor from a 24-bit RGB color encoded as an integer. -static inline UIColor *MDCColorFromRGB(uint32_t rgbValue) { - return [UIColor colorWithRed:((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255 - green:((CGFloat)((rgbValue & 0x00FF00) >> 8)) / 255 - blue:((CGFloat)((rgbValue & 0x0000FF) >> 0)) / 255 - alpha:1]; -} - -// Expands size by provided edge insets. -static inline CGSize CGSizeExpandWithInsets(CGSize size, UIEdgeInsets edgeInsets) { - return CGSizeMake(size.width + edgeInsets.left + edgeInsets.right, - size.height + edgeInsets.top + edgeInsets.bottom); -} - -// Shrinks size by provided edge insets, and also standardized. -static inline CGSize CGSizeShrinkWithInsets(CGSize size, UIEdgeInsets edgeInsets) { - return CGSizeMake(MAX(0, size.width - (edgeInsets.left + edgeInsets.right)), - MAX(0, size.height - (edgeInsets.top + edgeInsets.bottom))); -} - -static NSAttributedString *UppercaseAttributedString(NSAttributedString *string) { - // Store the attributes. - NSMutableArray *attributes = [NSMutableArray array]; - [string enumerateAttributesInRange:NSMakeRange(0, [string length]) - options:0 - usingBlock:^(NSDictionary *attrs, NSRange range, __unused BOOL *stop) { - [attributes addObject:@{ - @"attrs" : attrs, - @"range" : [NSValue valueWithRange:range] - }]; - }]; - - // Make the string uppercase. - NSString *uppercaseString = [[string string] uppercaseStringWithLocale:[NSLocale currentLocale]]; - - // Apply the text and attributes to a mutable copy of the title attributed string. - NSMutableAttributedString *mutableString = [string mutableCopy]; - [mutableString replaceCharactersInRange:NSMakeRange(0, [string length]) - withString:uppercaseString]; - for (NSDictionary *attribute in attributes) { - [mutableString setAttributes:attribute[@"attrs"] range:[attribute[@"range"] rangeValue]]; - } - - return [mutableString copy]; -} - -@interface MDCButton () { - // For each UIControlState. - NSMutableDictionary *_userElevations; - NSMutableDictionary *_backgroundColors; - NSMutableDictionary *_borderColors; - NSMutableDictionary *_borderWidths; - NSMutableDictionary *_shadowColors; - NSMutableDictionary *_imageTintColors; - NSMutableDictionary *_fonts; - - CGFloat _enabledAlpha; - BOOL _hasCustomDisabledTitleColor; - BOOL _imageTintStatefulAPIEnabled; - - // Cached titles and accessibility labels. - NSMutableDictionary *_nontransformedTitles; - NSString *_accessibilityLabelExplicitValue; - - BOOL _mdc_adjustsFontForContentSizeCategory; - BOOL _cornerRadiusObserverAdded; - CGFloat _inkMaxRippleRadius; - - MDCShapeMediator *_shapedLayer; - CGFloat _currentElevation; -} -@property(nonatomic, strong, readonly, nonnull) MDCStatefulRippleView *rippleView; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -@property(nonatomic, strong) MDCInkView *inkView; -#pragma clang diagnostic pop -@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; -@property(nonatomic, assign) BOOL accessibilityTraitsIncludesButton; -@property(nonatomic, assign) BOOL enableTitleFontForState; -@property(nonatomic, assign) UIEdgeInsets visibleAreaInsets; -@property(nonatomic, strong) UIView *visibleAreaLayoutGuideView; -@property(nonatomic) UIEdgeInsets hitAreaInsets; -@property(nonatomic, assign) UIEdgeInsets currentVisibleAreaInsets; -@property(nonatomic, assign) CGSize lastRecordedIntrinsicContentSize; -@end - -@implementation MDCButton - -static BOOL gEnablePerformantShadow = NO; - -@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation; -@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock; -@synthesize visibleAreaInsets = _visibleAreaInsets; -@synthesize visibleAreaLayoutGuide = _visibleAreaLayoutGuide; -@synthesize shadowsCollection = _shadowsCollection; -@dynamic layer; - -+ (Class)layerClass { - if (gEnablePerformantShadow) { - return [super class]; - } else { - return [MDCShapedShadowLayer class]; - } -} - -- (instancetype)init { - return [self initWithFrame:CGRectZero]; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - // Set up title label attributes. - // TODO(#2709): Have a single source of truth for fonts - // Migrate to [UIFont standardFont] when possible - self.titleLabel.font = [MDCTypography buttonFont]; - - [self commonMDCButtonInit]; - [self updateBackgroundColor]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonMDCButtonInit]; - - if (self.titleLabel.font) { - _fonts = [@{} mutableCopy]; - _fonts[@(UIControlStateNormal)] = self.titleLabel.font; - } - - // Storyboards will set the backgroundColor via the UIView backgroundColor setter, so we have - // to write that in to our _backgroundColors dictionary. - _backgroundColors[@(UIControlStateNormal)] = gEnablePerformantShadow - ? _shapedLayer.shapedBackgroundColor - : self.layer.shapedBackgroundColor; - [self updateBackgroundColor]; - } - return self; -} - -- (void)commonMDCButtonInit { - // TODO(b/142861610): Default to `NO`, then remove once all internal usage is migrated. - _enableTitleFontForState = YES; - if (gEnablePerformantShadow) { - _shapedLayer = [[MDCShapeMediator alloc] initWithViewLayer:self.layer]; - } - _disabledAlpha = MDCButtonDisabledAlpha; - _enabledAlpha = self.alpha; - _uppercaseTitle = YES; - _userElevations = [NSMutableDictionary dictionary]; - _nontransformedTitles = [NSMutableDictionary dictionary]; - _borderColors = [NSMutableDictionary dictionary]; - _imageTintColors = [NSMutableDictionary dictionary]; - _borderWidths = [NSMutableDictionary dictionary]; - _fonts = [NSMutableDictionary dictionary]; - _accessibilityTraitsIncludesButton = YES; - _adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable = YES; - _mdc_overrideBaseElevation = -1; - _currentElevation = 0; - - if (!_backgroundColors) { - // _backgroundColors may have already been initialized by setting the backgroundColor setter. - _backgroundColors = [NSMutableDictionary dictionary]; - _backgroundColors[@(UIControlStateNormal)] = MDCColorFromRGB(MDCButtonDefaultBackgroundColor); - } - - // Disable default highlight state. - self.adjustsImageWhenHighlighted = NO; - -#if (!defined(TARGET_OS_TV) || TARGET_OS_TV == 0) - self.showsTouchWhenHighlighted = NO; -#endif - - self.layer.cornerRadius = MDCButtonDefaultCornerRadius; - if (gEnablePerformantShadow) { - self.layer.shadowColor = MDCShadowColor().CGColor; - } else { - self.layer.shadowColor = [UIColor blackColor].CGColor; - self.layer.elevation = [self elevationForState:self.state]; - } - - _shadowColors = [NSMutableDictionary dictionary]; - _shadowColors[@(UIControlStateNormal)] = [UIColor colorWithCGColor:self.layer.shadowColor]; - - // Set up ink layer. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - _inkView = [[MDCInkView alloc] initWithFrame:self.bounds]; -#pragma clang diagnostic pop - _inkView.usesLegacyInkRipple = NO; - [self insertSubview:_inkView belowSubview:self.imageView]; - // UIButton has a drag enter/exit boundary that is outside of the frame of the button itself. - // Because this is not exposed externally, we can't use -touchesMoved: to calculate when to - // change ink state. So instead we fall back on adding target/actions for these specific events. - [self addTarget:self - action:@selector(touchDragEnter:forEvent:) - forControlEvents:UIControlEventTouchDragEnter]; - [self addTarget:self - action:@selector(touchDragExit:forEvent:) - forControlEvents:UIControlEventTouchDragExit]; - -#if (!defined(TARGET_OS_TV) || TARGET_OS_TV == 0) - // Block users from activating multiple buttons simultaneously by default. - self.exclusiveTouch = YES; -#endif - - _inkView.inkColor = [UIColor colorWithWhite:1 alpha:(CGFloat)0.2]; - - _rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds]; - _rippleColor = [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha]; - _rippleView.rippleColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.12]; - - // Default content insets - // The default contentEdgeInsets are set here (instead of above, as they were previously) because - // of a UIButton bug introduced in the iOS 13 betas that is unresolved as of Xcode 11 beta 4 - // (b/136088498) wherein setting self.contentEdgeInsets before accessing self.imageView causes the - // imageView's bounds.origin to be set to { -(i.left + i.right), -(i.top + i.bottom) } - // This causes images created by using imageWithHorizontallyFlippedOrientation to not display. - // Images that have not been created this way seem to be fine. - // This behavior can also be seen in vanilla UIButtons by setting contentEdgeInsets to non-zero - // inset values and then setting an image created with imageWithHorizontallyFlippedOrientation. - self.contentEdgeInsets = [self defaultContentEdgeInsets]; - _minimumSize = CGSizeZero; - _maximumSize = CGSizeZero; - - // Uppercase all titles - if (_uppercaseTitle) { - [self updateTitleCase]; - } - -#ifdef __IPHONE_13_4 -#if !TARGET_OS_TV - if (@available(iOS 13.4, *)) { - if ([self respondsToSelector:@selector(pointerStyleProvider)]) { - __weak __typeof__(self) weakSelf = self; - UIButtonPointerStyleProvider buttonPointerStyleProvider = ^UIPointerStyle *( - UIButton *buttonToStyle, UIPointerEffect *proposedEffect, UIPointerShape *proposedShape) { - __typeof__(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return [UIPointerStyle styleWithEffect:proposedEffect shape:proposedShape]; - } - CGPathRef boundingCGPath = [strongSelf boundingPath].CGPath; - UIBezierPath *boundingBezierPath = [UIBezierPath bezierPathWithCGPath:boundingCGPath]; - UIPointerShape *shape = [UIPointerShape shapeWithPath:boundingBezierPath]; - return [UIPointerStyle styleWithEffect:proposedEffect shape:shape]; - }; - self.pointerStyleProvider = buttonPointerStyleProvider; - // Setting the pointerStyleProvider to a non-nil value flips pointerInteractionEnabled to YES. - // To maintain parity with UIButton's default behavior, we want it to default to NO. - self.pointerInteractionEnabled = NO; - } - } -#endif // !TARGET_OS_TV -#endif // __IPHONE_13_4 -} - -- (void)dealloc { - [self removeTarget:self action:NULL forControlEvents:UIControlEventAllEvents]; - - if (_cornerRadiusObserverAdded) { - [self.layer removeObserver:self - forKeyPath:NSStringFromSelector(@selector(cornerRadius)) - context:kKVOContextCornerRadius]; - } -} - -- (void)setUnderlyingColorHint:(UIColor *)underlyingColorHint { - _underlyingColorHint = underlyingColorHint; - [self updateAlphaAndBackgroundColorAnimated:NO]; -} - -- (void)setAlpha:(CGFloat)alpha { - _enabledAlpha = alpha; - [super setAlpha:alpha]; -} - -- (void)setDisabledAlpha:(CGFloat)disabledAlpha { - _disabledAlpha = disabledAlpha; - [self updateAlphaAndBackgroundColorAnimated:NO]; -} - -#pragma mark - UIView - -- (void)layoutSubviews { - [super layoutSubviews]; - - [self updateShadowColor]; - [self updateBackgroundColor]; - [self updateBorderColor]; - - if (self.centerVisibleArea) { - UIEdgeInsets visibleAreaInsets = self.visibleAreaInsets; - if (!UIEdgeInsetsEqualToEdgeInsets(visibleAreaInsets, self.currentVisibleAreaInsets)) { - self.currentVisibleAreaInsets = visibleAreaInsets; - MDCRectangleShapeGenerator *shapeGenerator = - [self generateShapeWithCornerRadius:self.layer.cornerRadius - visibleAreaInsets:visibleAreaInsets]; - [self configureLayerWithShapeGenerator:shapeGenerator]; - if (self.visibleAreaLayoutGuideView) { - self.visibleAreaLayoutGuideView.frame = - UIEdgeInsetsInsetRect(self.bounds, visibleAreaInsets); - } - } - } - - if (gEnablePerformantShadow) { - if (_shapedLayer.shapeGenerator) { - [_shapedLayer layoutShapedSublayers]; - } - [self updateShadow]; - } else { - if (!self.layer.shapeGenerator) { - self.layer.shadowPath = [self boundingPath].CGPath; - } - } - - // Center unbounded ink view frame taking into account possible insets using contentRectForBounds. - if (_inkView.inkStyle == MDCInkStyleUnbounded && _inkView.usesLegacyInkRipple) { - CGRect contentRect = [self contentRectForBounds:self.bounds]; - CGPoint contentCenterPoint = - CGPointMake(CGRectGetMidX(contentRect), CGRectGetMidY(contentRect)); - CGPoint boundsCenterPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); - - CGFloat offsetX = contentCenterPoint.x - boundsCenterPoint.x + self.inkViewOffset.width; - CGFloat offsetY = contentCenterPoint.y - boundsCenterPoint.y + self.inkViewOffset.height; - _inkView.frame = - CGRectMake(offsetX, offsetY, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); - } else { - CGRect bounds = CGRectStandardize(self.bounds); - bounds = CGRectOffset(bounds, self.inkViewOffset.width, self.inkViewOffset.height); - bounds = UIEdgeInsetsInsetRect(bounds, self.rippleEdgeInsets); - _inkView.frame = bounds; - self.rippleView.frame = bounds; - } - self.titleLabel.frame = MDCRectAlignToScale(self.titleLabel.frame, [UIScreen mainScreen].scale); - - if ([self shouldInferMinimumAndMaximumSize]) { - [self inferMinimumAndMaximumSize]; - } -} - -- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { - // If there are custom hitAreaInsets, use those - if (!UIEdgeInsetsEqualToEdgeInsets(self.hitAreaInsets, UIEdgeInsetsZero)) { - return CGRectContainsPoint( - UIEdgeInsetsInsetRect(CGRectStandardize(self.bounds), self.hitAreaInsets), point); - } - - // If the bounds are smaller than the minimum touch target, produce a warning once - CGFloat width = CGRectGetWidth(self.bounds); - CGFloat height = CGRectGetHeight(self.bounds); - if (width < MDCButtonMinimumTouchTargetWidth || height < MDCButtonMinimumTouchTargetHeight) { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSLog( - @"Button touch target does not meet minimum size guidelines of (%0.f, %0.f). Button: %@, " - @"Touch Target: %@", - MDCButtonMinimumTouchTargetWidth, MDCButtonMinimumTouchTargetHeight, [self description], - NSStringFromCGSize(CGSizeMake(width, height))); - }); - } - return [super pointInside:point withEvent:event]; -} - -- (void)willMoveToSuperview:(UIView *)newSuperview { - [super willMoveToSuperview:newSuperview]; - [self.inkView cancelAllAnimationsAnimated:NO]; - [self.rippleView cancelAllRipplesAnimated:NO completion:nil]; -} - -- (CGSize)sizeThatFits:(CGSize)size { - CGSize givenSizeWithInsets = CGSizeShrinkWithInsets(size, _visibleAreaInsets); - CGSize superSize = [super sizeThatFits:givenSizeWithInsets]; - - // TODO(b/171816831): revisit this in a future iOS version to verify reproducibility. - // Because of a UIKit bug in iOS 13 and 14 (current), buttons that have both an image and text - // will not return an appropriately large size from [super sizeThatFits:]. In this case, we need - // to expand the width. The number 1 was chosen somewhat arbitrarily, but based on some spot - // testing, adding the smallest amount of extra width possible seems to fix the issue. -#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) - if (@available(iOS 13.0, *)) { - if (UIAccessibilityIsBoldTextEnabled() && [self imageForState:UIControlStateNormal] && - [self titleForState:UIControlStateNormal]) { - superSize.width += 1; - } - } -#endif - - if (self.minimumSize.height > 0) { - superSize.height = MAX(self.minimumSize.height, superSize.height); - } - if (self.maximumSize.height > 0) { - superSize.height = MIN(self.maximumSize.height, superSize.height); - } - if (self.minimumSize.width > 0) { - superSize.width = MAX(self.minimumSize.width, superSize.width); - } - if (self.maximumSize.width > 0) { - superSize.width = MIN(self.maximumSize.width, superSize.width); - } - - CGSize adjustedSize = CGSizeExpandWithInsets(superSize, _visibleAreaInsets); - return adjustedSize; -} - -- (CGSize)intrinsicContentSize { - CGSize size = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - self.lastRecordedIntrinsicContentSize = size; - return size; -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - [super traitCollectionDidChange:previousTraitCollection]; - - if (self.traitCollectionDidChangeBlock) { - self.traitCollectionDidChangeBlock(self, previousTraitCollection); - } -} - -#pragma mark - UIResponder - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.enableRippleBehavior) { - [self.rippleView touchesBegan:touches withEvent:event]; - } - [super touchesBegan:touches withEvent:event]; - - if (!self.enableRippleBehavior) { - [self handleBeginTouches:touches]; - } -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.enableRippleBehavior) { - [self.rippleView touchesMoved:touches withEvent:event]; - } - [super touchesMoved:touches withEvent:event]; - - // Drag events handled by -touchDragExit:forEvent: and -touchDragEnter:forEvent: -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.enableRippleBehavior) { - [self.rippleView touchesEnded:touches withEvent:event]; - } - [super touchesEnded:touches withEvent:event]; - - if (!self.enableRippleBehavior) { - CGPoint location = [self locationFromTouches:touches]; - [_inkView startTouchEndedAnimationAtPoint:location completion:nil]; - } -} - -// Note - in some cases, event may be nil (e.g. view removed from window). -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.enableRippleBehavior) { - [self.rippleView touchesCancelled:touches withEvent:event]; - } - [super touchesCancelled:touches withEvent:event]; - - if (!self.enableRippleBehavior) { - [self evaporateInkToPoint:[self locationFromTouches:touches]]; - } -} - -#pragma mark - UIControl methods - -- (void)setEnabled:(BOOL)enabled { - [self setEnabled:enabled animated:NO]; -} - -- (void)setEnabled:(BOOL)enabled animated:(BOOL)animated { - [super setEnabled:enabled]; - - [self updateAfterStateChange:animated]; -} - -- (void)setHighlighted:(BOOL)highlighted { - [super setHighlighted:highlighted]; - - self.rippleView.rippleHighlighted = highlighted; - [self updateAfterStateChange:NO]; -} - -- (void)setSelected:(BOOL)selected { - [super setSelected:selected]; - - self.rippleView.selected = selected; - [self updateAfterStateChange:NO]; -} - -- (void)updateAfterStateChange:(BOOL)animated { - [self updateAlphaAndBackgroundColorAnimated:animated]; - [self animateButtonToHeightForState:self.state]; - [self updateBorderColor]; - [self updateBorderWidth]; - [self updateShadowColor]; - [self updateTitleFont]; - [self updateImageTintColor]; -} - -#pragma mark - Title Uppercasing - -- (void)setUppercaseTitle:(BOOL)uppercaseTitle { - _uppercaseTitle = uppercaseTitle; - - [self updateTitleCase]; -} - -- (void)updateTitleCase { - // This calls setTitle or setAttributedTitle for every title value we have stored. In each - // respective setter the title is upcased if _uppercaseTitle is YES. - NSDictionary *nontransformedTitles = [_nontransformedTitles copy]; - for (NSNumber *key in nontransformedTitles.keyEnumerator) { - UIControlState state = key.unsignedIntegerValue; - NSString *title = nontransformedTitles[key]; - if ([title isKindOfClass:[NSAttributedString class]]) { - [self setAttributedTitle:(NSAttributedString *)title forState:state]; - } else if ([title isKindOfClass:[NSString class]]) { - [self setTitle:title forState:state]; - } - } -} - -- (void)updateShadowColor { - self.layer.shadowColor = [self shadowColorForState:self.state].CGColor; -} - -- (void)setShadowColor:(UIColor *)shadowColor forState:(UIControlState)state { - if (shadowColor) { - _shadowColors[@(state)] = shadowColor; - } else { - [_shadowColors removeObjectForKey:@(state)]; - } - - if (state == self.state) { - [self updateShadowColor]; - } -} - -- (UIColor *)shadowColorForState:(UIControlState)state { - UIColor *shadowColor = _shadowColors[@(state)]; - if (state != UIControlStateNormal && !shadowColor) { - shadowColor = _shadowColors[@(UIControlStateNormal)]; - } - return shadowColor; -} - -- (void)setTitle:(NSString *)title forState:(UIControlState)state { - // Intercept any setting of the title and store a copy in case the accessibilityLabel - // is requested and the original non-uppercased version needs to be returned. - if ([title length]) { - _nontransformedTitles[@(state)] = [title copy]; - } else { - [_nontransformedTitles removeObjectForKey:@(state)]; - } - - if (_uppercaseTitle) { - title = [title uppercaseStringWithLocale:[NSLocale currentLocale]]; - } - [super setTitle:title forState:state]; -} - -- (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state { - // Intercept any setting of the title and store a copy in case the accessibilityLabel - // is requested and the original non-uppercased version needs to be returned. - if ([title length]) { - _nontransformedTitles[@(state)] = [title copy]; - } else { - [_nontransformedTitles removeObjectForKey:@(state)]; - } - - if (_uppercaseTitle) { - title = UppercaseAttributedString(title); - } - [super setAttributedTitle:title forState:state]; -} - -#pragma mark - Accessibility - -- (void)setAccessibilityLabel:(NSString *)accessibilityLabel { - // Intercept any explicit setting of the accessibilityLabel so it can be returned - // later before the accessibiilityLabel is inferred from the setTitle:forState: - // argument values. - _accessibilityLabelExplicitValue = [accessibilityLabel copy]; - [super setAccessibilityLabel:accessibilityLabel]; -} - -- (NSString *)accessibilityLabel { - if (!_uppercaseTitle) { - return [super accessibilityLabel]; - } - - if ([_accessibilityLabelExplicitValue length]) { - return _accessibilityLabelExplicitValue; - } - - NSString *titleLabel; - id stateTitle = _nontransformedTitles[@(self.state)]; - if ([stateTitle isKindOfClass:[NSAttributedString class]]) { - titleLabel = [(NSAttributedString *)stateTitle string]; - } else if ([stateTitle isKindOfClass:[NSString class]]) { - titleLabel = stateTitle; - } - if ([titleLabel length]) { - return titleLabel; - } - - id normalTitle = _nontransformedTitles[@(UIControlStateNormal)]; - if ([normalTitle isKindOfClass:[NSAttributedString class]]) { - titleLabel = [(NSAttributedString *)normalTitle string]; - } else if ([normalTitle isKindOfClass:[NSString class]]) { - titleLabel = normalTitle; - } - if ([titleLabel length]) { - return titleLabel; - } - - NSString *label = [super accessibilityLabel]; - if ([label length]) { - return label; - } - - return nil; -} - -- (UIAccessibilityTraits)accessibilityTraits { - if (self.accessibilityTraitsIncludesButton) { - return [super accessibilityTraits] | UIAccessibilityTraitButton; - } - return [super accessibilityTraits]; -} - -#pragma mark - Ink - -- (MDCInkStyle)inkStyle { - return _inkView.inkStyle; -} - -- (void)setInkStyle:(MDCInkStyle)inkStyle { - _inkView.inkStyle = inkStyle; - self.rippleView.rippleStyle = - (inkStyle == MDCInkStyleUnbounded) ? MDCRippleStyleUnbounded : MDCRippleStyleBounded; -} - -- (void)setRippleStyle:(MDCRippleStyle)rippleStyle { - _rippleStyle = rippleStyle; - - self.rippleView.rippleStyle = rippleStyle; -} - -- (UIColor *)inkColor { - return _inkView.inkColor; -} - -- (void)setInkColor:(UIColor *)inkColor { - _inkView.inkColor = inkColor; - [self.rippleView setRippleColor:inkColor forState:MDCRippleStateHighlighted]; -} - -- (void)setRippleColor:(UIColor *)rippleColor { - _rippleColor = rippleColor ?: [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha]; - [self.rippleView setRippleColor:_rippleColor forState:MDCRippleStateHighlighted]; -} - -- (CGFloat)inkMaxRippleRadius { - return _inkMaxRippleRadius; -} - -- (void)setInkMaxRippleRadius:(CGFloat)inkMaxRippleRadius { - _inkMaxRippleRadius = inkMaxRippleRadius; - _inkView.maxRippleRadius = inkMaxRippleRadius; - self.rippleView.maximumRadius = inkMaxRippleRadius; -} - -- (void)setRippleMaximumRadius:(CGFloat)rippleMaximumRadius { - _rippleMaximumRadius = rippleMaximumRadius; - - self.rippleView.maximumRadius = rippleMaximumRadius; -} - -- (void)setInkViewOffset:(CGSize)inkViewOffset { - _inkViewOffset = inkViewOffset; - [self setNeedsLayout]; -} - -- (void)setRippleEdgeInsets:(UIEdgeInsets)rippleEdgeInsets { - _rippleEdgeInsets = rippleEdgeInsets; - - [self setNeedsLayout]; -} - -- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior { - _enableRippleBehavior = enableRippleBehavior; - - if (enableRippleBehavior) { - [self.inkView removeFromSuperview]; - [self insertSubview:self.rippleView belowSubview:self.imageView]; - } else { - [self.rippleView removeFromSuperview]; - [self insertSubview:self.inkView belowSubview:self.imageView]; - } -} - -#pragma mark - Shadows - -- (void)animateButtonToHeightForState:(UIControlState)state { - CGFloat newElevation = [self elevationForState:state]; - if (MDCCGFloatEqual(self.mdc_currentElevation, newElevation)) { - return; - } - _currentElevation = newElevation; - [CATransaction begin]; - [CATransaction setAnimationDuration:MDCButtonAnimationDuration]; - if (gEnablePerformantShadow) { - [self updateShadow]; - } else { - self.layer.elevation = newElevation; - } - [CATransaction commit]; - [self mdc_elevationDidChange]; -} - -#pragma mark - BackgroundColor - -- (void)setBackgroundColor:(nullable UIColor *)backgroundColor { - // Since setBackgroundColor can be called in the initializer we need to optionally build the dict. - if (!_backgroundColors) { - _backgroundColors = [NSMutableDictionary dictionary]; - } - _backgroundColors[@(UIControlStateNormal)] = backgroundColor; - [self updateBackgroundColor]; -} - -- (UIColor *)backgroundColor { - return gEnablePerformantShadow ? _shapedLayer.shapedBackgroundColor - : self.layer.shapedBackgroundColor; -} - -- (UIColor *)backgroundColorForState:(UIControlState)state { - // If the `.highlighted` flag is set, turn off the `.disabled` flag - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - state = state & ~UIControlStateDisabled; - } - - return _backgroundColors[@(state)] ?: _backgroundColors[@(UIControlStateNormal)]; -} - -- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state { - UIControlState storageState = state; - // If the `.highlighted` flag is set, turn off the `.disabled` flag - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - storageState = state & ~UIControlStateDisabled; - } - - // Only update the backing dictionary if: - // 1. The `state` argument is the same as the "storage" state, OR - // 2. There is already a value in the "storage" state. - if (storageState == state || _backgroundColors[@(storageState)] != nil) { - _backgroundColors[@(storageState)] = backgroundColor; - [self updateAlphaAndBackgroundColorAnimated:NO]; - } -} - -#pragma mark - Image Tint Color - -- (nullable UIColor *)imageTintColorForState:(UIControlState)state { - return _imageTintColors[@(state)] ?: _imageTintColors[@(UIControlStateNormal)]; -} - -- (void)setImageTintColor:(nullable UIColor *)imageTintColor forState:(UIControlState)state { - _imageTintColors[@(state)] = imageTintColor; - _imageTintStatefulAPIEnabled = YES; - [self updateImageTintColor]; -} - -- (void)updateImageTintColor { - if (!_imageTintStatefulAPIEnabled) { - return; - } - self.imageView.tintColor = [self imageTintColorForState:self.state]; -} - -#pragma mark - Elevations - -- (CGFloat)elevationForState:(UIControlState)state { - NSNumber *elevation = _userElevations[@(state)]; - if (state != UIControlStateNormal && (elevation == nil)) { - elevation = _userElevations[@(UIControlStateNormal)]; - } - if (elevation != nil) { - return (CGFloat)[elevation doubleValue]; - } - return 0; -} - -- (void)setElevation:(CGFloat)elevation forState:(UIControlState)state { - _userElevations[@(state)] = @(elevation); - MDCShadowElevation newElevation = [self elevationForState:self.state]; - // If no change to the current elevation, don't perform updates - if (MDCCGFloatEqual(newElevation, self.mdc_currentElevation)) { - return; - } - _currentElevation = newElevation; - if (gEnablePerformantShadow) { - [self updateShadow]; - } else { - self.layer.elevation = newElevation; - } - [self mdc_elevationDidChange]; - - // The elevation of the normal state controls whether this button is flat or not, and flat buttons - // have different background color requirements than raised buttons. - // TODO(ajsecord): Move to MDCFlatButton and update this comment. - if (state == UIControlStateNormal) { - [self updateAlphaAndBackgroundColorAnimated:NO]; - } -} - -- (void)updateShadow { - if (_shapedLayer.shapeGenerator == nil) { - MDCConfigureShadowForView(self, - [self.shadowsCollection shadowForElevation:self.mdc_currentElevation], - [self shadowColorForState:self.state] ?: MDCShadowColor()); - } else { - MDCConfigureShadowForViewWithPath( - self, [self.shadowsCollection shadowForElevation:self.mdc_currentElevation], - [self shadowColorForState:self.state] ?: MDCShadowColor(), self.layer.shadowPath); - } -} - -#pragma mark - Border Color - -- (UIColor *)borderColorForState:(UIControlState)state { - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - state = state & ~UIControlStateDisabled; - } - return _borderColors[@(state)] ?: _borderColors[@(UIControlStateNormal)]; -} - -- (void)setBorderColor:(UIColor *)borderColor forState:(UIControlState)state { - UIControlState storageState = state; - // If the `.highlighted` flag is set, turn off the `.disabled` flag - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - storageState = state & ~UIControlStateDisabled; - } - - // Only update the backing dictionary if: - // 1. The `state` argument is the same as the "storage" state, OR - // 2. There is already a value in the "storage" state. - if (storageState == state || _borderColors[@(storageState)] != nil) { - _borderColors[@(storageState)] = borderColor; - [self updateBorderColor]; - } -} - -#pragma mark - Border Width - -- (CGFloat)borderWidthForState:(UIControlState)state { - // If the `.highlighted` flag is set, turn off the `.disabled` flag - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - state = state & ~UIControlStateDisabled; - } - NSNumber *borderWidth = _borderWidths[@(state)]; - if (borderWidth != nil) { - return (CGFloat)borderWidth.doubleValue; - } - return (CGFloat)[_borderWidths[@(UIControlStateNormal)] doubleValue]; -} - -- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state { - UIControlState storageState = state; - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - storageState = state & ~UIControlStateDisabled; - } - // Only update the backing dictionary if: - // 1. The `state` argument is the same as the "storage" state, OR - // 2. There is already a value in the "storage" state. - if (storageState == state || _backgroundColors[@(storageState)] != nil) { - _borderWidths[@(state)] = @(borderWidth); - [self updateBorderWidth]; - } -} - -- (void)updateBorderWidth { - NSNumber *width = _borderWidths[@(self.state)]; - if ((width == nil) && self.state != UIControlStateNormal) { - // We fall back to UIControlStateNormal if there is no value for the current state. - width = _borderWidths[@(UIControlStateNormal)]; - } - if (gEnablePerformantShadow) { - _shapedLayer.shapedBorderWidth = (width != nil) ? (CGFloat)width.doubleValue : 0; - } else { - self.layer.shapedBorderWidth = (width != nil) ? (CGFloat)width.doubleValue : 0; - } -} - -#pragma mark - Title Font - -- (nullable UIFont *)titleFontForState:(UIControlState)state { - // If the `.highlighted` flag is set, turn off the `.disabled` flag - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - state = state & ~UIControlStateDisabled; - } - UIFont *font = _fonts[@(state)] ?: _fonts[@(UIControlStateNormal)]; - - if (!font) { - // TODO(#2709): Have a single source of truth for fonts - // Migrate to [UIFont standardFont] when possible - font = [MDCTypography buttonFont]; - } - - if (_mdc_adjustsFontForContentSizeCategory) { - // Dynamic type is enabled so apply scaling - if (font.mdc_scalingCurve) { - font = [font mdc_scaledFontForTraitEnvironment:self]; - } else { - if (self.adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable) { - font = [font mdc_fontSizedForMaterialTextStyle:MDCFontTextStyleButton - scaledForDynamicType:YES]; - } - } - } - return font; -} - -- (void)setTitleFont:(nullable UIFont *)font forState:(UIControlState)state { - UIControlState storageState = state; - // If the `.highlighted` flag is set, turn off the `.disabled` flag - if ((state & UIControlStateHighlighted) == UIControlStateHighlighted) { - storageState = state & ~UIControlStateDisabled; - } - - // Only update the backing dictionary if: - // 1. The `state` argument is the same as the "storage" state, OR - // 2. There is already a value in the "storage" state. - if (storageState == state || _fonts[@(storageState)] != nil) { - _fonts[@(storageState)] = font; - [self updateTitleFont]; - } -} - -#pragma mark - MaterialElevation - -- (CGFloat)mdc_currentElevation { - return _currentElevation; -} - -- (MDCShadowsCollection *)shadowsCollection { - if (!_shadowsCollection) { - _shadowsCollection = MDCShadowsCollectionDefault(); - } - return _shadowsCollection; -} - -#pragma mark - Private methods - -/** - The background color that a user would see for this button. If self.backgroundColor is not - transparent, then returns that. Otherwise, returns self.underlyingColorHint. - @note If self.underlyingColorHint is not set, then this method will return nil. - */ -- (UIColor *)effectiveBackgroundColor { - UIColor *backgroundColor = [self backgroundColorForState:self.state]; - if (![self isTransparentColor:backgroundColor]) { - return backgroundColor; - } else { - return self.underlyingColorHint; - } -} - -/** Returns YES if the color is not transparent and is a "dark" color. */ -- (BOOL)isDarkColor:(UIColor *)color { - // TODO: have a components/private/ColorCalculations/MDCColorCalculations.h|m - // return ![self isTransparentColor:color] && [QTMColorGroup luminanceOfColor:color] < 0.5; - return ![self isTransparentColor:color]; -} - -/** Returns YES if the color is transparent (including a nil color). */ -- (BOOL)isTransparentColor:(UIColor *)color { - return !color || [color isEqual:[UIColor clearColor]] || CGColorGetAlpha(color.CGColor) == 0; -} - -- (void)touchDragEnter:(__unused MDCButton *)button forEvent:(UIEvent *)event { - [self handleBeginTouches:event.allTouches]; -} - -- (void)touchDragExit:(__unused MDCButton *)button forEvent:(UIEvent *)event { - CGPoint location = [self locationFromTouches:event.allTouches]; - [self evaporateInkToPoint:location]; -} - -- (void)handleBeginTouches:(NSSet *)touches { - [_inkView startTouchBeganAnimationAtPoint:[self locationFromTouches:touches] completion:nil]; -} - -- (CGPoint)locationFromTouches:(NSSet *)touches { - UITouch *touch = [touches anyObject]; - return [touch locationInView:self]; -} - -- (void)evaporateInkToPoint:(CGPoint)toPoint { - [_inkView startTouchEndedAnimationAtPoint:toPoint completion:nil]; -} - -- (UIBezierPath *)boundingPath { - return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.layer.cornerRadius]; -} - -- (UIEdgeInsets)defaultContentEdgeInsets { - return UIEdgeInsetsMake(8, 16, 8, 16); -} - -- (BOOL)shouldHaveOpaqueBackground { - BOOL isFlatButton = MDCCGFloatIsExactlyZero([self elevationForState:UIControlStateNormal]); - return !isFlatButton; -} - -- (void)updateAlphaAndBackgroundColorAnimated:(BOOL)animated { - void (^animations)(void) = ^{ - // Set super to avoid overwriting _enabledAlpha - super.alpha = self.enabled ? self->_enabledAlpha : self.disabledAlpha; - [self updateBackgroundColor]; - }; - - if (animated) { - [UIView animateWithDuration:MDCButtonAnimationDuration animations:animations]; - } else { - animations(); - } -} - -- (void)updateBackgroundColor { - // When shapeGenerator is unset then self.layer.shapedBackgroundColor sets the layer's - // backgroundColor. Whereas when shapeGenerator is set the sublayer's fillColor is set. - if (gEnablePerformantShadow) { - _shapedLayer.shapedBackgroundColor = [self backgroundColorForState:self.state]; - } else { - self.layer.shapedBackgroundColor = [self backgroundColorForState:self.state]; - } - [self updateDisabledTitleColor]; -} - -- (void)updateDisabledTitleColor { - // We only want to automatically set a disabled title color if the user hasn't already provided a - // value. - if (_hasCustomDisabledTitleColor) { - return; - } - // Disabled buttons have very low opacity, so we full-opacity text color here to make the text - // readable. Also, even for non-flat buttons with opaque backgrounds, the correct background color - // to examine is the underlying color, since disabled buttons are so transparent. - BOOL darkBackground = [self isDarkColor:[self underlyingColorHint]]; - // We call super here to distinguish between automatic title color assignments and that of users. - [super setTitleColor:darkBackground ? [UIColor whiteColor] : [UIColor blackColor] - forState:UIControlStateDisabled]; -} - -- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { - [super setTitleColor:color forState:state]; - if (state == UIControlStateDisabled) { - _hasCustomDisabledTitleColor = color != nil; - if (!_hasCustomDisabledTitleColor) { - [self updateDisabledTitleColor]; - } - } -} - -- (void)updateBorderColor { - UIColor *color = _borderColors[@(self.state)]; - if (!color && self.state != UIControlStateNormal) { - // We fall back to UIControlStateNormal if there is no value for the current state. - color = _borderColors[@(UIControlStateNormal)]; - } - if (gEnablePerformantShadow) { - _shapedLayer.shapedBorderColor = color ?: NULL; - } else { - self.layer.shapedBorderColor = color ?: NULL; - } -} - -- (void)updateTitleFont { - if (!self.enableTitleFontForState) { - return; - } - - self.titleLabel.font = [self titleFontForState:self.state]; - - [self setNeedsLayout]; -} - -- (void)setShapeGenerator:(id)shapeGenerator { - if (!UIEdgeInsetsEqualToEdgeInsets(_visibleAreaInsets, UIEdgeInsetsZero) || - self.centerVisibleArea) { - // When visibleAreaInsets or centerVisibleArea is set, custom shapeGenerater is not allow - // to be set through setter. - return; - } - - [self configureLayerWithShapeGenerator:shapeGenerator]; -} - -- (void)configureLayerWithShapeGenerator:(id)shapeGenerator { - if (shapeGenerator) { - self.layer.shadowPath = nil; - } else { - if (gEnablePerformantShadow) { - MDCConfigureShadowForView( - self, [self.shadowsCollection shadowForElevation:self.mdc_currentElevation], - [self shadowColorForState:self.state] ?: MDCShadowColor()); - } else { - self.layer.shadowPath = [self boundingPath].CGPath; - } - } - - if (gEnablePerformantShadow) { - _shapedLayer.shapeGenerator = shapeGenerator; - } else { - self.layer.shapeGenerator = shapeGenerator; - } - // The imageView is added very early in the lifecycle of a UIButton, therefore we need to move - // the colorLayer behind the imageView otherwise the image will not show. - // Because the inkView needs to go below the imageView, but above the colorLayer - // we need to have the colorLayer be at the back - if (gEnablePerformantShadow) { - [_shapedLayer.colorLayer removeFromSuperlayer]; - if (self.enableRippleBehavior) { - [self.layer insertSublayer:_shapedLayer.colorLayer below:self.rippleView.layer]; - } else { - [self.layer insertSublayer:_shapedLayer.colorLayer below:self.inkView.layer]; - } - } else { - [self.layer.colorLayer removeFromSuperlayer]; - if (self.enableRippleBehavior) { - [self.layer insertSublayer:self.layer.colorLayer below:self.rippleView.layer]; - } else { - [self.layer insertSublayer:self.layer.colorLayer below:self.inkView.layer]; - } - } - [self updateBackgroundColor]; - [self updateInkForShape]; -} - -- (id)shapeGenerator { - if (gEnablePerformantShadow) { - return _shapedLayer.shapeGenerator; - } - return self.layer.shapeGenerator; -} - -- (void)updateInkForShape { - CGRect boundingBox = CGPathGetBoundingBox(gEnablePerformantShadow ? _shapedLayer.shapeLayer.path - : self.layer.shapeLayer.path); - self.inkView.maxRippleRadius = - (CGFloat)(hypot(CGRectGetHeight(boundingBox), CGRectGetWidth(boundingBox)) / 2 + 10); - self.inkView.layer.masksToBounds = NO; - self.rippleView.layer.masksToBounds = NO; -} - -#pragma mark - Dynamic Type - -- (BOOL)mdc_adjustsFontForContentSizeCategory { - return _mdc_adjustsFontForContentSizeCategory; -} - -- (void)mdc_setAdjustsFontForContentSizeCategory:(BOOL)adjusts { - _mdc_adjustsFontForContentSizeCategory = adjusts; - if (_mdc_adjustsFontForContentSizeCategory) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(contentSizeCategoryDidChange:) - name:UIContentSizeCategoryDidChangeNotification - object:nil]; - } else { - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIContentSizeCategoryDidChangeNotification - object:nil]; - } - - [self updateTitleFont]; -} - -- (void)contentSizeCategoryDidChange:(__unused NSNotification *)notification { - [self updateTitleFont]; - - [self sizeToFit]; -} - -#pragma mark - Deprecations - -- (void)setUnderlyingColor:(UIColor *)underlyingColor { - [self setUnderlyingColorHint:underlyingColor]; -} - -#pragma mark - Visible area - -- (void)setCenterVisibleArea:(BOOL)centerVisibleArea { - if (_centerVisibleArea == centerVisibleArea) { - return; - } - - _centerVisibleArea = centerVisibleArea; - - if (!centerVisibleArea && UIEdgeInsetsEqualToEdgeInsets(_visibleAreaInsets, UIEdgeInsetsZero)) { - self.shapeGenerator = nil; - - if (_cornerRadiusObserverAdded) { - [self.layer removeObserver:self - forKeyPath:NSStringFromSelector(@selector(cornerRadius)) - context:kKVOContextCornerRadius]; - _cornerRadiusObserverAdded = NO; - } - } else { - UIEdgeInsets visibleAreaInsets = self.visibleAreaInsets; - MDCRectangleShapeGenerator *shapeGenerator = - [self generateShapeWithCornerRadius:self.layer.cornerRadius - visibleAreaInsets:visibleAreaInsets]; - [self configureLayerWithShapeGenerator:shapeGenerator]; - - if (!_cornerRadiusObserverAdded) { - [self.layer addObserver:self - forKeyPath:NSStringFromSelector(@selector(cornerRadius)) - options:NSKeyValueObservingOptionNew - context:kKVOContextCornerRadius]; - _cornerRadiusObserverAdded = YES; - } - } -} - -- (void)setVisibleAreaInsets:(UIEdgeInsets)visibleAreaInsets { - if (UIEdgeInsetsEqualToEdgeInsets(visibleAreaInsets, _visibleAreaInsets)) { - return; - } - - _visibleAreaInsets = visibleAreaInsets; - - if (UIEdgeInsetsEqualToEdgeInsets(visibleAreaInsets, UIEdgeInsetsZero) && - !self.centerVisibleArea) { - self.shapeGenerator = nil; - - if (_cornerRadiusObserverAdded) { - [self.layer removeObserver:self - forKeyPath:NSStringFromSelector(@selector(cornerRadius)) - context:kKVOContextCornerRadius]; - _cornerRadiusObserverAdded = NO; - } - } else { - MDCRectangleShapeGenerator *shapeGenerator = - [self generateShapeWithCornerRadius:self.layer.cornerRadius - visibleAreaInsets:visibleAreaInsets]; - [self configureLayerWithShapeGenerator:shapeGenerator]; - - if (!_cornerRadiusObserverAdded) { - [self.layer addObserver:self - forKeyPath:NSStringFromSelector(@selector(cornerRadius)) - options:NSKeyValueObservingOptionNew - context:kKVOContextCornerRadius]; - _cornerRadiusObserverAdded = YES; - } - } -} - -- (UIEdgeInsets)visibleAreaInsets { - if (!UIEdgeInsetsEqualToEdgeInsets(_visibleAreaInsets, UIEdgeInsetsZero)) { - // Use custom visibleAreaInsets value when user sets it. - return _visibleAreaInsets; - } - - UIEdgeInsets visibleAreaInsets = UIEdgeInsetsZero; - if (self.centerVisibleArea) { - CGSize visibleAreaSize = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - CGFloat additionalRequiredHeight = - MAX(0, CGRectGetHeight(self.bounds) - visibleAreaSize.height); - CGFloat additionalRequiredWidth = MAX(0, CGRectGetWidth(self.bounds) - visibleAreaSize.width); - visibleAreaInsets.top = ceil(additionalRequiredHeight * 0.5f); - visibleAreaInsets.bottom = additionalRequiredHeight - visibleAreaInsets.top; - visibleAreaInsets.left = ceil(additionalRequiredWidth * 0.5f); - visibleAreaInsets.right = additionalRequiredWidth - visibleAreaInsets.left; - } - - return visibleAreaInsets; -} - -- (UILayoutGuide *)visibleAreaLayoutGuide { - if (!_visibleAreaLayoutGuide) { - _visibleAreaLayoutGuide = [[UILayoutGuide alloc] init]; - [self addLayoutGuide:_visibleAreaLayoutGuide]; - _visibleAreaLayoutGuideView = [[UIView alloc] init]; - _visibleAreaLayoutGuideView.userInteractionEnabled = NO; - [self insertSubview:_visibleAreaLayoutGuideView atIndex:0]; - self.visibleAreaLayoutGuideView.frame = - UIEdgeInsetsInsetRect(self.bounds, self.visibleAreaInsets); - - [_visibleAreaLayoutGuide.leftAnchor - constraintEqualToAnchor:_visibleAreaLayoutGuideView.leftAnchor] - .active = YES; - [_visibleAreaLayoutGuide.rightAnchor - constraintEqualToAnchor:_visibleAreaLayoutGuideView.rightAnchor] - .active = YES; - [_visibleAreaLayoutGuide.topAnchor - constraintEqualToAnchor:_visibleAreaLayoutGuideView.topAnchor] - .active = YES; - [_visibleAreaLayoutGuide.bottomAnchor - constraintEqualToAnchor:_visibleAreaLayoutGuideView.bottomAnchor] - .active = YES; - } - return _visibleAreaLayoutGuide; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context { - if (context == kKVOContextCornerRadius) { - if ((!UIEdgeInsetsEqualToEdgeInsets(self.visibleAreaInsets, UIEdgeInsetsZero) || - self.centerVisibleArea) && - self.shapeGenerator) { - MDCRectangleShapeGenerator *shapeGenerator = - [self generateShapeWithCornerRadius:self.layer.cornerRadius - visibleAreaInsets:self.visibleAreaInsets]; - [self configureLayerWithShapeGenerator:shapeGenerator]; - } - } else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - -- (MDCRectangleShapeGenerator *)generateShapeWithCornerRadius:(CGFloat)cornerRadius - visibleAreaInsets:(UIEdgeInsets)visibleAreaInsets { - MDCRectangleShapeGenerator *shapeGenerator = [[MDCRectangleShapeGenerator alloc] init]; - MDCCornerTreatment *cornerTreatment = - [[MDCRoundedCornerTreatment alloc] initWithRadius:cornerRadius]; - [shapeGenerator setCorners:cornerTreatment]; - shapeGenerator.topLeftCornerOffset = CGPointMake(visibleAreaInsets.left, visibleAreaInsets.top); - shapeGenerator.topRightCornerOffset = - CGPointMake(-visibleAreaInsets.right, visibleAreaInsets.top); - shapeGenerator.bottomLeftCornerOffset = - CGPointMake(visibleAreaInsets.left, -visibleAreaInsets.bottom); - shapeGenerator.bottomRightCornerOffset = - CGPointMake(-visibleAreaInsets.right, -visibleAreaInsets.bottom); - return shapeGenerator; -} - -#pragma mark Multi-line Minimum And Maximum Sizing Inference - -- (BOOL)shouldInferMinimumAndMaximumSize { - return (self.inferMinimumAndMaximumSizeWhenMultiline && self.titleLabel.numberOfLines != 1 && - self.titleLabel.text.length > 0); -} - -- (void)setInferMinimumAndMaximumSizeWhenMultiline:(BOOL)inferMinimumAndMaximumSizeWhenMultiline { - _inferMinimumAndMaximumSizeWhenMultiline = inferMinimumAndMaximumSizeWhenMultiline; - if (!_inferMinimumAndMaximumSizeWhenMultiline) { - self.minimumSize = CGSizeZero; - self.maximumSize = CGSizeZero; - } - [self setNeedsLayout]; - // Call -layoutIfNeeded in addition to -setNeedsLayout. If in a Manual Layout environment, the - // client will probably call -sizeToFit: after enabling this flag, like the docs suggest. We want - // the pending layout pass to happen before they are able to do that, so minimumSize and - // maximumSize are already set when it happens. - [self layoutIfNeeded]; -} - -- (void)inferMinimumAndMaximumSize { - CGSize buttonSize = self.bounds.size; - CGSize sizeShrunkFromVisibleAreaInsets = - CGSizeShrinkWithInsets(buttonSize, self.visibleAreaInsets); - CGSize sizeShrunkFromContentEdgeInsets = - CGSizeShrinkWithInsets(sizeShrunkFromVisibleAreaInsets, self.contentEdgeInsets); - CGSize boundingSizeForLabel = sizeShrunkFromContentEdgeInsets; - if ([self imageForState:self.state]) { - boundingSizeForLabel.width -= CGRectGetWidth(self.imageView.frame); - } - boundingSizeForLabel.height = CGFLOAT_MAX; - CGSize sizeThatFitsLabel = [self.titleLabel sizeThatFits:boundingSizeForLabel]; - CGSize sizeShrunkFromContentEdgeInsetsWithNewHeight = - CGSizeMake(sizeShrunkFromContentEdgeInsets.width, sizeThatFitsLabel.height); - CGSize sizeExpandedFromContentEdgeInsets = - CGSizeExpandWithInsets(sizeShrunkFromContentEdgeInsetsWithNewHeight, self.contentEdgeInsets); - CGSize sizeExpandedFromVisibleAreaInsets = - CGSizeExpandWithInsets(sizeExpandedFromContentEdgeInsets, self.visibleAreaInsets); - self.minimumSize = sizeExpandedFromVisibleAreaInsets; - self.maximumSize = sizeExpandedFromVisibleAreaInsets; - - if (!CGSizeEqualToSize(sizeExpandedFromVisibleAreaInsets, - self.lastRecordedIntrinsicContentSize)) { - [self invalidateIntrinsicContentSize]; - } -} - -#pragma mark - Performant Shadow Toggle - -+ (void)setEnablePerformantShadow:(BOOL)enable { - gEnablePerformantShadow = enable; -} - -+ (BOOL)enablePerformantShadow { - return gEnablePerformantShadow; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h deleted file mode 100644 index 09197e13..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCButton.h" - -#pragma mark - Soon to be deprecated - -/** - A "flat" MDCButton. - - Flat buttons should be considered the default button. They do not have their own background color, - do not raise when touched, and have uppercased text to indicate to the user that they are buttons. - Flat buttons should be used in most situations requiring a button. For layouts with many UI - elements in which a flat button might get visually lost, consider using a MDCRaisedButton instead. - - @warning This class will be deprecated soon. Consider using @c MDCTextButtonThemer with an - @c MDCButton instead. - - @see https://material.io/go/design-buttons#buttons-flat-buttons - */ -@interface MDCFlatButton : MDCButton - -@end - -@interface MDCFlatButton (ToBeDeprecated) - -/** - Use an opaque background color (default is NO). - - Flat buttons normally have a transparent background and blend seamlessly with their underlying - color, but occasionally a flat button with an opaque background will be required. Consider using - a raised button instead if possible. - */ -@property(nonatomic) BOOL hasOpaqueBackground; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m deleted file mode 100644 index d979cae4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFlatButton.m +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFlatButton.h" - -#import "private/MDCButton+Subclassing.h" -#import "MDCButton.h" -#import "MaterialShadowElevations.h" - -@interface MDCFlatButton () -@property(nonatomic) BOOL hasOpaqueBackground; -@end - -@implementation MDCFlatButton - -+ (void)initialize { - // Default background colors. - [MDCFlatButton.appearance setBackgroundColor:[UIColor clearColor] forState:UIControlStateNormal]; - [MDCFlatButton.appearance setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - [MDCFlatButton.appearance setElevation:MDCShadowElevationNone forState:UIControlStateNormal]; - [MDCFlatButton.appearance setElevation:MDCShadowElevationNone forState:UIControlStateHighlighted]; - MDCFlatButton.appearance.inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.06]; -} - -#pragma mark - MDCButton Subclassing - -- (void)setHasOpaqueBackground:(BOOL)hasOpaqueBackground { - _hasOpaqueBackground = hasOpaqueBackground; - [self updateBackgroundColor]; -} - -- (BOOL)shouldHaveOpaqueBackground { - return [super shouldHaveOpaqueBackground] || self.hasOpaqueBackground; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h deleted file mode 100644 index 8b7a360e..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFloatingButton.h" - -/** - This category is used to animate @c MDCFloatingButton instances, to expand or - collapse. - */ -@interface MDCFloatingButton (Animation) - -/** - Expand this button to its unscaled (normal) size. - - @param animated YES if the size change should be animated. - @param completion a completion block to call after the size change is complete. - - @note This method will modify the transform property of the button. Apple's documentation about - UIView frames and transforms states that whenever the transform is not the identity - transform, the frame is undefined and should be ignored. - https://developer.apple.com/documentation/uikit/uiview/1622621-frame - */ -- (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion; - -/** - Collapses this button so that it becomes smaller than 0.1% of its normal size. - - @param animated YES if the size change should be animated. - @param completion a completion block to call after the size change is complete. - - @note This method will modify the transform property of the button. Apple's documentation about - UIView frames and transforms states that whenever the transform is not the identity - transform, the frame is undefined and should be ignored. - https://developer.apple.com/documentation/uikit/uiview/1622621-frame - */ -- (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m deleted file mode 100644 index 0aedcdf5..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton+Animation.m +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFloatingButton+Animation.h" -#import "MDCFloatingButton.h" - -#if TARGET_IPHONE_SIMULATOR -float UIAnimationDragCoefficient(void); // Private API for simulator animation speed -#endif - -static NSString *const kMDCFloatingButtonTransformKey = @"kMDCFloatingButtonTransformKey"; -static NSString *const kMDCFloatingButtonOpacityKey = @"kMDCFloatingButtonOpacityKey"; - -// By using a power of 2 (2^-12), we can reduce rounding errors during transform multiplication -static const CGFloat kMDCFloatingButtonTransformScale = (CGFloat)0.000244140625; - -static const NSTimeInterval kMDCFloatingButtonEnterDuration = 0.270; -static const NSTimeInterval kMDCFloatingButtonExitDuration = 0.180; - -static const NSTimeInterval kMDCFloatingButtonEnterIconDuration = 0.180; -static const NSTimeInterval kMDCFloatingButtonEnterIconOffset = 0.090; -static const NSTimeInterval kMDCFloatingButtonExitIconDuration = 0.135; -static const NSTimeInterval kMDCFloatingButtonExitIconOffset = 0; - -static const NSTimeInterval kMDCFloatingButtonOpacityDuration = 0.015; -static const NSTimeInterval kMDCFloatingButtonOpacityEnterOffset = 0.030; -static const NSTimeInterval kMDCFloatingButtonOpacityExitOffset = 0.150; - -@implementation MDCFloatingButton (Animation) - -+ (CATransform3D)collapseTransform { - return CATransform3DMakeScale(kMDCFloatingButtonTransformScale, kMDCFloatingButtonTransformScale, - 1); -} - -+ (CATransform3D)expandTransform { - return CATransform3DInvert([MDCFloatingButton collapseTransform]); -} - -+ (CABasicAnimation *)animationWithKeypath:(nonnull NSString *)keyPath - toValue:(nonnull id)toValue - fromValue:(nullable id)fromValue - timingFunction:(nonnull CAMediaTimingFunction *)timingFunction - fillMode:(nonnull NSString *)fillMode - duration:(NSTimeInterval)duration - beginOffset:(NSTimeInterval)beginOffset { - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath]; - animation.toValue = toValue; - animation.fromValue = fromValue; - animation.timingFunction = timingFunction; - animation.fillMode = fillMode; - animation.removedOnCompletion = NO; - animation.duration = duration; - if (fabs(beginOffset) > DBL_EPSILON) { - animation.beginTime = CACurrentMediaTime() + beginOffset; - } - -#if TARGET_IPHONE_SIMULATOR - animation.duration *= [self fab_dragCoefficient]; - if (fabs(beginOffset) > DBL_EPSILON) { - animation.beginTime = CACurrentMediaTime() + (beginOffset * [self fab_dragCoefficient]); - } -#endif - - return animation; -} - -#if TARGET_IPHONE_SIMULATOR -+ (float)fab_dragCoefficient { - if (&UIAnimationDragCoefficient) { - float coeff = UIAnimationDragCoefficient(); - if (coeff > 1) { - return coeff; - } - } - return 1; -} -#endif - -- (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion { -// The typical approach to adjusting the iPad's pointer frame is to invalidate the relevant -// pointer interactions. This was attempted, but, as you can see in go/mdc-fab-pointer-bug, -// invalidating the interaction while a transform animation is happening causes undesired behavior. -// Because of this, we instead temporarily disable pointer interaction for the button while it -// animates and reenable (if previously enabled) once the animation has ended. -#ifdef __IPHONE_13_4 -#if !TARGET_OS_TV - BOOL wasPointerInteractionEnabled = NO; - if (@available(iOS 13.4, *)) { - if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { - wasPointerInteractionEnabled = self.pointerInteractionEnabled; - self.pointerInteractionEnabled = NO; - } - } -#endif // !TARGET_OS_TV -#endif // __IPHONE_13_4 - void (^expandActions)(void) = ^{ - self.layer.transform = - CATransform3DConcat(self.layer.transform, [MDCFloatingButton expandTransform]); - self.layer.opacity = 1; - self.imageView.layer.transform = - CATransform3DConcat(self.imageView.layer.transform, [MDCFloatingButton expandTransform]); - [self.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; - [self.layer removeAnimationForKey:kMDCFloatingButtonOpacityKey]; - [self.imageView.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; -#ifdef __IPHONE_13_4 -#if !TARGET_OS_TV - if (@available(iOS 13.4, *)) { - if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { - self.pointerInteractionEnabled = wasPointerInteractionEnabled; - } - } -#endif // !TARGET_OS_TV -#endif // __IPHONE_13_4 - if (completion) { - completion(); - } - }; - - if (animated) { - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - [CATransaction setCompletionBlock:expandActions]; - - CABasicAnimation *overallScaleAnimation = [MDCFloatingButton - animationWithKeypath:@"transform" - toValue:[NSValue - valueWithCATransform3D:CATransform3DConcat( - self.layer.transform, - [MDCFloatingButton expandTransform])] - fromValue:nil - timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:0:0:(float)0.2:1] - fillMode:kCAFillModeForwards - duration:kMDCFloatingButtonEnterDuration - beginOffset:0]; - [self.layer addAnimation:overallScaleAnimation forKey:kMDCFloatingButtonTransformKey]; - - CALayer *iconPresentationLayer = self.imageView.layer.presentationLayer; - if (iconPresentationLayer) { - // Transform from a scale of 0, up to the icon view's current (animated) transform - CALayer *presentationLayer = self.layer.presentationLayer; - NSValue *fromValue = - presentationLayer ? [NSValue valueWithCATransform3D:CATransform3DConcat( - presentationLayer.transform, - CATransform3DMakeScale(0, 0, 1))] - : nil; - - // clang-format off - CAMediaTimingFunction *iconScaleTimingFunction = - [[CAMediaTimingFunction alloc] initWithControlPoints:0:0:(float)0.2:1]; - // clang-format on - CABasicAnimation *iconScaleAnimation = [MDCFloatingButton - animationWithKeypath:@"transform" - toValue:[NSValue valueWithCATransform3D:iconPresentationLayer.transform] - fromValue:fromValue - timingFunction:iconScaleTimingFunction - fillMode:kCAFillModeBoth - duration:kMDCFloatingButtonEnterIconDuration - beginOffset:kMDCFloatingButtonEnterIconOffset]; - - [self.imageView.layer addAnimation:iconScaleAnimation forKey:kMDCFloatingButtonTransformKey]; - } - - CABasicAnimation *opacityAnimation = [MDCFloatingButton - animationWithKeypath:@"opacity" - toValue:[NSNumber numberWithInt:1] - fromValue:nil - timingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] - fillMode:kCAFillModeForwards - duration:kMDCFloatingButtonOpacityDuration - beginOffset:kMDCFloatingButtonOpacityEnterOffset]; - [self.layer addAnimation:opacityAnimation forKey:kMDCFloatingButtonOpacityKey]; - - [CATransaction commit]; - } else { - expandActions(); - } -} - -- (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion { -// The typical approach to adjusting the iPad's pointer frame is to invalidate the relevant -// pointer interactions. This was attempted, but, as you can see in go/mdc-fab-pointer-bug, -// invalidating the interaction while a transform animation is happening causes undesired behavior. -// Because of this, we instead temporarily disable pointer interaction for the button while it -// animates and reenable (if previously enabled) once the animation has ended. -#ifdef __IPHONE_13_4 -#if !TARGET_OS_TV - BOOL wasPointerInteractionEnabled = NO; - if (@available(iOS 13.4, *)) { - if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { - wasPointerInteractionEnabled = self.pointerInteractionEnabled; - self.pointerInteractionEnabled = NO; - } - } -#endif // !TARGET_OS_TV -#endif // __IPHONE_13_4 - - void (^collapseActions)(void) = ^{ - self.layer.transform = - CATransform3DConcat(self.layer.transform, [MDCFloatingButton collapseTransform]); - self.layer.opacity = 0; - self.imageView.layer.transform = - CATransform3DConcat(self.imageView.layer.transform, [MDCFloatingButton collapseTransform]); - [self.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; - [self.layer removeAnimationForKey:kMDCFloatingButtonOpacityKey]; - [self.imageView.layer removeAnimationForKey:kMDCFloatingButtonTransformKey]; -#ifdef __IPHONE_13_4 -#if !TARGET_OS_TV - if (@available(iOS 13.4, *)) { - if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) { - self.pointerInteractionEnabled = wasPointerInteractionEnabled; - } - } -#endif // !TARGET_OS_TV -#endif // __IPHONE_13_4 - if (completion) { - completion(); - } - }; - - if (animated) { - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - [CATransaction setCompletionBlock:collapseActions]; - - CABasicAnimation *overallScaleAnimation = [MDCFloatingButton - animationWithKeypath:@"transform" - toValue:[NSValue - valueWithCATransform3D:CATransform3DConcat( - self.layer.transform, - [MDCFloatingButton collapseTransform])] - fromValue:nil - // clang-format off - timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:1:1] - // clang-format on - fillMode:kCAFillModeForwards - duration:kMDCFloatingButtonExitDuration - beginOffset:0]; - - [self.layer addAnimation:overallScaleAnimation forKey:kMDCFloatingButtonTransformKey]; - - // clang-format off - CABasicAnimation *iconScaleAnimation = [MDCFloatingButton - animationWithKeypath:@"transform" - toValue:[NSValue - valueWithCATransform3D:CATransform3DConcat( - self.imageView.layer.transform, - [MDCFloatingButton collapseTransform])] - fromValue:nil - // clang-format off - timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:(float)0.4:0:1:1] - // clang-format on - fillMode:kCAFillModeForwards - duration:kMDCFloatingButtonExitIconDuration - beginOffset:kMDCFloatingButtonExitIconOffset]; - [self.imageView.layer addAnimation:iconScaleAnimation forKey:kMDCFloatingButtonTransformKey]; - - CABasicAnimation *opacityAnimation = [MDCFloatingButton - animationWithKeypath:@"opacity" - toValue:[NSNumber numberWithFloat:0] - fromValue:nil - timingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] - fillMode:kCAFillModeForwards - duration:kMDCFloatingButtonOpacityDuration - beginOffset:kMDCFloatingButtonOpacityExitOffset]; - [self.layer addAnimation:opacityAnimation forKey:kMDCFloatingButtonOpacityKey]; - - [CATransaction commit]; - } else { - collapseActions(); - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h deleted file mode 100644 index a76414c4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.h +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#import "MDCButton.h" - -/** - Shapes for Material Floating buttons. - - The mini size should only be used when required for visual continuity with other elements on the - screen. - */ -typedef NS_ENUM(NSInteger, MDCFloatingButtonShape) { - /** - A 56-point circular button surrounding a 24- or 36-point square icon or short text. - */ - MDCFloatingButtonShapeDefault = 0, - /** - A 40-point circular button surrounding a 24-point square icon or short text. - */ - MDCFloatingButtonShapeMini = 1 -}; - -/** - Size of Material Floating button. - - The expanded mode should only be used when text and an icon are used. - */ -typedef NS_ENUM(NSInteger, MDCFloatingButtonMode) { - /** - The floating button is a circle with its contents centered. - */ - MDCFloatingButtonModeNormal = 0, - - /** - The floating button is a "pill shape" with the image to one side of the title. - */ - MDCFloatingButtonModeExpanded = 1, -}; - -/** - Image location of Material Floating button. - - If the button is @c MDCFloatingButtonModeExpanded this determines where the - text is rendered in relation to the icon. - */ -typedef NS_ENUM(NSInteger, MDCFloatingButtonImageLocation) { - /** - The image of the floating button is on the leading side of the title. - */ - MDCFloatingButtonImageLocationLeading = 0, - - /** - The image of the floating button is on the trailing side of the title. - */ - MDCFloatingButtonImageLocationTrailing = 1, -}; - -/** - A "floating" MDCButton. - - Floating action buttons are circular, float a considerable amount above their parent, have - their own background color, and also raise briefly when touched. Floating action buttons should - only be used rarely, for the main action of a screen. - - @see https://material.io/go/design-buttons#buttons-main-buttons - */ -@interface MDCFloatingButton : MDCButton - -/** - The mode of the floating button can either be .normal (a circle) or .expanded (a pill-shaped - rounded rectangle). In the @c .normal mode, the button should have either an image or a title, - but not both. In the @c .expanded mode, the button should have both an image and a title. - - The @c .normal layout is identical to that of UIButton. The content will be centered (or otherwise - aligned based on the @c contentHorizontalAlignment and @c contentVerticalAlignment properties. In - @c .expanded layout, the image view will be inset from the leading edge (or trailing edge when - @c imageLocation is .trailing). The "bounding box" for the title will be inset from the opposite - edge and separated from @c imageView by @c imageTitleSpace and the title label will be - leading-aligned within this box. In @c .expanded mode, the @c contentVerticalAlignment and - @c contentHorizontalAlignment properties are ignored. - - @note Setting the mode directly is equivalent to calling - @code [self setMode:mode animated:NO] @endcode. - - The default value is @c .normal . - */ -@property(nonatomic, assign) MDCFloatingButtonMode mode; - -/** - The shape of the floating button. - - The default value is decided by the @c -initWithFrame:shape: initializer. - */ -@property(nonatomic, assign) MDCFloatingButtonShape shape; - -/** - Changes the mode (with animation, if desired). - - If animated, the floating button's size will be updated automatically as part of the animation. - Otherwise, the floating button's size will need to be explicitly recalculated after the mode has - changed. - - @see @c mode for more details about the mode value. - */ -- (void)setMode:(MDCFloatingButtonMode)mode animated:(BOOL)animated; - -/** - Changes the mode (with animation, if desired). - - If animated, the floating button's size will be updated automatically as part of the animation. - Otherwise, the floating button's size will need to be explicitly recalculated after the mode has - changed. - - @param animateAlongside An optional block that will be invoked alongside the animation, if - animated, otherwise it will be invoked immediately. - @param completion An optional block that will be invoked upon completion of the animation, if - animated, otherwise it will be invoked immediately. - - @see @c mode for more details about the mode value. - */ -- (void)setMode:(MDCFloatingButtonMode)mode - animated:(BOOL)animated - animateAlongside:(nullable void (^)(void))animateAlongside - completion:(nullable void (^)(BOOL finished))completion; - -/** - The location of the image relative to the title when the floating button is in @c expanded mode. - - The default value is @c .leading . - */ -@property(nonatomic, assign) MDCFloatingButtonImageLocation imageLocation UI_APPEARANCE_SELECTOR; - -/** - The horizontal spacing in points between the @c imageView and @c titleLabel when the button is in - @c .expanded mode. If set to a negative value, the image and title may overlap. - - The default value is 8. - */ -@property(nonatomic, assign) CGFloat imageTitleSpace UI_APPEARANCE_SELECTOR; - -/** - Returns a MDCFloatingButton with default colors and the given @c shape. - - @param shape Button shape. - @return Button with shape. - */ -+ (nonnull instancetype)floatingButtonWithShape:(MDCFloatingButtonShape)shape; - -/** - @return The default floating button size dimension. - */ -+ (CGFloat)defaultDimension; - -/** - @return The mini floating button size dimension. - */ -+ (CGFloat)miniDimension; - -/** - Initializes self to a button with the given @c shape. - - @param frame Button frame. - @param shape Button shape. - @return Button with shape. - */ -- (nonnull instancetype)initWithFrame:(CGRect)frame - shape:(MDCFloatingButtonShape)shape NS_DESIGNATED_INITIALIZER; - -/** - Initializes self to a button with the MDCFloatingButtonShapeDefault shape. - - @param frame Button frame. - @return Button with MDCFloatingButtonShapeDefault shape. - */ -- (nonnull instancetype)initWithFrame:(CGRect)frame; - -/** - Initializes self to a button with the MDCFloatingButtonShapeDefault shape. - - @return Button with MDCFloatingButtonShapeDefault shape. - */ -- (nonnull instancetype)init; - -- (void)setMinimumSize:(CGSize)size NS_UNAVAILABLE; - -/** - Sets the minimum size when the button has the specified @c shape @c mode. - Setting a size of @c CGSizeZero is equivalent to no minimum size. To set a fixed size for a - button, use the same value when setting the minimum and maximum sizes for a @c shape and @c mode - combination. - - @param minimumSize The new minimum size of the button. - @param shape The shape that the size constrains. - @param mode The mode that the size constrains. - */ -- (void)setMinimumSize:(CGSize)minimumSize - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR; - -- (void)setMaximumSize:(CGSize)maximumSize NS_UNAVAILABLE; - -/** - Sets the maximum size when the button has the specified @c shape and @c mode. - Setting a size of @c CGSizeZero is equivalent to no maximum size. To set a fixed size for a - button, use the same value when setting the minimum and maximum sizes for a @c shape and @c mode - combination. - - @param maximumSize The new maximum size of the button. - @param shape The shape that the size constrains. - @param mode The mode that the size constrains. - */ -- (void)setMaximumSize:(CGSize)maximumSize - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR; - -- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets NS_UNAVAILABLE; - -/** - Sets the @c contentEdgeInsets value when the button has the specified @c shape and @c mode. - The behavior of @c contentEdgeInsets is the same as for UIButton. The button will layout its - subviews within the rectangle generated by insetting its @c bounds by @c contentEdgeInsets. - - @param contentEdgeInsets The new content edge insets value. - @param shape The shape for the content edge insets. - @param mode The mode for the content edge insets. - */ -- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR; - -- (void)setHitAreaInsets:(UIEdgeInsets)hitAreaInsets NS_UNAVAILABLE; - -/** - Sets the @c centerVisibleArea value when the button has the specified @c shape and @c mode. - - @param centerVisibleArea The boolean value that determines whether the visible area is centered in - the bounds of the view. - @param shape The floating action button's shape (Default, Mini). - @param mode The floating action button's mode (Normal, Expanded). - */ -- (void)setCenterVisibleArea:(BOOL)centerVisibleArea - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode; - -@end - -@interface MDCFloatingButton (Deprecated) - -/** - Sets the @c hitAreaInsets value when the button has the specified @c shape and @c mode. - - @param hitAreaInsets The new hit area insets value. - @param shape The shape for the hit area insets. - @param mode The mode for the hit area insets. - */ -- (void)setHitAreaInsets:(UIEdgeInsets)hitAreaInsets - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode UI_APPEARANCE_SELECTOR - __deprecated_msg("Use setCenterVisibleArea:forShape:inMode: instead."); - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m deleted file mode 100644 index 894002e3..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCFloatingButton.m +++ /dev/null @@ -1,573 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFloatingButton.h" - -#import "private/MDCFloatingButtonModeAnimator.h" -#import "private/MDCFloatingButtonModeAnimatorDelegate.h" -#import "MDCButton.h" -#import "MaterialShadowElevations.h" - -#import - -static const CGFloat MDCFloatingButtonDefaultDimension = 56; -static const CGFloat MDCFloatingButtonMiniDimension = 40; -static const CGFloat MDCFloatingButtonDefaultImageTitleSpace = 8; -static const UIEdgeInsets internalLayoutInsets = (UIEdgeInsets){0, 16, 0, 24}; - -@interface MDCFloatingButton () - -@property(nonatomic, readonly) - NSMutableDictionary *> - *shapeToModeToMinimumSize; - -@property(nonatomic, readonly) - NSMutableDictionary *> - *shapeToModeToMaximumSize; - -@property(nonatomic, readonly) - NSMutableDictionary *> - *shapeToModeToContentEdgeInsets; - -@property(nonatomic, readonly) - NSMutableDictionary *> - *shapeToModeToHitAreaInsets; - -@property(nonatomic, readonly) - NSMutableDictionary *> - *shapeToModeToCenterVisibleArea; - -@end - -@implementation MDCFloatingButton { - MDCFloatingButtonShape _shape; - - MDCFloatingButtonModeAnimator *_modeAnimator; - // Allows us to perform masking effects during mode animations. - UIView *_titleLabelContainerView; -} - -+ (void)initialize { - [[MDCFloatingButton appearance] setElevation:MDCShadowElevationFABResting - forState:UIControlStateNormal]; - [[MDCFloatingButton appearance] setElevation:MDCShadowElevationFABPressed - forState:UIControlStateHighlighted]; -} - -+ (CGFloat)defaultDimension { - return MDCFloatingButtonDefaultDimension; -} - -+ (CGFloat)miniDimension { - return MDCFloatingButtonMiniDimension; -} - -+ (instancetype)floatingButtonWithShape:(MDCFloatingButtonShape)shape { - return [[[self class] alloc] initWithFrame:CGRectZero shape:shape]; -} - -- (instancetype)init { - return [self initWithFrame:CGRectZero shape:MDCFloatingButtonShapeDefault]; -} - -- (instancetype)initWithFrame:(CGRect)frame { - return [self initWithFrame:frame shape:MDCFloatingButtonShapeDefault]; -} - -- (instancetype)initWithFrame:(CGRect)frame shape:(MDCFloatingButtonShape)shape { - self = [super initWithFrame:frame]; - if (self) { - _shape = shape; - [self commonMDCFloatingButtonInit]; - // The superclass sets contentEdgeInsets from defaultContentEdgeInsets before the _shape is set. - // Set contentEdgeInsets again to ensure the defaults are for the correct shape. - [self updateShapeAndAllowResize:NO]; - } - return self; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" -// https://stackoverflow.com/questions/24458608/convenience-initializer-missing-a-self-call-to-another-initializer -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; -#pragma clang diagnostic pop - if (self) { - // Required to migrate any previously-archived FloatingButtons from .largeIcon shape value - if (@(_shape).integerValue >= 2) { - _shape = MDCFloatingButtonShapeDefault; - } - // Shape must be set first before the common initialization - [self commonMDCFloatingButtonInit]; - - [self updateShapeAndAllowResize:NO]; - } - return self; -} - -- (void)commonMDCFloatingButtonInit { - _imageTitleSpace = MDCFloatingButtonDefaultImageTitleSpace; - - // Create a container view for titleLabel and add the titelLabel to it. This will enable us to - // mask the titleLabel mode animations if desired, while acting effectively as a pass-through for - // the superview layout logic. - _titleLabelContainerView = [[UIView alloc] initWithFrame:self.bounds]; - _titleLabelContainerView.autoresizingMask = - UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self insertSubview:_titleLabelContainerView belowSubview:self.titleLabel]; - _titleLabelContainerView.userInteractionEnabled = NO; - [_titleLabelContainerView addSubview:self.titleLabel]; - - const CGSize miniNormalSize = - CGSizeMake(MDCFloatingButtonMiniDimension, MDCFloatingButtonMiniDimension); - const CGSize defaultNormalSize = - CGSizeMake(MDCFloatingButtonDefaultDimension, MDCFloatingButtonDefaultDimension); - const CGSize defaultExpandedMinimumSize = CGSizeMake(0, 48); - const CGSize defaultExpandedMaximumSize = CGSizeMake(328, 0); - - // Minimum size values for different shape + mode combinations - NSMutableDictionary *miniShapeMinimumSizeDictionary = - [@{@(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:miniNormalSize]} mutableCopy]; - NSMutableDictionary *defaultShapeMinimumSizeDictionary = [@{ - @(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:defaultNormalSize], - @(MDCFloatingButtonModeExpanded) : [NSValue valueWithCGSize:defaultExpandedMinimumSize], - } mutableCopy]; - _shapeToModeToMinimumSize = [@{ - @(MDCFloatingButtonShapeMini) : miniShapeMinimumSizeDictionary, - @(MDCFloatingButtonShapeDefault) : defaultShapeMinimumSizeDictionary, - } mutableCopy]; - - // Maximum size values for different shape + mode combinations - NSMutableDictionary *miniShapeMaximumSizeDictionary = - [@{@(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:miniNormalSize]} mutableCopy]; - NSMutableDictionary *defaultShapeMaximumSizeDictionary = [@{ - @(MDCFloatingButtonModeNormal) : [NSValue valueWithCGSize:defaultNormalSize], - @(MDCFloatingButtonModeExpanded) : [NSValue valueWithCGSize:defaultExpandedMaximumSize], - } mutableCopy]; - _shapeToModeToMaximumSize = [@{ - @(MDCFloatingButtonShapeMini) : miniShapeMaximumSizeDictionary, - @(MDCFloatingButtonShapeDefault) : defaultShapeMaximumSizeDictionary, - } mutableCopy]; - - // Content edge insets values for different shape + mode combinations - // .mini shape, .normal mode - const UIEdgeInsets miniNormalContentInsets = UIEdgeInsetsMake(8, 8, 8, 8); - NSMutableDictionary *miniShapeContentEdgeInsetsDictionary = - [@{@(MDCFloatingButtonModeNormal) : [NSValue valueWithUIEdgeInsets:miniNormalContentInsets]} - mutableCopy]; - _shapeToModeToContentEdgeInsets = - [@{@(MDCFloatingButtonShapeMini) : miniShapeContentEdgeInsetsDictionary} mutableCopy]; - - // Hit area insets values for different shape + mode combinations - // .mini shape, .normal mode - const UIEdgeInsets miniNormalHitAreaInset = UIEdgeInsetsMake(-4, -4, -4, -4); - NSMutableDictionary *miniShapeHitAreaInsetsDictionary = [@{ - @(MDCFloatingButtonModeNormal) : [NSValue valueWithUIEdgeInsets:miniNormalHitAreaInset], - } mutableCopy]; - _shapeToModeToHitAreaInsets = [@{ - @(MDCFloatingButtonShapeMini) : miniShapeHitAreaInsetsDictionary, - } mutableCopy]; - - _shapeToModeToCenterVisibleArea = [[NSMutableDictionary alloc] init]; -} - -#pragma mark - UIView - -- (CGSize)sizeThatFits:(__unused CGSize)size { - return [self intrinsicContentSize]; -} - -- (CGSize)intrinsicContentSizeForModeNormal { - switch (_shape) { - case MDCFloatingButtonShapeDefault: - return CGSizeMake(MDCFloatingButtonDefaultDimension, MDCFloatingButtonDefaultDimension); - case MDCFloatingButtonShapeMini: - return CGSizeMake(MDCFloatingButtonMiniDimension, MDCFloatingButtonMiniDimension); - } -} - -- (CGSize)intrinsicContentSizeForModeExpanded { - const CGSize intrinsicTitleSize = - [self.titleLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - const CGSize intrinsicImageSize = - [self.imageView sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - CGFloat intrinsicWidth = intrinsicTitleSize.width + intrinsicImageSize.width + - self.imageTitleSpace + internalLayoutInsets.left + - internalLayoutInsets.right + self.contentEdgeInsets.left + - self.contentEdgeInsets.right; - CGFloat intrinsicHeight = MAX(intrinsicTitleSize.height, intrinsicImageSize.height) + - self.contentEdgeInsets.top + self.contentEdgeInsets.bottom + - internalLayoutInsets.top + internalLayoutInsets.bottom; - return CGSizeMake(intrinsicWidth, intrinsicHeight); -} - -- (CGSize)intrinsicContentSize { - CGSize contentSize = CGSizeZero; - if (self.mode == MDCFloatingButtonModeNormal) { - contentSize = [self intrinsicContentSizeForModeNormal]; - } else if (self.mode == MDCFloatingButtonModeExpanded) { - contentSize = [self intrinsicContentSizeForModeExpanded]; - } - - if (self.minimumSize.height > 0) { - contentSize.height = MAX(self.minimumSize.height, contentSize.height); - } - if (self.maximumSize.height > 0) { - contentSize.height = MIN(self.maximumSize.height, contentSize.height); - } - if (self.minimumSize.width > 0) { - contentSize.width = MAX(self.minimumSize.width, contentSize.width); - } - if (self.maximumSize.width > 0) { - contentSize.width = MIN(self.maximumSize.width, contentSize.width); - } - - return contentSize; -} - -/* - Performs custom layout when the FAB is in .expanded mode. Specifically, the layout algorithm is - as follows: - - 1. Inset the bounds by the value of `contentEdgeInsets` and use this as the layout bounds. - 2. Determine the intrinsic sizes of the imageView and titleLabel. - 3. Compute the space remaining for the titleLabel after accounting for the imageView and built-in - alignment guidelines (internalLayoutInsets). - 4. Position the imageView along the leading (or trailing) edge of the button, inset by - internalLayoutInsets.left (flipped for RTL). - 5. Position the titleLabel along the leading edge of its available space. - 6. Apply the imageEdgeInsets and titleEdgeInsets to their respective views. - */ -- (void)layoutSubviews { - // We have to set cornerRadius before laying out subviews so that the boundingPath is correct. - CGRect visibleBounds = UIEdgeInsetsInsetRect(self.bounds, self.visibleAreaInsets); - self.layer.cornerRadius = CGRectGetHeight(visibleBounds) / 2; - [super layoutSubviews]; - - if (self.mode == MDCFloatingButtonModeNormal) { - return; - } - - // Position the imageView and titleView - // - // +------------------------------------+ - // | | | | CEI TOP | | - // |CEI +--+ |+-----+ |CEI| - // | LT ||SP||Title| |RGT| - // | +--+ |+-----+ | | - // | | | | CEI BOT | | - // +------------------------------------+ - // - // (A) The same spacing on either side of the label. - // (SP) The spacing between the image and title - // (CEI) Content Edge Insets - // - // The diagram above assumes an LTR user interface orientation - // and a .leadingIcon imageLocation for this button. - - const CGRect insetBounds = [self insetBoundsForBounds:self.bounds]; - - const CGFloat imageViewWidth = CGRectGetWidth(self.imageView.bounds); - const CGFloat boundsCenterY = CGRectGetMidY(insetBounds); - CGFloat titleWidthAvailable = CGRectGetWidth(insetBounds); - titleWidthAvailable -= imageViewWidth; - titleWidthAvailable -= self.imageTitleSpace; - - const CGFloat availableHeight = CGRectGetHeight(insetBounds); - CGSize titleIntrinsicSize = - [self.titleLabel sizeThatFits:CGSizeMake(titleWidthAvailable, availableHeight)]; - - const CGSize titleSize = CGSizeMake(MAX(0, MIN(titleIntrinsicSize.width, titleWidthAvailable)), - MAX(0, MIN(titleIntrinsicSize.height, availableHeight))); - - CGPoint titleCenter; - CGPoint imageCenter; - BOOL isLTR = - self.mdf_effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionLeftToRight; - BOOL isLeadingIcon = self.imageLocation == MDCFloatingButtonImageLocationLeading; - - // If we are LTR with a leading image, the image goes on the left. - // If we are RTL with a trailing image, the image goes on the left. - if ((isLTR && isLeadingIcon) || (!isLTR && !isLeadingIcon)) { - const CGFloat imageCenterX = CGRectGetMinX(insetBounds) + (imageViewWidth / 2); - const CGFloat titleCenterX = - CGRectGetMaxX(insetBounds) - titleWidthAvailable + (titleSize.width / 2); - titleCenter = CGPointMake(titleCenterX, boundsCenterY); - imageCenter = CGPointMake(imageCenterX, boundsCenterY); - } - // If we are LTR with a trailing image, the image goes on the right. - // If we are RTL with a leading image, the image goes on the right. - else { - const CGFloat imageCenterX = CGRectGetMaxX(insetBounds) - (imageViewWidth / 2); - const CGFloat titleCenterX = - CGRectGetMinX(insetBounds) + titleWidthAvailable - (titleSize.width / 2); - imageCenter = CGPointMake(imageCenterX, boundsCenterY); - titleCenter = CGPointMake(titleCenterX, boundsCenterY); - } - - self.imageView.center = imageCenter; - self.imageView.frame = UIEdgeInsetsInsetRect(self.imageView.frame, self.imageEdgeInsets); - self.titleLabel.center = titleCenter; - CGRect newBounds = CGRectStandardize(self.titleLabel.bounds); - self.titleLabel.bounds = (CGRect){newBounds.origin, titleSize}; - self.titleLabel.frame = UIEdgeInsetsInsetRect(self.titleLabel.frame, self.titleEdgeInsets); -} - -- (CGRect)insetBoundsForBounds:(CGRect)bounds { - BOOL isLeadingIcon = self.imageLocation == MDCFloatingButtonImageLocationLeading; - UIEdgeInsets adjustedLayoutInsets = - (isLeadingIcon ? internalLayoutInsets : MDFInsetsFlippedHorizontally(internalLayoutInsets)); - return UIEdgeInsetsInsetRect(UIEdgeInsetsInsetRect(bounds, adjustedLayoutInsets), - self.contentEdgeInsets); -} - -#pragma mark - Mode animator - -- (MDCFloatingButtonModeAnimator *)modeAnimator { - if (!_modeAnimator) { - _modeAnimator = - [[MDCFloatingButtonModeAnimator alloc] initWithTitleLabel:self.titleLabel - titleLabelContainerView:_titleLabelContainerView]; - _modeAnimator.delegate = self; - } - return _modeAnimator; -} - -#pragma mark MDCFloatingButtonModeAnimatorDelegate - -- (void)floatingButtonModeAnimatorCommitLayoutChanges:(MDCFloatingButtonModeAnimator *)modeAnimator - mode:(MDCFloatingButtonMode)mode { - [self sizeToFit]; -} - -#pragma mark - Property Setters/Getters - -- (void)setShape:(MDCFloatingButtonShape)shape { - if (_shape == shape) { - return; - } - _shape = shape; - [self updateShapeAndAllowResize:YES]; -} - -- (void)setMode:(MDCFloatingButtonMode)mode { - [self setMode:mode animated:NO animateAlongside:nil completion:nil]; -} - -- (void)setMode:(MDCFloatingButtonMode)mode animated:(BOOL)animated { - [self setMode:mode animated:animated animateAlongside:nil completion:nil]; -} - -- (void)setMode:(MDCFloatingButtonMode)mode - animated:(BOOL)animated - animateAlongside:(void (^)(void))animateAlongside - completion:(void (^)(BOOL finished))completion { - if (_mode == mode) { - if (animateAlongside) { - animateAlongside(); - } - if (completion) { - completion(YES); - } - return; - } - _mode = mode; - - [self updateShapeAndAllowResize:YES]; - - [[self modeAnimator] modeDidChange:mode - animated:animated - animateAlongside:animateAlongside - completion:completion]; -} - -- (void)setMinimumSize:(CGSize)size - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToMinimumSize = self.shapeToModeToMinimumSize[@(shape)]; - if (!modeToMinimumSize) { - modeToMinimumSize = [@{} mutableCopy]; - self.shapeToModeToMinimumSize[@(shape)] = modeToMinimumSize; - } - modeToMinimumSize[@(mode)] = [NSValue valueWithCGSize:size]; - if (shape == _shape && mode == self.mode) { - [self updateShapeAndAllowResize:YES]; - } -} - -- (CGSize)minimumSizeForMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToMinimumSize = self.shapeToModeToMinimumSize[@(_shape)]; - if (!modeToMinimumSize) { - return CGSizeZero; - } - - NSValue *sizeValue = modeToMinimumSize[@(mode)]; - if (sizeValue) { - return [sizeValue CGSizeValue]; - } else { - return CGSizeZero; - } -} - -- (BOOL)updateMinimumSize { - CGSize newSize = [self minimumSizeForMode:self.mode]; - if (CGSizeEqualToSize(newSize, self.minimumSize)) { - return NO; - } - super.minimumSize = newSize; - return YES; -} - -- (void)setMaximumSize:(CGSize)size - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToMaximumSize = self.shapeToModeToMaximumSize[@(shape)]; - if (!modeToMaximumSize) { - modeToMaximumSize = [@{} mutableCopy]; - self.shapeToModeToMaximumSize[@(shape)] = modeToMaximumSize; - } - modeToMaximumSize[@(mode)] = [NSValue valueWithCGSize:size]; - if (shape == _shape && mode == self.mode) { - [self updateShapeAndAllowResize:YES]; - } -} - -- (CGSize)maximumSizeForMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToMaximumSize = self.shapeToModeToMaximumSize[@(_shape)]; - if (!modeToMaximumSize) { - return CGSizeZero; - } - - NSValue *sizeValue = modeToMaximumSize[@(mode)]; - if (sizeValue) { - return [sizeValue CGSizeValue]; - } else { - return CGSizeZero; - } -} - -- (BOOL)updateMaximumSize { - CGSize newSize = [self maximumSizeForMode:self.mode]; - if (CGSizeEqualToSize(newSize, self.maximumSize)) { - return NO; - } - super.maximumSize = newSize; - return YES; -} - -- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToContentEdgeInsets = self.shapeToModeToContentEdgeInsets[@(shape)]; - if (!modeToContentEdgeInsets) { - modeToContentEdgeInsets = [@{} mutableCopy]; - self.shapeToModeToContentEdgeInsets[@(shape)] = modeToContentEdgeInsets; - } - modeToContentEdgeInsets[@(mode)] = [NSValue valueWithUIEdgeInsets:contentEdgeInsets]; - if (shape == _shape && mode == self.mode) { - [self updateShapeAndAllowResize:YES]; - } -} - -- (UIEdgeInsets)contentEdgeInsetsForMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToContentEdgeInsets = self.shapeToModeToContentEdgeInsets[@(_shape)]; - if (!modeToContentEdgeInsets) { - return UIEdgeInsetsZero; - } - - NSValue *insetsValue = modeToContentEdgeInsets[@(mode)]; - if (insetsValue) { - return [insetsValue UIEdgeInsetsValue]; - } else { - return UIEdgeInsetsZero; - } -} - -- (void)updateContentEdgeInsets { - super.contentEdgeInsets = [self contentEdgeInsetsForMode:self.mode]; -} - -- (void)setHitAreaInsets:(UIEdgeInsets)insets - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToHitAreaInsets = self.shapeToModeToHitAreaInsets[@(shape)]; - if (!modeToHitAreaInsets) { - modeToHitAreaInsets = [@{} mutableCopy]; - self.shapeToModeToHitAreaInsets[@(shape)] = modeToHitAreaInsets; - } - modeToHitAreaInsets[@(mode)] = [NSValue valueWithUIEdgeInsets:insets]; - if (shape == _shape && mode == self.mode) { - [self updateShapeAndAllowResize:NO]; - } -} - -- (UIEdgeInsets)hitAreaInsetsForMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToHitAreaInsets = self.shapeToModeToHitAreaInsets[@(_shape)]; - if (!modeToHitAreaInsets) { - return UIEdgeInsetsZero; - } - - NSValue *insetsValue = modeToHitAreaInsets[@(mode)]; - if (insetsValue) { - return [insetsValue UIEdgeInsetsValue]; - } else { - return UIEdgeInsetsZero; - } -} - -- (void)updateHitAreaInsets { - super.hitAreaInsets = [self hitAreaInsetsForMode:self.mode]; -} - -- (void)setCenterVisibleArea:(BOOL)centerVisibleArea - forShape:(MDCFloatingButtonShape)shape - inMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToCenterVisibleArea = self.shapeToModeToCenterVisibleArea[@(shape)]; - if (!modeToCenterVisibleArea) { - modeToCenterVisibleArea = [@{} mutableCopy]; - self.shapeToModeToCenterVisibleArea[@(shape)] = modeToCenterVisibleArea; - } - modeToCenterVisibleArea[@(mode)] = @(centerVisibleArea); - if (shape == _shape && mode == self.mode) { - [self updateShapeAndAllowResize:NO]; - } -} - -- (BOOL)centerVisibleAreaForMode:(MDCFloatingButtonMode)mode { - NSMutableDictionary *modeToCenterVisibleArea = self.shapeToModeToCenterVisibleArea[@(_shape)]; - if (!modeToCenterVisibleArea) { - return NO; - } - - return [modeToCenterVisibleArea[@(mode)] boolValue]; -} - -- (void)updateCenterVisibleArea { - super.centerVisibleArea = [self centerVisibleAreaForMode:self.mode]; -} - -- (void)updateShapeAndAllowResize:(BOOL)allowsResize { - BOOL minimumSizeChanged = [self updateMinimumSize]; - BOOL maximumSizeChanged = [self updateMaximumSize]; - [self updateContentEdgeInsets]; - [self updateHitAreaInsets]; - [self updateCenterVisibleArea]; - - if (allowsResize && (minimumSizeChanged || maximumSizeChanged)) { - [self invalidateIntrinsicContentSize]; - [self setNeedsLayout]; - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h deleted file mode 100644 index 4c184f75..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCButton.h" - -#pragma mark - Soon to be deprecated - -/** - A "raised" MDCButton. - - Raised buttons have their own background color, float above their parent slightly, and raise - briefly when touched. Raised buttons should be used when flat buttons would get lost among other - UI elements on the screen. - - @warning This class will be deprecated soon. Consider using @c MDCContainedButtonThemer with an - @c MDCButton instead. - - @see https://material.io/go/design-buttons#buttons-raised-buttons - */ -@interface MDCRaisedButton : MDCButton -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m deleted file mode 100644 index 06483389..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MDCRaisedButton.m +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRaisedButton.h" - -#import "MaterialShadowElevations.h" - -@implementation MDCRaisedButton - -+ (void)initialize { - [[MDCRaisedButton appearance] setElevation:MDCShadowElevationRaisedButtonResting - forState:UIControlStateNormal]; - [[MDCRaisedButton appearance] setElevation:MDCShadowElevationRaisedButtonPressed - forState:UIControlStateHighlighted]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h deleted file mode 100644 index b9879fdb..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/MaterialButtons.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCButton.h" -#import "MDCFlatButton.h" -#import "MDCFloatingButton+Animation.h" -#import "MDCFloatingButton.h" -#import "MDCRaisedButton.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h deleted file mode 100644 index ebd3789d..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCButton+Subclassing.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCButton.h" - -@class MDCInkView; - -@interface MDCButton (Subclassing) - -/** Access to the ink view layer. Mainly used for subclasses to override ink properties. */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -@property(nonatomic, readonly, strong, nonnull) MDCInkView *inkView; -#pragma clang diagnostic pop - -/** Whether the background color should be opaque. */ -- (BOOL)shouldHaveOpaqueBackground; - -/** Updates the background color based on the button's current configuration. */ -- (void)updateBackgroundColor; - -/** - Should the button raise when touched? - - Default is YES. - */ -@property(nonatomic) BOOL shouldRaiseOnTouch; - -/** The bounding path of the button. The shadow will follow that path. */ -- (nonnull UIBezierPath *)boundingPath; - -@end - -@interface MDCButton () - -/** - A collection of MDCShadow instances each assigned an elevation (in dp). - - To create your own MDCShadowsCollection, please use the provided MDCShadowsCollectionBuilder and - populate it with MDCShadow instances using the provided MDCShadowBuilder. - - Defaults to MDCShadowsCollectionDefault(). - */ -@property(nonatomic, strong, null_resettable) MDCShadowsCollection *shadowsCollection; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h deleted file mode 100644 index 203646d3..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#import "MDCFloatingButton.h" - -@protocol MDCFloatingButtonModeAnimatorDelegate; - -/** - Animates an MDCFloatingButton's mode. - */ -__attribute__((objc_subclassing_restricted)) @interface MDCFloatingButtonModeAnimator : NSObject - -- (nonnull instancetype)initWithTitleLabel:(nonnull UILabel *)titleLabel - titleLabelContainerView:(nonnull UIView *)titleLabelContainerView - NS_DESIGNATED_INITIALIZER; - -/** - Informs the animator that the floating button mode has changed. - - If the change was animated, then the animator will initiate the necessary animations to create the - visual effect of the modes animating from one state to the next. - */ -- (void)modeDidChange:(MDCFloatingButtonMode)mode - animated:(BOOL)animated - animateAlongside:(nullable void (^)(void))animateAlongside - completion:(nullable void (^)(BOOL finished))completion; - -/** - The animator uses the delegate to interact with its owning context: the MDCFloatingButton instance. - */ -@property(nonatomic, weak, nullable) id delegate; - -- (nonnull instancetype)init NS_UNAVAILABLE; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m deleted file mode 100644 index 3771b2d6..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimator.m +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFloatingButtonModeAnimator.h" - -#import "MDCFloatingButton.h" -#import "MDCFloatingButtonModeAnimatorDelegate.h" - -#if TARGET_IPHONE_SIMULATOR -UIKIT_EXTERN float UIAnimationDragCoefficient(void); // UIKit private drag coefficient. -#endif - -static CGFloat SimulatorAnimationDragCoefficient(void) { -#if TARGET_IPHONE_SIMULATOR - return UIAnimationDragCoefficient(); -#else - return 1.0; -#endif -} - -typedef struct { - NSTimeInterval duration; - NSTimeInterval titleOpacityDelay; - NSTimeInterval titleOpacityDuration; -} AnimationTiming; - -// go/mdc-fab-expansion-animation -static const AnimationTiming kExpandAnimationTiming = (AnimationTiming){ - .duration = 0.200, - .titleOpacityDelay = 0.083, - .titleOpacityDuration = 0.067, -}; - -// go/mdc-fab-collapse-animation -static const AnimationTiming kCollapseAnimationTiming = (AnimationTiming){ - .duration = 0.167, - .titleOpacityDelay = 0.016, - .titleOpacityDuration = 0.033, -}; - -static const UIViewAnimationOptions kTitleOpacityAnimationOptions = - UIViewAnimationOptionCurveLinear; - -static NSString *const kModeVerticalDriftAnimationKey = @"position.y.fix"; - -@interface MDCFloatingButtonModeAnimator () -@property(nonatomic, strong) UILabel *titleLabel; -@property(nonatomic, strong) UIView *titleLabelContainerView; -@end - -@implementation MDCFloatingButtonModeAnimator - -- (instancetype)initWithTitleLabel:(UILabel *)titleLabel - titleLabelContainerView:(UIView *)titleLabelContainerView { - self = [super init]; - if (self) { - self.titleLabel = titleLabel; - self.titleLabelContainerView = titleLabelContainerView; - } - return self; -} - -- (void)modeDidChange:(MDCFloatingButtonMode)mode - animated:(BOOL)animated - animateAlongside:(nullable void (^)(void))animateAlongside - completion:(nullable void (^)(BOOL finished))completion { - if (!animated) { - self.titleLabelContainerView.clipsToBounds = NO; - if (animateAlongside) { - animateAlongside(); - } - if (completion) { - completion(YES); - } - return; - } - - // Floating button mode animations are relatively rare, so to avoid having the non-animated steady - // state pay any compositing costs due to masking we only enable clipsToBounds for the course of - // the mode animation. The bounds clipping is necessary to achieve the clipped label effect as the - // button expands / collapses. - _titleLabelContainerView.clipsToBounds = YES; - - const BOOL expanding = mode == MDCFloatingButtonModeExpanded; - - // ## Prepare the label for animation - - // Because the titleLabel has an empty frame in the collapsed state, we key this entire animation - // off of the expanded state's frame. When expanding, we can animate the titleLabel directly - // because the destination frame is non-empty. When collapsing, we need to animate a snapshot - // because the titleLabel's destination frame is empty. - UIView *animationTitleLabel; - void (^titleLabelCleanup)(BOOL); - if (expanding) { - animationTitleLabel = self.titleLabel; - animationTitleLabel.alpha = 0; // Start off initially transparent. - titleLabelCleanup = nil; - } else { - animationTitleLabel = [self.titleLabel snapshotViewAfterScreenUpdates:NO]; - animationTitleLabel.frame = self.titleLabel.frame; - [_titleLabelContainerView addSubview:animationTitleLabel]; - titleLabelCleanup = ^(BOOL finished) { - [animationTitleLabel removeFromSuperview]; - }; - } - - // ## Perform the frame animation - - CGRect priorTitleLabelFrame = self.titleLabel.frame; - AnimationTiming timing = expanding ? kExpandAnimationTiming : kCollapseAnimationTiming; - [UIView animateWithDuration:timing.duration - animations:^{ - NSSet *priorTitleAnimationKeys = [NSSet setWithArray:self.titleLabel.layer.animationKeys]; - - // Force the button to adjust its frame in order to generate the default animations. Note - // that this will also animate the title label and any other subviews within the button. - [self.delegate floatingButtonModeAnimatorCommitLayoutChanges:self mode:mode]; - - // If we allowed the title label to animate as a result of the sizeToFit changes, then we - // would see the title expand / collapse its frame in a squishy and undesirable manner. To - // avoid this, we remove any *newly added* animations from the title label before they get - // committed to the render server. The resulting effect is that the title label's frame is - // instantly where it needs to be and we can animate its alpha independently and compensate - // for any vertical drift below. - NSMutableSet *newTitleAnimationKeys = - [NSMutableSet setWithArray:self.titleLabel.layer.animationKeys]; - [newTitleAnimationKeys minusSet:priorTitleAnimationKeys]; - for (NSString *animationKey in newTitleAnimationKeys) { - [self.titleLabel.layer removeAnimationForKey:animationKey]; - } - - if (animateAlongside) { - animateAlongside(); - } - } - completion:^(BOOL finished) { - if (titleLabelCleanup) { - titleLabelCleanup(finished); - } - self.titleLabelContainerView.clipsToBounds = NO; - if (completion) { - completion(finished); - } - }]; - - // ## Compensate for vertical drift - - // As noted above, the default titleLabel frame animations cause an undesired scaling effect of - // the label and so the default animations are removed. The titleLabel's frame is now set to the - // final state of the animation which is generally desired in terms of the titleLabel's size, but - // if the button's height changes then we need to animate the label's y position, otherwise the - // label will appear to be pinned to the top - rather than the center - of the button as the - // button expands / contracts. - // - // We compensate for this effect by creating an additive animation below. The purpose of this - // additive animation is to give the appearance that the label is centered vertically within the - // button over the course of the animation and without making adjustments to the model layer. - - CGRect newTitleLabelFrame = self.titleLabel.frame; - CGFloat centerYDelta = CGRectGetMidY(newTitleLabelFrame) - CGRectGetMidY(priorTitleLabelFrame); - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; - animation.additive = YES; - animation.timingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - if (expanding) { - // When expanding, we initially compensate for the vertical shift and then gradually reduce that - // compensation as the animation progresses. This is because the label's model layer is already - // shifted relative to the original center position. - animation.fromValue = @(-centerYDelta); - animation.toValue = @0; - } else { - // When contracting we undo the effect of the expansion by adding the compensation gradually - // back to the label. - animation.fromValue = @0; - animation.toValue = @(centerYDelta); - } - animation.duration = timing.duration * SimulatorAnimationDragCoefficient(); - [animationTitleLabel.layer addAnimation:animation forKey:kModeVerticalDriftAnimationKey]; - - // ## Animate the title opacity - - [UIView animateWithDuration:timing.titleOpacityDuration - delay:timing.titleOpacityDelay - options:kTitleOpacityAnimationOptions - animations:^{ - if (mode == MDCFloatingButtonModeExpanded) { - animationTitleLabel.alpha = 1; - } else { - animationTitleLabel.alpha = 0; - } - } - completion:nil]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h deleted file mode 100644 index d7d2d71e..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#import "MDCFloatingButton.h" - -@class MDCFloatingButtonModeAnimator; - -/** - MDCFloatingButtonModeAnimator uses this delegate to interact with its owning context. - */ -@protocol MDCFloatingButtonModeAnimatorDelegate -@required - -/** - Asks the receiver to commit any layout changes relevant to the mode change. - */ -- (void)floatingButtonModeAnimatorCommitLayoutChanges: - (nonnull MDCFloatingButtonModeAnimator *)modeAnimator - mode:(MDCFloatingButtonMode)mode; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h deleted file mode 100644 index e03c5f3f..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialElevation.h" -#import "MaterialInk.h" -#import "MaterialRipple.h" -#import "MaterialShadowLayer.h" - -@protocol MDCShapeGenerating; - -@interface MDCCard : UIControl - -/** - The corner radius for the card - Default is set to 4. - */ -@property(nonatomic, assign) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; - -/** - The inkView for the card that is initiated on tap - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -@property(nonatomic, readonly, strong, nonnull) MDCInkView *inkView; -#pragma clang diagnostic pop - -/** - The rippleView for the card that is initiated on tap. The ripple view is the successor of ink - view, and can be used by setting `enableRippleBehavior` to YES after initializing the card. - */ -@property(nonatomic, readonly, strong, nonnull) MDCStatefulRippleView *rippleView; - -/** - This property defines if a card as a whole should be interactable or not. - What this means is that when isInteractable is set to NO, there will be no ink ripple and - no change in shadow elevation when tapped or selected. Also the card container itself will not be - tappable, but any of its subviews will still be tappable. - - Default is set to YES. - - Important: Our specification for cards explicitly define a card as being an interactable component. - Therefore, this property should be set to NO *only if* there are other interactable items within - the card's content, such as buttons or other tappable controls. - */ -@property(nonatomic, getter=isInteractable) IBInspectable BOOL interactable; - -/** - By setting this property to YES, you will enable and use inkView's successor rippleView as the - main view to provide visual feedback for taps. It is recommended to set this property right after - initializing the card. - - Defaults to NO. - */ -@property(nonatomic, assign) BOOL enableRippleBehavior; - -/** - Sets the shadow elevation for an UIControlState state - - @param shadowElevation The shadow elevation - @param state UIControlState the card state - */ -- (void)setShadowElevation:(MDCShadowElevation)shadowElevation - forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the shadow elevation for an UIControlState state - - If no elevation has been set for a state, the value for UIControlStateNormal will be returned. - Default value for UIControlStateNormal is 1 - Default value for UIControlStateHighlighted is 8 - - @param state UIControlState the card state - @return The shadow elevation for the requested state. - */ -- (MDCShadowElevation)shadowElevationForState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the border width for an UIControlState state - - @param borderWidth The border width - @param state UIControlState the card state - */ -- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the border width for an UIControlState state - - If no border width has been set for a state, the value for UIControlStateNormal will be returned. - Default value for UIControlStateNormal is 0 - - @param state UIControlState the card state - @return The border width for the requested state. - */ -- (CGFloat)borderWidthForState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the border color for an UIControlState state - - @param borderColor The border color - @param state UIControlState the card state - */ -- (void)setBorderColor:(nullable UIColor *)borderColor - forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the border color for an UIControlState state - - If no border color has been set for a state, it will check the value of UIControlStateNormal. - If that value also isn't set, then nil will be returned. - - @param state UIControlState the card state - @return The border color for the requested state. - */ -- (nullable UIColor *)borderColorForState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the shadow color for an UIControlState state - - @param shadowColor The shadow color - @param state UIControlState the card state - */ -- (void)setShadowColor:(nullable UIColor *)shadowColor - forState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the shadow color for an UIControlState state - - If no color has been set for a state, the value for MDCCardViewStateNormal will be returned. - Default value for UIControlStateNormal is blackColor - - @param state UIControlState the card state - @return The shadow color for the requested state. - */ -- (nullable UIColor *)shadowColorForState:(UIControlState)state UI_APPEARANCE_SELECTOR; - -/** - A block that is invoked when the @c MDCCard receives a call to @c - traitCollectionDidChange:. The block is called after the call to the superclass. - */ -@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) - (MDCCard *_Nonnull card, UITraitCollection *_Nullable previousTraitCollection); - -/* - The shape generator used to define the card's shape. - When set, layer properties such as cornerRadius and other layer properties are nullified/zeroed. - If a layer property is explicitly set after the shapeGenerator has been set, it will lead to - unexpected behavior. - - When the shapeGenerator is nil, MDCCard will use the default underlying layer with - its default settings. - - Default value for shapeGenerator is nil. - */ -@property(nullable, nonatomic, strong) id shapeGenerator; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m deleted file mode 100644 index 6c48571d..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCard.m +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCard.h" - -#import "MaterialElevation.h" -#import "MaterialInk.h" -#import "MaterialRipple.h" -#import "MaterialShadowElevations.h" -#import "MaterialShadowLayer.h" -#import "MaterialShapes.h" -#import "MaterialMath.h" - -static const CGFloat MDCCardShadowElevationNormal = 1; -static const CGFloat MDCCardShadowElevationHighlighted = 8; -static const CGFloat MDCCardCornerRadiusDefault = 4; -static const BOOL MDCCardIsInteractableDefault = YES; - -@interface MDCCard () -@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; -@end - -@implementation MDCCard { - NSMutableDictionary *_shadowElevations; - NSMutableDictionary *_shadowColors; - NSMutableDictionary *_borderWidths; - NSMutableDictionary *_borderColors; - UIColor *_backgroundColor; - CGPoint _lastTouch; -} - -@dynamic layer; -@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation; -@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock; - -+ (Class)layerClass { - return [MDCShapedShadowLayer class]; -} - -- (instancetype)initWithCoder:(NSCoder *)coder { - self = [super initWithCoder:coder]; - if (self) { - [self commonMDCCardInit]; - } - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonMDCCardInit]; - } - return self; -} - -- (void)commonMDCCardInit { - self.layer.cornerRadius = MDCCardCornerRadiusDefault; - _interactable = MDCCardIsInteractableDefault; - _mdc_overrideBaseElevation = -1; - - if (_inkView == nil) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - _inkView = [[MDCInkView alloc] initWithFrame:self.bounds]; -#pragma clang diagnostic pop - _inkView.autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - _inkView.usesLegacyInkRipple = NO; - _inkView.layer.zPosition = FLT_MAX; - [self addSubview:_inkView]; - } - - if (_shadowElevations == nil) { - _shadowElevations = [NSMutableDictionary dictionary]; - _shadowElevations[@(UIControlStateNormal)] = @(MDCCardShadowElevationNormal); - _shadowElevations[@(UIControlStateHighlighted)] = @(MDCCardShadowElevationHighlighted); - } - - if (_shadowColors == nil) { - _shadowColors = [NSMutableDictionary dictionary]; - _shadowColors[@(UIControlStateNormal)] = UIColor.blackColor; - } - - if (_borderColors == nil) { - _borderColors = [NSMutableDictionary dictionary]; - } - - if (_borderWidths == nil) { - _borderWidths = [NSMutableDictionary dictionary]; - } - - if (_backgroundColor == nil) { - _backgroundColor = UIColor.whiteColor; - } - - [self updateShadowElevation]; - [self updateShadowColor]; - [self updateBorderWidth]; - [self updateBorderColor]; - [self updateBackgroundColor]; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - if (!self.layer.shapeGenerator) { - self.layer.shadowPath = [self boundingPath].CGPath; - } - - [self updateShadowColor]; - [self updateBackgroundColor]; - [self updateBorderColor]; -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - [super traitCollectionDidChange:previousTraitCollection]; - - if (self.traitCollectionDidChangeBlock) { - self.traitCollectionDidChangeBlock(self, previousTraitCollection); - } -} - -- (void)setCornerRadius:(CGFloat)cornerRadius { - self.layer.cornerRadius = cornerRadius; - [self setNeedsLayout]; -} - -- (CGFloat)cornerRadius { - return self.layer.cornerRadius; -} - -- (UIBezierPath *)boundingPath { - CGFloat cornerRadius = self.cornerRadius; - return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; -} - -- (MDCShadowElevation)shadowElevationForState:(UIControlState)state { - NSNumber *elevation = _shadowElevations[@(state)]; - if (state != UIControlStateNormal && elevation == nil) { - elevation = _shadowElevations[@(UIControlStateNormal)]; - } - if (elevation != nil) { - return (CGFloat)[elevation doubleValue]; - } - return 0; -} - -- (void)setShadowElevation:(MDCShadowElevation)shadowElevation forState:(UIControlState)state { - _shadowElevations[@(state)] = @(shadowElevation); - - [self updateShadowElevation]; -} - -- (void)updateShadowElevation { - CGFloat elevation = [self shadowElevationForState:self.state]; - if (!MDCCGFloatEqual(((MDCShadowLayer *)self.layer).elevation, elevation)) { - if (!self.layer.shapeGenerator) { - self.layer.shadowPath = [self boundingPath].CGPath; - } - [(MDCShadowLayer *)self.layer setElevation:elevation]; - [self mdc_elevationDidChange]; - } -} - -- (void)setBorderWidth:(CGFloat)borderWidth forState:(UIControlState)state { - _borderWidths[@(state)] = @(borderWidth); - - [self updateBorderWidth]; -} - -- (void)updateBorderWidth { - CGFloat borderWidth = [self borderWidthForState:self.state]; - self.layer.shapedBorderWidth = borderWidth; -} - -- (CGFloat)borderWidthForState:(UIControlState)state { - NSNumber *borderWidth = _borderWidths[@(state)]; - if (state != UIControlStateNormal && borderWidth == nil) { - borderWidth = _borderWidths[@(UIControlStateNormal)]; - } - if (borderWidth != nil) { - return (CGFloat)[borderWidth doubleValue]; - } - return 0; -} - -- (void)setBorderColor:(UIColor *)borderColor forState:(UIControlState)state { - _borderColors[@(state)] = borderColor; - - [self updateBorderColor]; -} - -- (void)updateBorderColor { - UIColor *borderColor = [self borderColorForState:self.state]; - self.layer.shapedBorderColor = borderColor; -} - -- (UIColor *)borderColorForState:(UIControlState)state { - UIColor *borderColor = _borderColors[@(state)]; - if (state != UIControlStateNormal && borderColor == nil) { - borderColor = _borderColors[@(UIControlStateNormal)]; - } - return borderColor; -} - -- (void)setShadowColor:(UIColor *)shadowColor forState:(UIControlState)state { - _shadowColors[@(state)] = shadowColor; - - [self updateShadowColor]; -} - -- (void)updateShadowColor { - CGColorRef shadowColor = [self shadowColorForState:self.state].CGColor; - self.layer.shadowColor = shadowColor; -} - -- (UIColor *)shadowColorForState:(UIControlState)state { - UIColor *shadowColor = _shadowColors[@(state)]; - if (state != UIControlStateNormal && shadowColor == nil) { - shadowColor = _shadowColors[@(UIControlStateNormal)]; - } - if (shadowColor != nil) { - return shadowColor; - } - return [UIColor blackColor]; -} - -- (void)setHighlighted:(BOOL)highlighted { - // Original logic for changing the state to highlighted. - if (self.rippleView == nil) { - if (highlighted && !self.highlighted) { - [self.inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil]; - } else if (!highlighted && self.highlighted) { - [self.inkView startTouchEndedAnimationAtPoint:_lastTouch completion:nil]; - } - } - [super setHighlighted:highlighted]; - // Updated logic using Ripple for changing the state to highlighted. - if (self.rippleView) { - self.rippleView.rippleHighlighted = highlighted; - } - - [self updateShadowElevation]; - [self updateBorderColor]; - [self updateBorderWidth]; - [self updateShadowColor]; -} - -- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event { - BOOL beginTracking = [super beginTrackingWithTouch:touch withEvent:event]; - CGPoint location = [touch locationInView:self]; - _lastTouch = location; - return beginTracking; -} - -- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - UIView *result = [super hitTest:point withEvent:event]; - if (!_interactable && result == self) { - return nil; - } - if (self.layer.shapeGenerator) { - if (!CGPathContainsPoint(self.layer.shapeLayer.path, nil, point, true)) { - return nil; - } - } - return result; -} - -- (void)setShapeGenerator:(id)shapeGenerator { - if (shapeGenerator) { - self.layer.shadowPath = nil; - } else { - self.layer.shadowPath = [self boundingPath].CGPath; - } - - self.layer.shapeGenerator = shapeGenerator; - self.layer.shadowMaskEnabled = NO; - [self updateBackgroundColor]; - // Original logic for configuring Ink prior to the Ripple integration. - if (self.rippleView == nil) { - [self updateInkForShape]; - } -} - -- (id)shapeGenerator { - return self.layer.shapeGenerator; -} - -- (void)updateInkForShape { - CGRect boundingBox = CGPathGetBoundingBox(self.layer.shapeLayer.path); - self.inkView.maxRippleRadius = - (CGFloat)(hypot(CGRectGetHeight(boundingBox), CGRectGetWidth(boundingBox)) / 2 + 10); - self.inkView.layer.masksToBounds = NO; -} - -- (void)setBackgroundColor:(UIColor *)backgroundColor { - _backgroundColor = backgroundColor; - [self updateBackgroundColor]; -} - -- (UIColor *)backgroundColor { - return _backgroundColor; -} - -- (void)updateBackgroundColor { - self.layer.shapedBackgroundColor = _backgroundColor; -} - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.rippleView) { - [self.rippleView touchesBegan:touches withEvent:event]; - } - [super touchesBegan:touches withEvent:event]; -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - // The ripple invocation must come before touchesMoved of the super, otherwise the setHighlighted - // of the UIControl will be triggered before the ripple identifies that the highlighted was - // trigerred from a long press entering the view and shouldn't invoke a ripple. - if (self.rippleView) { - [self.rippleView touchesMoved:touches withEvent:event]; - } - [super touchesMoved:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.rippleView) { - [self.rippleView touchesEnded:touches withEvent:event]; - } - [super touchesEnded:touches withEvent:event]; -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.rippleView) { - [self.rippleView touchesCancelled:touches withEvent:event]; - } - [super touchesCancelled:touches withEvent:event]; -} - -- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior { - if (enableRippleBehavior == _enableRippleBehavior) { - return; - } - _enableRippleBehavior = enableRippleBehavior; - if (enableRippleBehavior) { - if (_rippleView == nil) { - _rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds]; - _rippleView.layer.zPosition = FLT_MAX; - [self addSubview:_rippleView]; - } - if (_inkView) { - [_inkView removeFromSuperview]; - _inkView = nil; - } - } else { - if (_rippleView) { - [_rippleView removeFromSuperview]; - _rippleView = nil; - } - [self addSubview:_inkView]; - } -} - -- (CGFloat)mdc_currentElevation { - return [self shadowElevationForState:self.state]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h deleted file mode 100644 index c84b20ea..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.h +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import "MaterialElevation.h" -#import "MaterialInk.h" -#import "MaterialRipple.h" -#import "MaterialShadowLayer.h" - -@protocol MDCShapeGenerating; - -/** - Through the lifecycle of the cell, the cell can go through one of the 3 states, - normal, highlighted, and selected. The cell starts in its default state, normal. - When `selectable` is set to NO, each touch on the cell turns it to the highlighted state, and when - the touch is released, it is returned to the normal state. When `selectable` is set to YES. Each - touch on the cell that isn't cancelled turns the cell to its selected state. Another touch on the - cell changes it back to normal. - */ -typedef NS_ENUM(NSInteger, MDCCardCellState) { - /** The visual state when the cell is in its normal state. */ - MDCCardCellStateNormal = 0, - - /** The visual state when the cell is in its highlighted state. */ - MDCCardCellStateHighlighted, - - /** The visual state when the cell has been selected. */ - MDCCardCellStateSelected, - - /** - The visual state when the cell is being dragged. - Currently only used with the Ripple Beta component. - */ - MDCCardCellStateDragged -}; - -/** - The horizontal alignment of the image when in selectable mode (`selectable` is set to YES). - */ -typedef NS_ENUM(NSInteger, MDCCardCellHorizontalImageAlignment) { - /** The alignment of the image is to the right of the card. */ - MDCCardCellHorizontalImageAlignmentRight = 0, - - /** The alignment of the image is to the center of the card. */ - MDCCardCellHorizontalImageAlignmentCenter, - - /** The alignment of the image is to the left of the card. */ - MDCCardCellHorizontalImageAlignmentLeft, - - // TODO: Add AlignmentLeading and AlignmentTrailing. See Github issue #3045 -}; - -/** - The vertical alignment of the image when in selectable mode (`selectable` is set to YES). - */ -typedef NS_ENUM(NSInteger, MDCCardCellVerticalImageAlignment) { - /** The alignment of the image is to the top of the card. */ - MDCCardCellVerticalImageAlignmentTop = 0, - - /** The alignment of the image is to the center of the card. */ - MDCCardCellVerticalImageAlignmentCenter, - - /** The alignment of the image is to the bottom of the card. */ - MDCCardCellVerticalImageAlignmentBottom, -}; - -@interface MDCCardCollectionCell : UICollectionViewCell - -/** - When selectable is set to YES, a tap on a cell will trigger a visual change between selected - and unselected. When it is set to NO, a tap will trigger a normal tap (rather than trigger - different visual selection states on the card). - Default is set to NO. - */ -@property(nonatomic, assign, getter=isSelectable) BOOL selectable; - -/** - A Boolean value indicating whether the card is in the dragged state. - */ -@property(nonatomic, getter=isDragged) BOOL dragged; - -/** - The corner radius for the card - Default is set to 4. - */ -@property(nonatomic, assign) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; - -/** - The inkView for the card that is initiated on tap - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -@property(nonatomic, readonly, strong, nonnull) MDCInkView *inkView; -#pragma clang diagnostic pop - -/** - The rippleView for the card that is initiated on tap. The ripple view is the successor of ink - view, and can be used by setting `enableRippleBehavior` to YES after initializing the card. - */ -@property(nonatomic, readonly, strong, nonnull) MDCStatefulRippleView *rippleView; - -/** - This property defines if a card as a whole should be interactable or not. - What this means is that when isInteractable is set to NO, there will be no ink ripple and - no change in shadow elevation when tapped or selected. Also the card container itself will not be - tappable, but any of its subviews will still be tappable. - - Default is set to YES. - - Important: Our specification for cards explicitly define a card as being an interactable component. - Therefore, this property should be set to NO *only if* there are other interactable items within - the card's content, such as buttons or other tappable controls. - */ -@property(nonatomic, getter=isInteractable) IBInspectable BOOL interactable; - -/* - The shape generator used to define the card cell's shape. - When set, layer properties such as cornerRadius and other layer properties are nullified/zeroed. - If a layer property is explicitly set after the shapeGenerator has been set, it will lead to - unexpected behavior. - - When the shapeGenerator is nil, MDCCardCollectionCell will use the default underlying layer with - its default settings. - - Default value for shapeGenerator is nil. - */ -@property(nullable, nonatomic, strong) id shapeGenerator; - -/** - By setting this property to YES, you will enable and use inkView's successor rippleView as the - main view to provide visual feedback for taps. It is recommended to set this property right after - initializing the card. - - Defaults to NO. - */ -@property(nonatomic, assign) BOOL enableRippleBehavior; - -/** - Sets the shadow elevation for an MDCCardViewState state - - @param shadowElevation The shadow elevation - @param state MDCCardCellState the card state - */ -- (void)setShadowElevation:(MDCShadowElevation)shadowElevation - forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the shadow elevation for an MDCCardViewState state - - If no elevation has been set for a state, the value for MDCCardCellStateNormal will be returned. - Default value for MDCCardCellStateNormal is 1 - Default value for MDCCardCellStateHighlighted is 8 - Default value for MDCCardCellStateSelected is 8 - - @param state MDCCardCellStateNormal the card state - @return The shadow elevation for the requested state. - */ -- (MDCShadowElevation)shadowElevationForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the border width for an MDCCardViewState state - - @param borderWidth The border width - @param state MDCCardCellState the card state - */ -- (void)setBorderWidth:(CGFloat)borderWidth forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the border width for an MDCCardCellState state - - If no border width has been set for a state, the value for MDCCardCellStateNormal will be returned. - Default value for MDCCardCellStateNormal is 0 - - @param state MDCCardCellState the card state - @return The border width for the requested state. - */ -- (CGFloat)borderWidthForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the border color for an MDCCardCellStateNormal state - - @param borderColor The border color - @param state MDCCardCellState the card state - */ -- (void)setBorderColor:(nullable UIColor *)borderColor - forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the border color for an MDCCardCellStateNormal state - - If no border color has been set for a state, it will check the value of UIControlStateNormal. - If that value also isn't set, then nil will be returned. - - @param state MDCCardCellState the card state - @return The border color for the requested state. - */ -- (nullable UIColor *)borderColorForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the shadow color for an MDCCardCellStateNormal state - - @param shadowColor The shadow color - @param state MDCCardCellState the card state - */ -- (void)setShadowColor:(nullable UIColor *)shadowColor - forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the shadow color for an MDCCardCellStateNormal state - - If no color has been set for a state, the value for MDCCardViewStateNormal will be returned. - Default value for MDCCardCellStateNormal is blackColor - - @param state MDCCardCellState the card state - @return The shadow color for the requested state. - */ -- (nullable UIColor *)shadowColorForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the image for an MDCCardCellStateNormal state. - - @note The image is only displayed when `selectable` is YES. - If no image has been set for a state, it will check the value of UIControlStateNormal. - If that value also isn't set, then nil will be returned. - Default value for MDCCardCellStateSelected is ic_check_circle - - @param state MDCCardCellState the card state - @return The image for the requested state. - */ -- (nullable UIImage *)imageForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the image for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - @param image The image - @param state MDCCardCellState the card state - */ -- (void)setImage:(nullable UIImage *)image forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the horizontal image alignment for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - If no alignment has been set for a state, it will check the value of UIControlStateNormal. - If that value also isn't set, then MDCCardCellImageHorizontalAlignmentRight will be returned. - - @param state MDCCardCellState the card state - @return The horizontal alignment for the requested state. - */ -- (MDCCardCellHorizontalImageAlignment)horizontalImageAlignmentForState:(MDCCardCellState)state - UI_APPEARANCE_SELECTOR; - -/** - Sets the image alignment for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - @param horizontalImageAlignment The image alignment - @param state MDCCardCellState the card state - */ -- (void)setHorizontalImageAlignment:(MDCCardCellHorizontalImageAlignment)horizontalImageAlignment - forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the vertical image alignment for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - If no alignment has been set for a state, it will check the value of UIControlStateNormal. - If that value also isn't set, then MDCCardCellImageVerticalAlignmentTop will be returned. - - @param state MDCCardCellState the card state - @return The vertical alignment for the requested state. - */ -- (MDCCardCellVerticalImageAlignment)verticalImageAlignmentForState:(MDCCardCellState)state - UI_APPEARANCE_SELECTOR; - -/** - Sets the image alignment for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - @param verticalImageAlignment The image alignment - @param state MDCCardCellState the card state - */ -- (void)setVerticalImageAlignment:(MDCCardCellVerticalImageAlignment)verticalImageAlignment - forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Returns the image tint color for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - If no tint color has been set for a state, it will check the value of UIControlStateNormal. - If that value also isn't set, then nil will be returned. - - @param state MDCCardCellState the card state - @return The image tint color for the requested state. - */ -- (nullable UIColor *)imageTintColorForState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - Sets the image tint color for an MDCCardCellStateNormal state - - @note The image is only displayed when `selectable` is YES. - @param imageTintColor The image tint color - @param state MDCCardCellState the card state - */ -- (void)setImageTintColor:(nullable UIColor *)imageTintColor - forState:(MDCCardCellState)state UI_APPEARANCE_SELECTOR; - -/** - The state of the card cell. - Default is MDCCardCellStateNormal. - */ -@property(nonatomic, readonly) MDCCardCellState state; - -/** - A block that is invoked when the @c MDCCardCollectionCell receives a call to @c - traitCollectionDidChange:. The block is called after the call to the superclass. - */ -@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) - (MDCCardCollectionCell *_Nonnull collectionCell, - UITraitCollection *_Nullable previousTraitCollection); - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m deleted file mode 100644 index baffd348..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MDCCardCollectionCell.m +++ /dev/null @@ -1,659 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCardCollectionCell.h" - -#import "MaterialElevation.h" -#import "MaterialInk.h" -#import "MaterialRipple.h" -#import "MaterialShadowElevations.h" -#import "MaterialShadowLayer.h" -#import "MaterialShapes.h" -#import "MaterialIcons+ic_check_circle.h" -#import "MaterialMath.h" - -static const CGFloat MDCCardCellCornerRadiusDefault = 4; -static const CGFloat MDCCardCellSelectedImagePadding = 8; -static const CGFloat MDCCardCellShadowElevationHighlighted = 8; -static const CGFloat MDCCardCellShadowElevationNormal = 1; -static const CGFloat MDCCardCellShadowElevationSelected = 8; -static const CGFloat MDCCardCellShadowElevationDragged = 8; -static const BOOL MDCCardCellIsInteractableDefault = YES; - -@interface MDCCardCollectionCell () -@property(nonatomic, strong, nullable) UIImageView *selectedImageView; -@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; -@end - -@implementation MDCCardCollectionCell { - NSMutableDictionary *_shadowElevations; - NSMutableDictionary *_shadowColors; - NSMutableDictionary *_borderWidths; - NSMutableDictionary *_borderColors; - NSMutableDictionary *_images; - NSMutableDictionary *_horizontalImageAlignments; - NSMutableDictionary *_verticalImageAlignments; - NSMutableDictionary *_imageTintColors; - UIColor *_backgroundColor; - CGPoint _lastTouch; -} - -@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation; -@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock; -@synthesize state = _state; -@dynamic layer; - -+ (Class)layerClass { - return [MDCShapedShadowLayer class]; -} - -- (instancetype)initWithCoder:(NSCoder *)coder { - self = [super initWithCoder:coder]; - if (self) { - self.layer.cornerRadius = MDCCardCellCornerRadiusDefault; - _interactable = MDCCardCellIsInteractableDefault; - [self commonMDCCardCollectionCellInit]; - } - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.layer.cornerRadius = MDCCardCellCornerRadiusDefault; - _interactable = MDCCardCellIsInteractableDefault; - [self commonMDCCardCollectionCellInit]; - } - return self; -} - -- (void)commonMDCCardCollectionCellInit { - _mdc_overrideBaseElevation = -1; - - if (_inkView == nil) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - _inkView = [[MDCInkView alloc] initWithFrame:self.bounds]; -#pragma clang diagnostic pop - _inkView.autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - _inkView.usesLegacyInkRipple = NO; - _inkView.layer.zPosition = FLT_MAX; - [self addSubview:_inkView]; - } - - if (_selectedImageView == nil) { - _selectedImageView = [[UIImageView alloc] init]; - _selectedImageView.layer.zPosition = _inkView.layer.zPosition - 1; - _selectedImageView.autoresizingMask = - (UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | - UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin); - [self.contentView addSubview:_selectedImageView]; - _selectedImageView.hidden = YES; - } - - if (_shadowElevations == nil) { - _shadowElevations = [NSMutableDictionary dictionary]; - _shadowElevations[@(MDCCardCellStateNormal)] = @(MDCCardCellShadowElevationNormal); - _shadowElevations[@(MDCCardCellStateHighlighted)] = @(MDCCardCellShadowElevationHighlighted); - _shadowElevations[@(MDCCardCellStateSelected)] = @(MDCCardCellShadowElevationSelected); - _shadowElevations[@(MDCCardCellStateDragged)] = @(MDCCardCellShadowElevationDragged); - } - - if (_shadowColors == nil) { - _shadowColors = [NSMutableDictionary dictionary]; - _shadowColors[@(MDCCardCellStateNormal)] = UIColor.blackColor; - } - - if (_borderColors == nil) { - _borderColors = [NSMutableDictionary dictionary]; - } - - if (_borderWidths == nil) { - _borderWidths = [NSMutableDictionary dictionary]; - } - - if (_images == nil) { - _images = [NSMutableDictionary dictionary]; - UIImage *circledCheck = [MDCIcons imageFor_ic_check_circle]; - circledCheck = [circledCheck imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - _images[@(MDCCardCellStateSelected)] = circledCheck; - } - - if (_horizontalImageAlignments == nil) { - _horizontalImageAlignments = [NSMutableDictionary dictionary]; - _horizontalImageAlignments[@(MDCCardCellStateNormal)] = - @(MDCCardCellHorizontalImageAlignmentRight); - } - - if (_verticalImageAlignments == nil) { - _verticalImageAlignments = [NSMutableDictionary dictionary]; - _verticalImageAlignments[@(MDCCardCellStateNormal)] = @(MDCCardCellVerticalImageAlignmentTop); - } - - if (_imageTintColors == nil) { - _imageTintColors = [NSMutableDictionary dictionary]; - } - - if (_backgroundColor == nil) { - _backgroundColor = UIColor.whiteColor; - } - - [self updateCardCellVisuals]; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - if (!self.layer.shapeGenerator) { - self.layer.shadowPath = [self boundingPath].CGPath; - } - [self updateImageAlignment]; -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - [super traitCollectionDidChange:previousTraitCollection]; - - if (self.traitCollectionDidChangeBlock) { - self.traitCollectionDidChangeBlock(self, previousTraitCollection); - } -} - -- (void)updateCardCellVisuals { - [self updateShadowElevation]; - [self updateBorderColor]; - [self updateBorderWidth]; - [self updateShadowColor]; - [self updateImage]; - [self updateImageAlignment]; - [self updateImageTintColor]; - [self updateBackgroundColor]; -} - -- (void)setCornerRadius:(CGFloat)cornerRadius { - self.layer.cornerRadius = cornerRadius; - [self setNeedsLayout]; -} - -- (CGFloat)cornerRadius { - return self.layer.cornerRadius; -} - -- (void)setState:(MDCCardCellState)state animated:(BOOL)animated { - if (self.rippleView) { - return; - } - switch (state) { - case MDCCardCellStateSelected: { - if (_state != MDCCardCellStateHighlighted) { - if (animated) { - [self.inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil]; - } else { - [self.inkView cancelAllAnimationsAnimated:NO]; - [self.inkView startTouchBeganAtPoint:self.center animated:NO withCompletion:nil]; - } - } - break; - } - case MDCCardCellStateNormal: { - [self.inkView startTouchEndAtPoint:_lastTouch animated:animated withCompletion:nil]; - break; - } - case MDCCardCellStateHighlighted: { - // Note: setHighlighted: can get getting more calls with YES than NO when clicking rapidly. - // To guard against ink never going away and darkening our card we call - // startTouchEndedAnimationAtPoint:completion:. - [self.inkView startTouchEndAtPoint:_lastTouch animated:animated withCompletion:nil]; - [self.inkView startTouchBeganAtPoint:_lastTouch animated:animated withCompletion:nil]; - break; - } - default: - break; - } - _state = state; - [self updateCardCellVisuals]; -} - -- (MDCCardCellState)state { - if (self.rippleView) { - if (self.selected && self.selectable) { - return MDCCardCellStateSelected; - } else if (self.dragged) { - return MDCCardCellStateDragged; - } else if (self.highlighted) { - return MDCCardCellStateHighlighted; - } else { - return MDCCardCellStateNormal; - } - } - return _state; -} - -- (void)setSelected:(BOOL)selected { - [super setSelected:selected]; - if (self.rippleView) { - if (!self.selectable) { - return; - } - self.rippleView.selected = selected; - [self updateCardCellVisuals]; - } else { - if (self.selectable) { - if (selected) { - [self setState:MDCCardCellStateSelected animated:NO]; - } else { - [self setState:MDCCardCellStateNormal animated:NO]; - } - } - } -} - -- (void)setHighlighted:(BOOL)highlighted { - [super setHighlighted:highlighted]; - if (self.rippleView) { - self.rippleView.rippleHighlighted = highlighted; - [self updateCardCellVisuals]; - } -} - -- (void)setSelectable:(BOOL)selectable { - _selectable = selectable; - if (self.rippleView) { - self.rippleView.allowsSelection = selectable; - } else { - self.selectedImageView.hidden = !selectable; - } -} - -- (void)setDragged:(BOOL)dragged { - _dragged = dragged; - if (self.rippleView) { - self.rippleView.dragged = dragged; - if (dragged) { - self.highlighted = NO; - } - [self updateCardCellVisuals]; - } -} - -- (UIBezierPath *)boundingPath { - CGFloat cornerRadius = self.cornerRadius; - return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; -} - -- (MDCShadowElevation)shadowElevationForState:(MDCCardCellState)state { - NSNumber *elevation = _shadowElevations[@(state)]; - if (state != MDCCardCellStateNormal && elevation == nil) { - elevation = _shadowElevations[@(MDCCardCellStateNormal)]; - } - if (elevation != nil) { - return (CGFloat)[elevation doubleValue]; - } - return 0; -} - -- (void)setShadowElevation:(MDCShadowElevation)shadowElevation forState:(MDCCardCellState)state { - _shadowElevations[@(state)] = @(shadowElevation); - - [self updateShadowElevation]; -} - -- (void)updateShadowElevation { - CGFloat elevation = [self shadowElevationForState:self.state]; - if (!MDCCGFloatEqual(((MDCShadowLayer *)self.layer).elevation, elevation)) { - if (!self.layer.shapeGenerator) { - self.layer.shadowPath = [self boundingPath].CGPath; - } - [(MDCShadowLayer *)self.layer setElevation:elevation]; - [self mdc_elevationDidChange]; - } -} - -- (void)setBorderWidth:(CGFloat)borderWidth forState:(MDCCardCellState)state { - _borderWidths[@(state)] = @(borderWidth); - - [self updateBorderWidth]; -} - -- (void)updateBorderWidth { - CGFloat borderWidth = [self borderWidthForState:self.state]; - self.layer.shapedBorderWidth = borderWidth; -} - -- (CGFloat)borderWidthForState:(MDCCardCellState)state { - NSNumber *borderWidth = _borderWidths[@(state)]; - if (state != MDCCardCellStateNormal && borderWidth == nil) { - borderWidth = _borderWidths[@(MDCCardCellStateNormal)]; - } - if (borderWidth != nil) { - return (CGFloat)[borderWidth doubleValue]; - } - return 0; -} - -- (void)setBorderColor:(UIColor *)borderColor forState:(MDCCardCellState)state { - _borderColors[@(state)] = borderColor; - - [self updateBorderColor]; -} - -- (void)updateBorderColor { - UIColor *borderColor = [self borderColorForState:self.state]; - self.layer.shapedBorderColor = borderColor; -} - -- (UIColor *)borderColorForState:(MDCCardCellState)state { - UIColor *borderColor = _borderColors[@(state)]; - if (state != MDCCardCellStateNormal && borderColor == nil) { - borderColor = _borderColors[@(MDCCardCellStateNormal)]; - } - return borderColor; -} - -- (void)setShadowColor:(UIColor *)shadowColor forState:(MDCCardCellState)state { - _shadowColors[@(state)] = shadowColor; - - [self updateShadowColor]; -} - -- (void)updateShadowColor { - CGColorRef shadowColor = [self shadowColorForState:self.state].CGColor; - self.layer.shadowColor = shadowColor; -} - -- (UIColor *)shadowColorForState:(MDCCardCellState)state { - UIColor *shadowColor = _shadowColors[@(state)]; - if (state != MDCCardCellStateNormal && shadowColor == nil) { - shadowColor = _shadowColors[@(MDCCardCellStateNormal)]; - } - if (shadowColor != nil) { - return shadowColor; - } - return [UIColor blackColor]; -} - -- (void)setImage:(UIImage *)image forState:(MDCCardCellState)state { - _images[@(state)] = image; - - [self updateImage]; -} - -- (void)updateImage { - UIImage *image = [self imageForState:self.state]; - if (self.rippleView) { - // TODO(#6661): CardCollectionCell's state system doesn't incorporate multiple states occurring - // simultaneously. When the card is selected and highlighted it should take the image of - // MDCCardCellStateSelected. - if (self.rippleView.selected) { - image = [self imageForState:MDCCardCellStateSelected]; - } - } - [self.selectedImageView setImage:image]; - [self.selectedImageView sizeToFit]; -} - -- (UIImage *)imageForState:(MDCCardCellState)state { - UIImage *image = _images[@(state)]; - if (state != MDCCardCellStateNormal && image == nil) { - image = _images[@(MDCCardCellStateNormal)]; - } - return image; -} - -- (void)setHorizontalImageAlignment:(MDCCardCellHorizontalImageAlignment)horizontalImageAlignment - forState:(MDCCardCellState)state { - _horizontalImageAlignments[@(state)] = @(horizontalImageAlignment); - - [self updateImageAlignment]; -} - -- (MDCCardCellHorizontalImageAlignment)horizontalImageAlignmentForState:(MDCCardCellState)state { - NSNumber *horizontalImageAlignment = _horizontalImageAlignments[@(state)]; - if (state != MDCCardCellStateNormal && horizontalImageAlignment == nil) { - horizontalImageAlignment = _horizontalImageAlignments[@(MDCCardCellStateNormal)]; - } - if (horizontalImageAlignment != nil) { - return (MDCCardCellHorizontalImageAlignment)[horizontalImageAlignment integerValue]; - } - return MDCCardCellHorizontalImageAlignmentRight; -} - -- (void)setVerticalImageAlignment:(MDCCardCellVerticalImageAlignment)verticalImageAlignment - forState:(MDCCardCellState)state { - _verticalImageAlignments[@(state)] = @(verticalImageAlignment); - - [self updateImageAlignment]; -} - -- (MDCCardCellVerticalImageAlignment)verticalImageAlignmentForState:(MDCCardCellState)state { - NSNumber *verticalImageAlignment = _verticalImageAlignments[@(state)]; - if (state != MDCCardCellStateNormal && verticalImageAlignment == nil) { - verticalImageAlignment = _verticalImageAlignments[@(MDCCardCellStateNormal)]; - } - if (verticalImageAlignment != nil) { - return (MDCCardCellVerticalImageAlignment)[verticalImageAlignment integerValue]; - } - return MDCCardCellVerticalImageAlignmentTop; -} - -- (void)updateImageAlignment { - MDCCardCellVerticalImageAlignment verticalImageAlignment = - [self verticalImageAlignmentForState:self.state]; - MDCCardCellHorizontalImageAlignment horizontalImageAlignment = - [self horizontalImageAlignmentForState:self.state]; - - CGFloat yAlignment = 0; - CGFloat xAlignment = 0; - - switch (verticalImageAlignment) { - case MDCCardCellVerticalImageAlignmentTop: - yAlignment = - MDCCardCellSelectedImagePadding + CGRectGetHeight(self.selectedImageView.frame) / 2; - break; - case MDCCardCellVerticalImageAlignmentCenter: - yAlignment = CGRectGetHeight(self.bounds) / 2; - break; - case MDCCardCellVerticalImageAlignmentBottom: - yAlignment = CGRectGetHeight(self.bounds) - MDCCardCellSelectedImagePadding - - CGRectGetHeight(self.selectedImageView.frame) / 2; - break; - } - - switch (horizontalImageAlignment) { - case MDCCardCellHorizontalImageAlignmentLeft: - xAlignment = - MDCCardCellSelectedImagePadding + CGRectGetWidth(self.selectedImageView.frame) / 2; - break; - case MDCCardCellHorizontalImageAlignmentCenter: - xAlignment = CGRectGetWidth(self.bounds) / 2; - break; - case MDCCardCellHorizontalImageAlignmentRight: - xAlignment = CGRectGetWidth(self.bounds) - MDCCardCellSelectedImagePadding - - CGRectGetWidth(self.selectedImageView.frame) / 2; - break; - } - - self.selectedImageView.center = CGPointMake(xAlignment, yAlignment); -} - -- (void)setImageTintColor:(UIColor *)imageTintColor forState:(MDCCardCellState)state { - _imageTintColors[@(state)] = imageTintColor; - - [self updateImageTintColor]; -} - -- (void)updateImageTintColor { - UIColor *imageTintColor = [self imageTintColorForState:self.state]; - if (self.rippleView) { - // TODO(#6661): CardCollectionCell's state system doesn't incorporate multiple states occurring - // simultaneously. When the card is selected and highlighted it should take the image tint of - // MDCCardCellStateSelected. - if (self.rippleView.selected) { - imageTintColor = [self imageTintColorForState:MDCCardCellStateSelected]; - } - } - [self.selectedImageView setTintColor:imageTintColor]; -} - -- (UIColor *)imageTintColorForState:(MDCCardCellState)state { - UIColor *imageTintColor = _imageTintColors[@(state)]; - if (state != MDCCardCellStateNormal && imageTintColor == nil) { - imageTintColor = _imageTintColors[@(MDCCardCellStateNormal)]; - } - return imageTintColor; -} - -- (void)tintColorDidChange { - [super tintColorDidChange]; - [self setImageTintColor:self.tintColor forState:MDCCardCellStateNormal]; -} - -- (void)setShapeGenerator:(id)shapeGenerator { - if (shapeGenerator) { - self.layer.shadowPath = nil; - } else { - self.layer.shadowPath = [self boundingPath].CGPath; - } - - self.layer.shapeGenerator = shapeGenerator; - self.layer.shadowMaskEnabled = NO; - [self updateBackgroundColor]; - // Original logic for configuring Ink prior to the Ripple integration. - if (self.rippleView == nil) { - [self updateInkForShape]; - } -} - -- (id)shapeGenerator { - return self.layer.shapeGenerator; -} - -- (void)updateInkForShape { - CGRect boundingBox = CGPathGetBoundingBox(self.layer.shapeLayer.path); - self.inkView.maxRippleRadius = - (CGFloat)(hypot(CGRectGetHeight(boundingBox), CGRectGetWidth(boundingBox)) / 2 + 10); - self.inkView.layer.masksToBounds = NO; -} - -- (void)setBackgroundColor:(UIColor *)backgroundColor { - _backgroundColor = backgroundColor; - [self updateBackgroundColor]; -} - -- (UIColor *)backgroundColor { - return _backgroundColor; -} - -- (void)updateBackgroundColor { - self.layer.shapedBackgroundColor = _backgroundColor; -} - -#pragma mark - UIResponder - -- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - UIView *result = [super hitTest:point withEvent:event]; - if (!_interactable && (result == self.contentView || result == self)) { - return nil; - } - return result; -} - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.rippleView) { - [self.rippleView touchesBegan:touches withEvent:event]; - } - [super touchesBegan:touches withEvent:event]; - if (self.rippleView == nil) { - UITouch *touch = [touches anyObject]; - CGPoint location = [touch locationInView:self]; - _lastTouch = location; - if (!self.selected || !self.selectable) { - [self setState:MDCCardCellStateHighlighted animated:YES]; - } - } -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - // The ripple invocation must come before touchesMoved of the super, otherwise the setHighlighted - // of the UICollectionViewCell will be triggered before the ripple identifies that the highlighted - // was trigerred from a long press entering the view and shouldn't invoke a ripple. - if (self.rippleView) { - [self.rippleView touchesMoved:touches withEvent:event]; - } - [super touchesMoved:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.rippleView) { - [self.rippleView touchesEnded:touches withEvent:event]; - if (self.dragged) { - self.dragged = NO; - } - } - [super touchesEnded:touches withEvent:event]; - if (self.rippleView == nil) { - if (!self.selected || !self.selectable) { - [self setState:MDCCardCellStateNormal animated:YES]; - } - } -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - if (self.rippleView) { - [self.rippleView touchesCancelled:touches withEvent:event]; - if (self.dragged) { - self.dragged = NO; - } - } - [super touchesCancelled:touches withEvent:event]; - if (self.rippleView == nil) { - if (!self.selected || !self.selectable) { - [self setState:MDCCardCellStateNormal animated:YES]; - } - } -} - -- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior { - if (enableRippleBehavior == _enableRippleBehavior) { - return; - } - _enableRippleBehavior = enableRippleBehavior; - if (enableRippleBehavior) { - // With the new states implementation the selectedImageView doesn't need to be hidden as - // there can be an image apparent not only when the cell is selected, but rather - // depending on the setImage:ForState: API. - self.selectedImageView.hidden = NO; - if (_rippleView == nil) { - _rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds]; - _rippleView.layer.zPosition = FLT_MAX; - [self addSubview:_rippleView]; - } - if (_inkView) { - [_inkView removeFromSuperview]; - _inkView = nil; - } - } else { - self.selectedImageView.hidden = YES; - if (_rippleView) { - [_rippleView removeFromSuperview]; - _rippleView = nil; - } - [self addSubview:_inkView]; - } -} - -- (CGFloat)mdc_currentElevation { - return [self shadowElevationForState:self.state]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h deleted file mode 100644 index 69bd1b0a..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/MaterialCards.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCard.h" -#import "MDCCardCollectionCell.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h deleted file mode 100644 index 1a0c608f..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@interface UICollectionViewController (MDCCardReordering) - -/** - This method should be called when using a UICollectionViewController and want to have - the reorder or drag and drop visuals. Please see - https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623979-installsstandardgestureforintera - for more information. It will make sure that the underlying longPressGestureRecognizer - doesn't cancel the ink tap visual causing the ink to disappear once the reorder begins. - */ -- (void)mdc_setupCardReordering; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m deleted file mode 100644 index 948e1224..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Cards/src/UICollectionViewController+MDCCardReordering.m +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UICollectionViewController+MDCCardReordering.h" - - -@implementation UICollectionViewController (MDCCardReordering) - -- (void)mdc_setupCardReordering { - UILongPressGestureRecognizer *longGestureRecognizer = - [[UILongPressGestureRecognizer alloc] initWithTarget:self action:nil]; - - longGestureRecognizer.delegate = self; - longGestureRecognizer.cancelsTouchesInView = NO; - [self.collectionView addGestureRecognizer:longGestureRecognizer]; -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer - shouldReceiveTouch:(UITouch *)touch { - for (UIGestureRecognizer *gesture in self.collectionView.gestureRecognizers) { - if ([gesture isKindOfClass:[UILongPressGestureRecognizer class]]) { - gesture.cancelsTouchesInView = NO; - } - } - return YES; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h deleted file mode 100644 index 09681a8f..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevatable.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -/** - Provides APIs for @c UIViews to communicate their elevation throughout the view hierarchy. - */ -@protocol MDCElevatable - -/** - The current elevation of the conforming @c UIView. - */ -@property(nonatomic, assign, readonly) CGFloat mdc_currentElevation; - -/** - This block is called when the elevation changes for the conforming @c UIView or @c UIViewController - receiver or one of its direct ancestors in the view hierarchy. - - Use this block to respond to elevation changes in the view or its ancestor views. - - @param absoluteElevation The @c mdc_currentElevation plus the @c mdc_currentElevation of all - ancestor views. This equates to @c mdc_absoluteElevation of the UIView+MaterialElevationResponding - category. - @param object The receiver (self) which conforms to the protocol. - */ -@property(nonatomic, copy, nullable) void (^mdc_elevationDidChangeBlock) - (id _Nonnull object, CGFloat absoluteElevation); - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h deleted file mode 100644 index b3e0e9fb..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MDCElevationOverriding.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -/** - Provides APIs for @c UIViews to communicate their elevation throughout the view hierarchy. - */ -@protocol MDCElevationOverriding - -/** - Used by @c MaterialElevationResponding instead of @c mdc_baseElevation. - - This can be used in cases where there is elevation behind an object that is not part of the - view hierarchy, like a @c UIPresentationController. - - Note: If set to a negative value, this property is ignored as part of the @c mdc_baseElevation - calculation. - */ -@property(nonatomic, assign, readwrite) CGFloat mdc_overrideBaseElevation; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h deleted file mode 100644 index 60c02b35..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/MaterialElevation.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCElevatable.h" -#import "MDCElevationOverriding.h" -#import "UIColor+MaterialElevation.h" -#import "UIView+MaterialElevationResponding.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h deleted file mode 100644 index 14a1aa14..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -/** - Provides extension to UIColor for Material Elevation usage. - */ -@interface UIColor (MaterialElevation) - -/** - Returns a color that takes the specified elevation value into account. - The color is the blended color of Surface and Elevation Overlay in - https://material.io/design/color/dark-theme.html#properties - Negative elevation is treated as 0. - Pattern-based UIColor is not supported. - @param elevation The @c mdc_absoluteElevation value to use when resolving the color. - */ -- (nonnull UIColor *)mdc_resolvedColorWithElevation:(CGFloat)elevation; - -/** - Returns a color that takes the specified elevation value and traits into account when there is a - color appearance difference between current traits and previous traits. When userInterfaceStyle is - UIUserInterfaceStyleDark in currentTraitCollection, elevation will be used to resolve the color. - - Negative elevation is treated as 0. - Pattern-based UIColor is not supported. - UIColor in UIExtendedGrayColorSpace will be resolved to UIExtendedSRGBColorSpace. - - @param traitCollection The traits to use when resolving the color. - @param previousTraitCollection The previous traits to use when comparing color appearance. - @param elevation The @c mdc_absoluteElevation to use when resolving the color. - */ -- (nonnull UIColor *) - mdc_resolvedColorWithTraitCollection:(nonnull UITraitCollection *)traitCollection - previousTraitCollection:(nonnull UITraitCollection *)previousTraitCollection - elevation:(CGFloat)elevation; - -/** - Returns a color that takes the specified elevation value and traits into account. - When userInterfaceStyle is UIUserInterfaceStyleDark in traitCollection, elevation will be used - to resolve the color. - Negative elevation is treated as 0. - Pattern-based UIColor is not supported. - UIColor in UIExtendedGrayColorSpace will be resolved to UIExtendedSRGBColorSpace. - - @param traitCollection The traits to use when resolving the color. - @param elevation The @c mdc_absoluteElevation to use when resolving the color. - */ -- (nonnull UIColor *)mdc_resolvedColorWithTraitCollection: - (nonnull UITraitCollection *)traitCollection - elevation:(CGFloat)elevation; -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m deleted file mode 100644 index 274b0d34..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIColor+MaterialElevation.m +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIColor+MaterialElevation.h" - -#import - -#import "MaterialAvailability.h" -#import "MaterialMath.h" -#import "UIColor+MaterialBlending.h" - -@implementation UIColor (MaterialElevation) - -- (UIColor *)mdc_resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection - previousTraitCollection:(UITraitCollection *)previousTraitCollection - elevation:(CGFloat)elevation { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - if ([traitCollection - hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { - return [self mdc_resolvedColorWithTraitCollection:traitCollection elevation:elevation]; - } - } -#endif // MDC_AVAILABLE_SDK_IOS(13_0) - return self; -} - -- (UIColor *)mdc_resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection - elevation:(CGFloat)elevation { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - UIColor *resolvedColor = [self resolvedColorWithTraitCollection:traitCollection]; - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return [resolvedColor mdc_resolvedColorWithElevation:elevation]; - } else { - return resolvedColor; - } - } -#endif // MDC_AVAILABLE_SDK_IOS(13_0) - return self; -} - -- (UIColor *)mdc_resolvedColorWithElevation:(CGFloat)elevation { - if (CGColorGetPattern(self.CGColor)) { - [NSException raise:NSGenericException - format:@"Pattern-based colors are not supported by %@", NSStringFromSelector(_cmd)]; - } - - UIColor *overlayColor = UIColor.whiteColor; - elevation = MAX(elevation, 0); - CGFloat alphaValue = 0; - if (!MDCCGFloatEqual(elevation, 0)) { - if (elevation < 1) { - // A formula for values between 0 to 1 is used here to simulate the alpha percentage - // as in the main formula below there is a jump between any number larger than 0 to an - // alpha value of 2. This formula provides a gradual polynomial curve that makes the delta - // of the alpha value between lower numbers to be smaller than the higher numbers. - // AlphaValue = 5.11916 * elevationValue ^ 2 - alphaValue = (CGFloat)5.11916 * pow((CGFloat)elevation, 2); - } else { - // A formula is used here to simulate the alpha percentage stated on - // https://material.io/design/color/dark-theme.html#properties - // AlphaValue = 4.5 * ln (elevationValue + 1) + 2 - // Note: Both formulas meet at the transition point of (1, 5.11916). - alphaValue = (CGFloat)4.5 * (CGFloat)log(elevation + 1) + 2; - } - } - // TODO (https://github.com/material-components/material-components-ios/issues/8096): - // Grayscale color should be returned if color space is UIExtendedGrayColorSpace. - return [UIColor mdc_blendColor:[overlayColor colorWithAlphaComponent:alphaValue * (CGFloat)0.01] - withBackgroundColor:self]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h deleted file mode 100644 index f559a327..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -/** - Allows elevation changes to propagate down the view hierarchy and allows objects conforming to - @c MDCElevatable to react to those changes accordingly. - */ -@interface UIView (MaterialElevationResponding) - -/** - Returns the sum of all @c mdc_currentElevation of the superviews going up the view hierarchy - recursively. - - If a view in the hierarchy conforms to @c MDCElevationOveriding and @c mdc_overrideBaseElevation - is non-negative, then the sum of the current total plus the value of @c mdc_overrideBaseElevation - is returned. - - If a @c UIViewController conforms to @c MDCElevatable or @c MDCElevationOveriding then its @c view - will report the view controllers base elevation. - */ -@property(nonatomic, assign, readonly) CGFloat mdc_baseElevation; - -/** - Returns the sum of the view's @c mdc_currentElevation with the @c mdc_currentElevation of its - superviews going up the view hierarchy recursively. - - This value is effectively the sum of @c mdc_baseElevation and @c mdc_currentElevation. - */ -@property(nonatomic, assign, readonly) CGFloat mdc_absoluteElevation; - -/** - Should be called when the view's @c mdc_currentElevation has changed. Will be called on the - receiver's @c subviews. - - If a @c UIView views conform to @c MDCElevation then @c mdc_elevationDidChangeBlock: is called. - */ -- (void)mdc_elevationDidChange; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m deleted file mode 100644 index 95bc4dec..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Elevation/src/UIView+MaterialElevationResponding.m +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIView+MaterialElevationResponding.h" - -#import "MDCElevatable.h" -#import "MDCElevationOverriding.h" - -@implementation UIView (MaterialElevationResponding) - -- (void)mdc_elevationDidChange { - CGFloat baseElevation = self.mdc_baseElevation; - [self mdc_elevationDidChangeWithBaseElevation:baseElevation]; -} - -- (void)mdc_elevationDidChangeWithBaseElevation:(CGFloat)baseElevation { - CGFloat elevation = baseElevation; - id elevatableSelf = [self objectConformingToElevationInResponderChain]; - if (elevatableSelf.mdc_elevationDidChangeBlock) { - elevation += elevatableSelf.mdc_currentElevation; - elevatableSelf.mdc_elevationDidChangeBlock(elevatableSelf, elevation); - } - - for (UIView *subview in self.subviews) { - [subview mdc_elevationDidChangeWithBaseElevation:elevation]; - } -} - -- (CGFloat)mdc_baseElevation { - CGFloat totalElevation = 0; - UIView *current = self; - - while (current != nil) { - id elevatableCurrent = [current objectConformingToElevationInResponderChain]; - if (current != self) { - totalElevation += elevatableCurrent.mdc_currentElevation; - } - id elevatableCurrentOverride = - [current objectConformingToOverrideInResponderChain]; - if (elevatableCurrentOverride != nil && - elevatableCurrentOverride.mdc_overrideBaseElevation >= 0) { - totalElevation += elevatableCurrentOverride.mdc_overrideBaseElevation; - break; - } - current = current.superview; - } - return totalElevation; -} - -- (CGFloat)mdc_absoluteElevation { - CGFloat elevation = self.mdc_baseElevation; - id elevatableSelf = [self objectConformingToElevationInResponderChain]; - elevation += elevatableSelf.mdc_currentElevation; - return elevation; -} - -/** - Checks whether a @c UIView or it's managing @c UIViewController conform to @c - MDCOverrideElevation. - - @returns the conforming @c UIView then @c UIViewController, otherwise @c nil. - */ -- (id)objectConformingToOverrideInResponderChain { - if ([self conformsToProtocol:@protocol(MDCElevationOverriding)]) { - return (id)self; - } - - UIResponder *nextResponder = self.nextResponder; - if ([nextResponder isKindOfClass:[UIViewController class]] && - [nextResponder conformsToProtocol:@protocol(MDCElevationOverriding)]) { - return (id)nextResponder; - } - - return nil; -} - -/** - Checks whether a @c UIView or it's managing @c UIViewController conform to @c - MDCElevation. - - @returns the conforming @c UIView then @c UIViewController, otherwise @c nil. - */ -- (id)objectConformingToElevationInResponderChain { - if ([self conformsToProtocol:@protocol(MDCElevatable)]) { - return (id)self; - } - - UIResponder *nextResponder = self.nextResponder; - if ([nextResponder isKindOfClass:[UIViewController class]] && - [nextResponder conformsToProtocol:@protocol(MDCElevatable)]) { - return (id)nextResponder; - } - - return nil; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h deleted file mode 100644 index 9d3b2c15..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - Custom gesture recognizer to observe the various ink response states. - - MDCInkGestureRecognizer is a continuous recognizer that tracks single touches and optionally - fails if the touch moves outside the recongizer's view. Multiple touches will cause the - recognizer to transition to the UIGestureRecognizerStateCancelled state. - */ -__deprecated_msg("Please use MDCRippleTouchController instead.") @interface MDCInkGestureRecognizer - : UIGestureRecognizer - -/** - Set the distance that causes the recognizer to cancel. - */ -@property(nonatomic, assign) CGFloat dragCancelDistance; - -/** - Set when dragging outside of the view causes the gesture recognizer to cancel. - - Defaults to YES. - */ -@property(nonatomic, assign) BOOL cancelOnDragOut; - -/** - Bounds inside of which the recognizer will recognize ink gestures, relative to self.view.frame. - - If set to CGRectNull (the default), then the recognizer will use self.view.bounds as the target - bounds. - - If cancelOnDragOut is YES and the user's touch moves beyond the target bounds inflated by - dragCancelDistance then the gesture is cancelled. - */ -@property(nonatomic) CGRect targetBounds; - -/** - Returns the point where the ink starts spreading from. - - @param view View which the point is relative to. - */ -- (CGPoint)touchStartLocationInView:(UIView *)view; - -/** Returns YES if the touch's current location is still within the target bounds. */ -- (BOOL)isTouchWithinTargetBounds; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m deleted file mode 100644 index d87a48f8..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkGestureRecognizer.m +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCInkGestureRecognizer.h" - -#import - -static const CGFloat kInkGestureDefaultDragCancelDistance = 20; - -@implementation MDCInkGestureRecognizer { - CGPoint _touchStartLocation; - CGPoint _touchCurrentLocation; - BOOL _cancelOnDragOut; -} - -- (instancetype)initWithTarget:(id)target action:(SEL)action { - self = [super initWithTarget:target action:action]; - if (self) { - _cancelOnDragOut = YES; - _dragCancelDistance = kInkGestureDefaultDragCancelDistance; - _targetBounds = CGRectNull; - self.cancelsTouchesInView = NO; - self.delaysTouchesEnded = NO; - } - return self; -} - -- (CGPoint)touchStartLocationInView:(UIView *)view { - return [view convertPoint:_touchStartLocation fromView:nil]; -} - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - [super touchesBegan:touches withEvent:event]; - if ([touches count] == 1) { - self.state = UIGestureRecognizerStateBegan; - _touchStartLocation = [[touches anyObject] locationInView:nil]; - _touchCurrentLocation = _touchStartLocation; - } else { - self.state = UIGestureRecognizerStateCancelled; - } -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - [super touchesMoved:touches withEvent:event]; - if (self.state == UIGestureRecognizerStateFailed) { - return; - } - - _touchCurrentLocation = [[touches anyObject] locationInView:nil]; - - // Cancel the gesture if it is too far away. - if (_cancelOnDragOut && ![self isTouchWithinTargetBounds]) { - self.state = UIGestureRecognizerStateCancelled; - } else { - self.state = UIGestureRecognizerStateChanged; - } -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - [super touchesEnded:touches withEvent:event]; - self.state = UIGestureRecognizerStateEnded; -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - [super touchesCancelled:touches withEvent:event]; - self.state = UIGestureRecognizerStateCancelled; -} - -- (BOOL)isTouchWithinTargetBounds { - CGRect targetBounds = [self effectiveTargetBounds]; - CGRect boundsInWindowCoord = [self.view convertRect:targetBounds toView:nil]; - boundsInWindowCoord = - CGRectInset(boundsInWindowCoord, -_dragCancelDistance, -_dragCancelDistance); - return CGRectContainsPoint(boundsInWindowCoord, _touchCurrentLocation); -} - -#pragma mark - Private methods - -- (CGRect)effectiveTargetBounds { - return CGRectEqualToRect(_targetBounds, CGRectNull) ? self.view.bounds : _targetBounds; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h deleted file mode 100644 index 7dc112d6..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.h +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCInkTouchControllerDelegate.h" - -@class MDCInkGestureRecognizer; -@class MDCInkTouchController; -@class MDCInkView; -@protocol MDCInkTouchControllerDelegate; - -/** - MDCInkTouchController associates a MDCInkView with a UIGestureRecognizer to control the spread of - the ink. - - Subclasses should avoid overriding the UIGestureRecognizerDelegate gestureRecognizerShouldBegin: - and gestureRecognizer:shouldReceiveTouch: methods to avoid breaking - MDCInkTouchControllerDelegate. - - **NOTE:** The controller does not keep a strong reference to the view to which it is attaching an - ink view. - It is expected that the view will keep a strong reference to its own ink controller, or that the - view controller controlling the view will keep a strong reference to that view's ink controller. - */ -__deprecated_msg("Please use MDCRippleTouchController instead.") @interface MDCInkTouchController - : NSObject - -/** Weak reference to the view that responds to touch events. */ -@property(nonatomic, weak, readonly, nullable) UIView *view; - -/** - The ink view for clients who do not create their own ink views via the delegate. - */ -@property(nonatomic, strong, readonly, nonnull) MDCInkView *defaultInkView; - -/** Delegate to extend the behavior of the touch control. */ -@property(nonatomic, weak, nullable) id delegate; - -/** If YES, the gesture recognizer should delay the start of ink spread. Default is NO. */ -@property(nonatomic, assign) BOOL delaysInkSpread; - -/** The distance that causes the recognizer to cancel. Defaults to 20pt. */ -@property(nonatomic, assign) CGFloat dragCancelDistance; - -/** - Whether dragging outside of the view causes the gesture recognizer to cancel. - Defaults to YES. - */ -@property(nonatomic, assign) BOOL cancelsOnDragOut; - -/** - If enabled, the ink gesture will require failure of UIScrollView gesture recognizers in order to - activate. - - Defaults to NO. - */ -@property(nonatomic, assign) BOOL requiresFailureOfScrollViewGestures; - -/** - Bounds inside of which the recognizer will recognize ink gestures, relative to self.view.frame. - - If set to CGRectNull (the default), then the recognizer will use self.view.bounds as the target - bounds. - - If cancelsOnDragOut is YES and the user's touch moves beyond the target bounds inflated by - dragCancelDistance then the gesture is cancelled. - */ -@property(nonatomic) CGRect targetBounds; - -/** Gesture recognizer used to bind touch events to ink. */ -@property(nonatomic, strong, readonly, nonnull) MDCInkGestureRecognizer *gestureRecognizer; - -/** Unavailable, please use initWithView: instead. */ -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** - Initializes the controller. - - @param view View that responds to touch events for ink. - */ -- (nonnull instancetype)initWithView:(nonnull UIView *)view NS_DESIGNATED_INITIALIZER; - -/** - When called the @c defaultInkView is added to the @c view. - - This method is a no-op when the delegate conforms to @c inkTouchController:inkViewAtTouchLocation: - because this is how a client specifies a custom ink view. - - If you want to specify a specific z-index order for your inkView please conform to - @c inkTouchController:insertInkView:intoView: and do so there. - */ -- (void)addInkView; - -/** - Cancels all touch processing and dissipates the ink. - - This is useful if your application needs to remove the ink on scrolling, when preparing a view - for reuse, etc. - */ -- (void)cancelInkTouchProcessing; - -/** - Returns the ink view at a particular touch location. - - If the delegate responds to @c inkTouchController:inkViewAtLocation: then this method queries it. - Otherwise, if @c addInkView has been called and @c location is in the bounds of - @c self.defaultView, then that view is returned. If none of these conditions are met, @c nil is - returned. - - @param location The query location in the coordinates of @c self.view. - @return The ink view at the touch location, or nil. -*/ -- (MDCInkView *_Nullable)inkViewAtTouchLocation:(CGPoint)location; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m deleted file mode 100644 index 670b4020..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchController.m +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCInkTouchController.h" - -#import "MDCInkGestureRecognizer.h" -#import "MDCInkTouchControllerDelegate.h" -#import "MDCInkView.h" - -static const NSTimeInterval kInkTouchDelayInterval = 0.1; - -@interface MDCInkTouchController () -@property(nonatomic, strong) MDCInkView *addedInkView; -@property(nonatomic, strong) MDCInkView *defaultInkView; -@property(nonatomic, assign) BOOL shouldRespondToTouch; -@property(nonatomic, assign) CGPoint previousLocation; -@end - -@implementation MDCInkTouchController - -- (CGFloat)dragCancelDistance { - return _gestureRecognizer.dragCancelDistance; -} - -- (void)setDragCancelDistance:(CGFloat)dragCancelDistance { - _gestureRecognizer.dragCancelDistance = dragCancelDistance; -} - -- (BOOL)cancelsOnDragOut { - return _gestureRecognizer.cancelOnDragOut; -} - -- (void)setCancelsOnDragOut:(BOOL)cancelsOnDragOut { - _gestureRecognizer.cancelOnDragOut = cancelsOnDragOut; -} - -- (CGRect)targetBounds { - return _gestureRecognizer.targetBounds; -} - -- (void)setTargetBounds:(CGRect)targetBounds { - _gestureRecognizer.targetBounds = targetBounds; -} - -- (instancetype)initWithView:(UIView *)view { - self = [super init]; - if (self) { - _requiresFailureOfScrollViewGestures = NO; - - _gestureRecognizer = - [[MDCInkGestureRecognizer alloc] initWithTarget:self action:@selector(handleInkGesture:)]; - _gestureRecognizer.delegate = self; - - _view = view; - [_view addGestureRecognizer:_gestureRecognizer]; - - _defaultInkView = [[MDCInkView alloc] initWithFrame:view.bounds]; - _defaultInkView.inkColor = _defaultInkView.defaultInkColor; - _defaultInkView.autoresizingMask = - UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - } - return self; -} - -- (void)dealloc { - [_view removeGestureRecognizer:_gestureRecognizer]; - _gestureRecognizer.delegate = nil; -} - -- (void)addInkView { - if (![_delegate respondsToSelector:@selector(inkTouchController:inkViewAtTouchLocation:)]) { - _addedInkView = _defaultInkView; - - if ([_delegate respondsToSelector:@selector(inkTouchController:insertInkView:intoView:)]) { - [_delegate inkTouchController:self insertInkView:_addedInkView intoView:_view]; - } else { - [_view addSubview:_addedInkView]; - } - } -} - -- (void)cancelInkTouchProcessing { - [_addedInkView cancelAllAnimationsAnimated:YES]; -} - -- (MDCInkView *_Nullable)inkViewAtTouchLocation:(CGPoint)location { - MDCInkView *inkView; - if ([_delegate respondsToSelector:@selector(inkTouchController:inkViewAtTouchLocation:)]) { - inkView = [_delegate inkTouchController:self inkViewAtTouchLocation:location]; - } else { - CGPoint locationInInkCoords = [self.view convertPoint:location toView:_addedInkView]; - if ([_addedInkView pointInside:locationInInkCoords withEvent:nil]) { - inkView = _addedInkView; - } - } - return inkView; -} - -- (void)handleInkGesture:(MDCInkGestureRecognizer *)recognizer { - CGPoint touchLocation = [recognizer locationInView:_view]; - - switch (recognizer.state) { - case UIGestureRecognizerStateBegan: { - if ([_delegate respondsToSelector:@selector(inkTouchController:inkViewAtTouchLocation:)]) { - _addedInkView = [_delegate inkTouchController:self inkViewAtTouchLocation:touchLocation]; - if (!_addedInkView) { - return [self cancelInkGestureWithRecognizer:recognizer]; - } - NSAssert([_addedInkView isDescendantOfView:_view], - @"Ink view %@ returned by inkTouchController:inkViewAtTouchLocation: must be a " - "subview of base view %@", - _addedInkView, _view); - recognizer.targetBounds = [_addedInkView convertRect:_addedInkView.bounds toView:_view]; - } - - _shouldRespondToTouch = YES; - dispatch_time_t delayTime = - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * kInkTouchDelayInterval)); - dispatch_after(_delaysInkSpread ? delayTime : 0, dispatch_get_main_queue(), ^(void) { - [self touchBeganAtPoint:[recognizer locationInView:self.addedInkView] - touchLocation:touchLocation]; - }); - break; - } - case UIGestureRecognizerStatePossible: // Ignored - break; - case UIGestureRecognizerStateChanged: { - // Due to changes on iPhone 6s, possibly due to the force touch, - // @c UIGestureRecognizerStateChanged constantly fires. However, we do not want to cancel the - // ink unless the users moves. - if (_shouldRespondToTouch && !CGPointEqualToPoint(touchLocation, _previousLocation)) { - _shouldRespondToTouch = NO; - } - break; - } - case UIGestureRecognizerStateCancelled: - [_addedInkView cancelAllAnimationsAnimated:YES]; - _shouldRespondToTouch = NO; - break; - case UIGestureRecognizerStateRecognized: - [_addedInkView startTouchEndedAnimationAtPoint:touchLocation completion:nil]; - _shouldRespondToTouch = NO; - break; - case UIGestureRecognizerStateFailed: - [_addedInkView cancelAllAnimationsAnimated:YES]; - _shouldRespondToTouch = NO; - break; - } - - if (_shouldRespondToTouch) { - _previousLocation = touchLocation; - } else { - _previousLocation = CGPointZero; - } -} - -- (void)cancelInkGestureWithRecognizer:(MDCInkGestureRecognizer *)recognizer { - // To exit, disable the recognizer immediately which forces it to drop out of the current - // loop and prevent any state updates. Then re-enable to allow future gesture recognition. - recognizer.enabled = NO; - recognizer.enabled = YES; -} - -- (void)touchBeganAtPoint:(CGPoint)point touchLocation:(CGPoint)touchLocation { - if (_shouldRespondToTouch) { - [_addedInkView startTouchBeganAnimationAtPoint:point completion:nil]; - if ([_delegate respondsToSelector:@selector(inkTouchController: - didProcessInkView:atTouchLocation:)]) { - [_delegate inkTouchController:self - didProcessInkView:_addedInkView - atTouchLocation:touchLocation]; - } - _shouldRespondToTouch = NO; - } -} - -#pragma mark - UIGestureRecognizerDelegate - -- (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer - shouldRecognizeSimultaneouslyWithGestureRecognizer:(__unused UIGestureRecognizer *)other { - // Subclasses can override this to prioritize another recognizer. - return YES; -} - -- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - if ([_delegate respondsToSelector:@selector(inkTouchController: - shouldProcessInkTouchesAtTouchLocation:)]) { - CGPoint touchLocation = [gestureRecognizer locationInView:_view]; - return [_delegate inkTouchController:self shouldProcessInkTouchesAtTouchLocation:touchLocation]; - } - return YES; -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer - shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { - if (self.requiresFailureOfScrollViewGestures && - [otherGestureRecognizer.view isKindOfClass:[UIScrollView class]] && - ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && - ![otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { - return YES; - } - return NO; -} - -#pragma mark - Deprecations - -- (MDCInkView *)inkView { - return _defaultInkView; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h deleted file mode 100644 index 87cd14ea..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkTouchControllerDelegate.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCInkTouchController; -@class MDCInkView; - -@protocol MDCInkTouchControllerDelegate -@optional - -/** - Inserts the ink view into the given view. - - If this method is not implemented, the ink view is added as a subview of the view when the - controller's addInkView method is called. Delegates can choose to insert the ink view below the - contents as a background view. When inkTouchController:inkViewAtTouchLocation is implemented - this method will not be invoked. - - @param inkTouchController The ink touch controller. - @param inkView The ink view. - @param view The view to add the ink view to. - */ -- (void)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController - insertInkView:(nonnull UIView *)inkView - intoView:(nonnull UIView *)view; - -/** - Returns the ink view to use for a touch located at location in inkTouchController.view. - - If the delegate implements this method, the controller will not create an ink view of its own and - inkTouchController:insertInkView:intoView: will not be called. This method allows the delegate - to control the creation and reuse of ink views. - - @param inkTouchController The ink touch controller. - @param location The touch location in the coords of @c inkTouchController.view. - @return An ink view to use at the touch location. - */ -- (nullable MDCInkView *)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController - inkViewAtTouchLocation:(CGPoint)location; - -/** - Controls whether the ink touch controller should be processing touches. - - The touch controller will query this method to determine if it should start or continue to - process touches controlling the ink. Returning NO at the start of a gesture will prevent any ink - from being displayed, and returning NO in the middle of a gesture will cancel that gesture and - evaporate the ink. - - If not implemented then YES is assumed. - - @param inkTouchController The ink touch controller. - @param location The touch location relative to the inkTouchController view. - @return YES if the controller should process touches at @c location. - - @see cancelInkTouchProcessing - */ -- (BOOL)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController - shouldProcessInkTouchesAtTouchLocation:(CGPoint)location; - -/** - Notifies the receiver that the ink touch controller did process an ink view at the - touch location. - - @param inkTouchController The ink touch controller. - @param inkView The ink view. - @param location The touch location relative to the inkTouchController superView. - */ -- (void)inkTouchController:(nonnull MDCInkTouchController *)inkTouchController - didProcessInkView:(nonnull MDCInkView *)inkView - atTouchLocation:(CGPoint)location; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h deleted file mode 100644 index e2d065c4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.h +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCInkViewDelegate.h" - -@protocol MDCInkViewDelegate; - -/** Completion block signature for all ink animations. */ -typedef void (^MDCInkCompletionBlock)(void); - -/** Ink styles. */ -typedef NS_ENUM(NSInteger, MDCInkStyle) { - /** Ink is clipped to the view's bounds. */ - MDCInkStyleBounded, - /** Ink is not clipped to the view's bounds. */ - MDCInkStyleUnbounded -}; - -/** - A UIView that draws and animates the Material Design ink effect for touch interactions. - - There are two kinds of ink: - - Bounded ink: Ink that spreads from a point and is contained in the bounds of a UI element such as a - button. The ink is visually clipped to the bounds of the UI element. Bounded ink is the most - commonly-used ink in the system. Examples include basic Material buttons, list menu items, and tile - grids. - - Unbounded ink: Ink that spreads out from a point "on top" of other UI elements. It typically - reaches a maximum circle radius and then fades, unclipped by other UI elements. Typically used - when interacting with small UI elements such as navigation bar icons or slider "thumb" controls. - Examples include overflow menus, icon toggle buttons, and phone dialer keys. - - Note that the two kinds of ink are designed to have different animation parameters, that is, - bounded ink isn't just clipped unbounded ink. Whether the ink is bounded or not depends on the kind - of UI element the user is interacting with. - */ -__deprecated_msg("Please use MDCRippleView instead.") @interface MDCInkView : UIView - -/** - Ink view animation delegate. Clients set this delegate to receive updates when ink animations - start and end. - */ -@property(nonatomic, weak, nullable) id animationDelegate; - -/** - The style of ink for this view. Defaults to MDCInkStyleBounded. - - Changes only affect subsequent animations, not animations in progress. - */ -@property(nonatomic, assign) MDCInkStyle inkStyle; - -/** The foreground color of the ink. The default value is defaultInkColor. */ -@property(nonatomic, strong, null_resettable) UIColor *inkColor UI_APPEARANCE_SELECTOR; - -/** Default color used for ink if no color is specified. */ -@property(nonatomic, strong, readonly, nonnull) UIColor *defaultInkColor; - -/** - Maximum radius of the ink. If the radius <= 0 then half the length of the diagonal of self.bounds - is used. This value is ignored if @c inkStyle is set to MDCInkStyleBounded and @c - usesLegacyInkLayer is set to NO. - */ -@property(nonatomic, assign) CGFloat maxRippleRadius; - -/** - Use the older legacy version of the ink ripple. Default is YES. - */ -@property(nonatomic, assign) BOOL usesLegacyInkRipple; - -/** - Use a custom center for the ink splash. If YES, then customInkCenter is used, otherwise the - center of self.bounds is used. Default is NO. - - Affects behavior only if usesLegacyInkRipple is enabled. - */ -@property(nonatomic, assign) BOOL usesCustomInkCenter; - -/** - Custom center for the ink splash in the view’s coordinate system. - - Affects behavior only if both usesCustomInkCenter and usesLegacyInkRipple are enabled. - */ -@property(nonatomic, assign) CGPoint customInkCenter; - -/** - A block that is invoked when the @c MDCInkView receives a call to @c - traitCollectionDidChange:. The block is called after the call to the superclass. - */ -@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) - (MDCInkView *_Nonnull ink, UITraitCollection *_Nullable previousTraitCollection); - -/** - Start the first part of the "press and release" animation at a particular point. - - The "press and release" animation begins by fading in the ink ripple when this method is called. - - @param point The user interaction position in the view’s coordinate system. - @param completionBlock Block called after the completion of the animation. - */ -- (void)startTouchBeganAnimationAtPoint:(CGPoint)point - completion:(nullable MDCInkCompletionBlock)completionBlock; - -/** - Start the second part of the "press and release" animation at a particular point. - - The "press and release" animation ends by completing the ink ripple expansion while fading out when - this method is called. - - @param point The user interaction position in the view’s coordinate system. - @param completionBlock Block called after the completion of the animation. - */ -- (void)startTouchEndedAnimationAtPoint:(CGPoint)point - completion:(nullable MDCInkCompletionBlock)completionBlock; - -/** - Cancel all animations. - - @param animated If false, remove the animations immediately. - */ -- (void)cancelAllAnimationsAnimated:(BOOL)animated; - -/** - Start the first part of spreading the ink at a particular point. - - This begins by fading in the ink ripple when this method is called. - - @param point The user interaction position in the view’s coordinate system. - @param animated to add the ink sublayer with animation or not. - @param completionBlock Block called after the completion of the animation. - */ -- (void)startTouchBeganAtPoint:(CGPoint)point - animated:(BOOL)animated - withCompletion:(nullable MDCInkCompletionBlock)completionBlock; - -/** - Start the second part of evaporating the ink at a particular point. - - This ends by completing the ink ripple expansion while fading out when - this method is called. - - @param point The user interaction position in the view’s coordinate system. - @param animated to remove the ink sublayer with animation or not. - @param completionBlock Block called after the completion of the animation. - */ -- (void)startTouchEndAtPoint:(CGPoint)point - animated:(BOOL)animated - withCompletion:(nullable MDCInkCompletionBlock)completionBlock; - -/** - Enumerates the given view's subviews for an instance of MDCInkView and returns it if found, or - creates and adds a new instance of MDCInkView if not. - - This method is a convenience method for adding ink to an arbitrary view without needing to subclass - the target view. Use this method in situations where you expect there to be many distinct ink views - in existence for a single ink touch controller. Example scenarios include: - - - Adding ink to individual collection view/table view cells - - This method can be used in your MDCInkTouchController delegate's - -inkTouchController:inkViewAtTouchLocation; implementation. - */ -+ (nonnull MDCInkView *)injectedInkViewForView:(nonnull UIView *)view; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m deleted file mode 100644 index 58c80ee8..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkView.m +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCInkView.h" - -#import "private/MDCInkLayer.h" -#import "private/MDCLegacyInkLayer.h" -#import "MDCInkViewDelegate.h" -#import "MDCInkLayerDelegate.h" -#import "MDCLegacyInkLayerDelegate.h" - -@interface MDCInkPendingAnimation : NSObject - -@property(nonatomic, weak) CALayer *animationSourceLayer; -@property(nonatomic, strong) NSString *keyPath; -@property(nonatomic, strong) id fromValue; -@property(nonatomic, strong) id toValue; - -@end - -@interface MDCInkView () - -@property(nonatomic, strong) CAShapeLayer *maskLayer; -@property(nonatomic, copy) MDCInkCompletionBlock startInkRippleCompletionBlock; -@property(nonatomic, copy) MDCInkCompletionBlock endInkRippleCompletionBlock; -@property(nonatomic, strong) MDCInkLayer *activeInkLayer; - -// Legacy ink ripple -@property(nonatomic, readonly, weak) MDCLegacyInkLayer *inkLayer; - -@end - -@implementation MDCInkView - -+ (Class)layerClass { - return [MDCLegacyInkLayer class]; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonMDCInkViewInit]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonMDCInkViewInit]; - } - return self; -} - -- (void)commonMDCInkViewInit { - self.userInteractionEnabled = NO; - self.backgroundColor = [UIColor clearColor]; - self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - self.layer.delegate = self; - self.inkColor = self.defaultInkColor; - _usesLegacyInkRipple = YES; - - // Use mask layer when the superview has a shadowPath. - _maskLayer = [CAShapeLayer layer]; - _maskLayer.delegate = self; - - self.inkLayer.animationDelegate = self; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - if (self.inkStyle == MDCInkStyleUnbounded) { - self.layer.mask = nil; - } else if (self.superview.layer.shadowPath) { - // If the superview has a shadowPath make sure ink does not spread outside of the shadowPath. - self.maskLayer.path = self.superview.layer.shadowPath; - self.layer.mask = _maskLayer; - } - - CGRect inkBounds = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); - self.layer.bounds = inkBounds; - - // When bounds change ensure all ink layer bounds are changed too. - for (CALayer *layer in self.layer.sublayers) { - if ([layer isKindOfClass:[MDCInkLayer class]]) { - MDCInkLayer *inkLayer = (MDCInkLayer *)layer; - inkLayer.bounds = inkBounds; - inkLayer.fillColor = self.inkColor.CGColor; - } - } -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - [super traitCollectionDidChange:previousTraitCollection]; - - if (self.traitCollectionDidChangeBlock) { - self.traitCollectionDidChangeBlock(self, previousTraitCollection); - } -} - -- (void)setInkStyle:(MDCInkStyle)inkStyle { - _inkStyle = inkStyle; - switch (inkStyle) { - case MDCInkStyleBounded: - self.inkLayer.masksToBounds = YES; - self.inkLayer.bounded = YES; - break; - case MDCInkStyleUnbounded: - self.inkLayer.masksToBounds = NO; - self.inkLayer.bounded = NO; - break; - } -} - -- (UIColor *)inkColor { - return self.inkLayer.inkColor; -} - -- (void)setInkColor:(UIColor *)inkColor { - self.inkLayer.inkColor = inkColor ?: self.defaultInkColor; -} - -- (CGFloat)maxRippleRadius { - return [self shouldIgnoreMaxRippleRadius] ? 0 : self.inkLayer.maxRippleRadius; -} - -- (void)setMaxRippleRadius:(CGFloat)radius { - self.inkLayer.maxRippleRadius = radius; - // This is required for legacy Ink so that the Ink bounds will be adjusted correctly - [self setNeedsLayout]; -} - -- (BOOL)shouldIgnoreMaxRippleRadius { - return !self.usesLegacyInkRipple && self.inkStyle == MDCInkStyleBounded; -} - -- (BOOL)usesCustomInkCenter { - return self.inkLayer.useCustomInkCenter; -} - -- (void)setUsesCustomInkCenter:(BOOL)usesCustomInkCenter { - self.inkLayer.useCustomInkCenter = usesCustomInkCenter; -} - -- (CGPoint)customInkCenter { - return self.inkLayer.customInkCenter; -} - -- (void)setCustomInkCenter:(CGPoint)customInkCenter { - self.inkLayer.customInkCenter = customInkCenter; -} - -- (MDCLegacyInkLayer *)inkLayer { - return (MDCLegacyInkLayer *)self.layer; -} - -- (void)startTouchBeganAnimationAtPoint:(CGPoint)point - completion:(MDCInkCompletionBlock)completionBlock { - [self startTouchBeganAtPoint:point animated:YES withCompletion:completionBlock]; -} - -- (void)startTouchBeganAtPoint:(CGPoint)point - animated:(BOOL)animated - withCompletion:(nullable MDCInkCompletionBlock)completionBlock { - if (self.usesLegacyInkRipple) { - [self.inkLayer spreadFromPoint:point completion:completionBlock]; - } else { - self.startInkRippleCompletionBlock = completionBlock; - MDCInkLayer *inkLayer = [MDCInkLayer layer]; - inkLayer.inkColor = self.inkColor; - inkLayer.maxRippleRadius = self.maxRippleRadius; - inkLayer.animationDelegate = self; - inkLayer.opacity = 0; - inkLayer.frame = self.bounds; - [self.layer addSublayer:inkLayer]; - [inkLayer startInkAtPoint:point animated:animated]; - self.activeInkLayer = inkLayer; - } -} - -- (void)startTouchEndAtPoint:(CGPoint)point - animated:(BOOL)animated - withCompletion:(nullable MDCInkCompletionBlock)completionBlock { - if (self.usesLegacyInkRipple) { - [self.inkLayer evaporateWithCompletion:completionBlock]; - } else { - self.endInkRippleCompletionBlock = completionBlock; - [self.activeInkLayer endInkAtPoint:point animated:animated]; - } -} - -- (void)startTouchEndedAnimationAtPoint:(CGPoint)point - completion:(MDCInkCompletionBlock)completionBlock { - [self startTouchEndAtPoint:point animated:YES withCompletion:completionBlock]; -} - -- (void)cancelAllAnimationsAnimated:(BOOL)animated { - if (self.usesLegacyInkRipple) { - [self.inkLayer resetAllInk:animated]; - } else { - NSArray *sublayers = [self.layer.sublayers copy]; - for (CALayer *layer in sublayers) { - if ([layer isKindOfClass:[MDCInkLayer class]]) { - MDCInkLayer *inkLayer = (MDCInkLayer *)layer; - if (animated) { - [inkLayer endAnimationAtPoint:CGPointZero]; - } else { - [inkLayer removeFromSuperlayer]; - } - } - } - } -} - -- (UIColor *)defaultInkColor { - static UIColor *defaultInkColor; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - defaultInkColor = [[UIColor alloc] initWithWhite:0 alpha:(CGFloat)0.14]; - }); - return defaultInkColor; -} - -+ (MDCInkView *)injectedInkViewForView:(UIView *)view { - MDCInkView *foundInkView = nil; - for (MDCInkView *subview in view.subviews) { - if ([subview isKindOfClass:[MDCInkView class]]) { - foundInkView = subview; - break; - } - } - if (!foundInkView) { - foundInkView = [[MDCInkView alloc] initWithFrame:view.bounds]; - foundInkView.autoresizingMask = - UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [view addSubview:foundInkView]; - } - return foundInkView; -} - -#pragma mark - MDCLegacyInkLayerDelegate - -- (void)legacyInkLayerAnimationDidStart:(MDCLegacyInkLayer *)inkLayer { - if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidStart:)]) { - [self.animationDelegate inkAnimationDidStart:self]; - } -} - -- (void)legacyInkLayerAnimationDidEnd:(MDCLegacyInkLayer *)inkLayer { - if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidEnd:)]) { - [self.animationDelegate inkAnimationDidEnd:self]; - } -} - -#pragma mark - MDCInkLayerDelegate - -- (void)inkLayerAnimationDidStart:(MDCInkLayer *)inkLayer { - if (self.activeInkLayer == inkLayer && self.startInkRippleCompletionBlock) { - self.startInkRippleCompletionBlock(); - } - if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidStart:)]) { - [self.animationDelegate inkAnimationDidStart:self]; - } -} - -- (void)inkLayerAnimationDidEnd:(MDCInkLayer *)inkLayer { - if (self.activeInkLayer == inkLayer && self.endInkRippleCompletionBlock) { - self.endInkRippleCompletionBlock(); - } - if ([self.animationDelegate respondsToSelector:@selector(inkAnimationDidEnd:)]) { - [self.animationDelegate inkAnimationDidEnd:self]; - } -} - -#pragma mark - CALayerDelegate - -- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { - if ([event isEqualToString:@"path"] || [event isEqualToString:@"shadowPath"]) { - // We have to create a pending animation because if we are inside a UIKit animation block we - // won't know any properties of the animation block until it is commited. - MDCInkPendingAnimation *pendingAnim = [[MDCInkPendingAnimation alloc] init]; - pendingAnim.animationSourceLayer = self.superview.layer; - pendingAnim.fromValue = [layer.presentationLayer valueForKey:event]; - pendingAnim.toValue = nil; - pendingAnim.keyPath = event; - - return pendingAnim; - } - return nil; -} - -@end - -@implementation MDCInkPendingAnimation - -- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict { - if ([anObject isKindOfClass:[CAShapeLayer class]]) { - CAShapeLayer *layer = (CAShapeLayer *)anObject; - - // In order to synchronize our animation with UIKit animations we have to fetch the resizing - // animation created by UIKit and copy the configuration to our custom animation. - CAAnimation *boundsAction = [self.animationSourceLayer animationForKey:@"bounds.size"]; - if ([boundsAction isKindOfClass:[CABasicAnimation class]]) { - CABasicAnimation *animation = (CABasicAnimation *)[boundsAction copy]; - animation.keyPath = self.keyPath; - animation.fromValue = self.fromValue; - animation.toValue = self.toValue; - - [layer addAnimation:animation forKey:event]; - } - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h deleted file mode 100644 index 1afa84af..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MDCInkViewDelegate.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCInkView; - -/** - Delegate protocol for MDCInkView. Clients may implement this protocol to receive updates when ink - layer start and end. - */ -__deprecated_msg("Please use MDCRippleViewDelegate instead.") @protocol MDCInkViewDelegate - -@optional - -/** - Called when the ink ripple animation begins. - - @param inkView The MDCInkView that starts animating. - */ -- (void)inkAnimationDidStart:(nonnull MDCInkView *)inkView; - -/** - Called when the ink ripple animation ends. - - @param inkView The MDCInkView that ends animating. - */ -- (void)inkAnimationDidEnd:(nonnull MDCInkView *)inkView; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h deleted file mode 100644 index de515671..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/MaterialInk.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCInkGestureRecognizer.h" -#import "MDCInkTouchController.h" -#import "MDCInkTouchControllerDelegate.h" -#import "MDCInkView.h" -#import "MDCInkViewDelegate.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h deleted file mode 100644 index 711a7833..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCInkLayerDelegate.h" - -@protocol MDCInkLayerDelegate; - -/** - A Core Animation layer that draws and animates the ink effect. - - Quick summary of how the ink ripple works: - - 1. On touch down, ink spreads from the touch point. - 2. On touch down hold, ink continues to spread, but will gravitate to the center point - of the view. - 3. On touch up, the ink ripple opacity will start to decrease. - */ -@interface MDCInkLayer : CAShapeLayer - -/** - Ink layer animation delegate. Clients set this delegate to receive updates when ink layer - animations start and end. - */ -@property(nonatomic, weak, nullable) id animationDelegate; - -/** - The start ink ripple spread animation has started and is active. - */ -@property(nonatomic, assign, readonly, getter=isStartAnimationActive) BOOL startAnimationActive; - -/** - Delay time in milliseconds before the end ink ripple spread animation begins. - */ -@property(nonatomic, assign) CGFloat endAnimationDelay; - -/** - The radius the ink ripple grows to when ink ripple ends. - - Default value is half the diagonal of the containing frame plus 10pt. - */ -@property(nonatomic, assign) CGFloat finalRadius; - -/** - The radius the ink ripple starts to grow from when the ink ripple begins. - - Default value is half the diagonal of the containing frame multiplied by 0.6. - */ -@property(nonatomic, assign) CGFloat initialRadius; - -/** - Maximum radius of the ink. If this is not set then the final radius value is used. - */ -@property(nonatomic, assign) CGFloat maxRippleRadius; - -/** - The color of the ink ripple. - */ -@property(nonatomic, strong, nonnull) UIColor *inkColor; - -/** - Starts the ink ripple animation at a specified point. - */ -- (void)startAnimationAtPoint:(CGPoint)point; - -/** - Starts the ink ripple - - @param point the point where to start the ink ripple - @param animated if to animate the ripple or not - */ -- (void)startInkAtPoint:(CGPoint)point animated:(BOOL)animated; - -/** - Changes the opacity of the ink ripple depending on if touch point is contained within or - outside of the ink layer. - */ -- (void)changeAnimationAtPoint:(CGPoint)point; - -/** - Ends the ink ripple animation. - */ -- (void)endAnimationAtPoint:(CGPoint)point; - -/** - Ends the ink ripple - - @param point the point where to end the ink ripple - @param animated if to animate the ripple or not - */ -- (void)endInkAtPoint:(CGPoint)point animated:(BOOL)animated; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m deleted file mode 100644 index 6f53b0b0..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayer.m +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCInkLayer.h" - -#import "MDCInkLayerDelegate.h" - -static const CGFloat MDCInkLayerCommonDuration = (CGFloat)0.083; -static const CGFloat MDCInkLayerEndFadeOutDuration = (CGFloat)0.15; -static const CGFloat MDCInkLayerStartScalePositionDuration = (CGFloat)0.333; -static const CGFloat MDCInkLayerStartFadeHalfDuration = (CGFloat)0.167; -static const CGFloat MDCInkLayerStartFadeHalfBeginTimeFadeOutDuration = (CGFloat)0.25; - -static const CGFloat MDCInkLayerScaleStartMin = (CGFloat)0.2; -static const CGFloat MDCInkLayerScaleStartMax = (CGFloat)0.6; -static const CGFloat MDCInkLayerScaleDivisor = 300; - -static NSString *const MDCInkLayerOpacityString = @"opacity"; -static NSString *const MDCInkLayerPositionString = @"position"; -static NSString *const MDCInkLayerScaleString = @"transform.scale"; - -@implementation MDCInkLayer - -- (instancetype)init { - self = [super init]; - if (self) { - _inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; - } - return self; -} - -- (instancetype)initWithLayer:(id)layer { - self = [super initWithLayer:layer]; - if (self) { - _endAnimationDelay = 0; - _finalRadius = 0; - _initialRadius = 0; - _inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; - _startAnimationActive = NO; - if ([layer isKindOfClass:[MDCInkLayer class]]) { - MDCInkLayer *inkLayer = (MDCInkLayer *)layer; - _endAnimationDelay = inkLayer.endAnimationDelay; - _finalRadius = inkLayer.finalRadius; - _initialRadius = inkLayer.initialRadius; - _maxRippleRadius = inkLayer.maxRippleRadius; - _inkColor = inkLayer.inkColor; - _startAnimationActive = NO; - } - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - - if (self) { - _inkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; - } - - return self; -} - -- (void)setNeedsLayout { - [super setNeedsLayout]; - [self setRadiiWithRect:self.bounds]; -} - -- (void)setRadiiWithRect:(CGRect)rect { - self.initialRadius = - (CGFloat)(hypot(CGRectGetHeight(rect), CGRectGetWidth(rect)) / 2 * (CGFloat)0.6); - self.finalRadius = (CGFloat)(hypot(CGRectGetHeight(rect), CGRectGetWidth(rect)) / 2 + 10); -} - -- (void)startAnimationAtPoint:(CGPoint)point { - [self startInkAtPoint:point animated:YES]; -} - -- (void)startInkAtPoint:(CGPoint)point animated:(BOOL)animated { - CGFloat radius = self.finalRadius; - if (self.maxRippleRadius > 0) { - radius = self.maxRippleRadius; - } - CGRect ovalRect = CGRectMake(CGRectGetWidth(self.bounds) / 2 - radius, - CGRectGetHeight(self.bounds) / 2 - radius, radius * 2, radius * 2); - UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:ovalRect]; - self.path = circlePath.CGPath; - self.fillColor = self.inkColor.CGColor; - if (!animated) { - self.opacity = 1; - self.position = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2); - } else { - self.opacity = 0; - self.position = point; - _startAnimationActive = YES; - - CAMediaTimingFunction *materialTimingFunction = - [[CAMediaTimingFunction alloc] initWithControlPoints:(float) 0.4:0:(float)0.2:1]; - - CGFloat scaleStart = - MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)) / MDCInkLayerScaleDivisor; - if (scaleStart < MDCInkLayerScaleStartMin) { - scaleStart = MDCInkLayerScaleStartMin; - } else if (scaleStart > MDCInkLayerScaleStartMax) { - scaleStart = MDCInkLayerScaleStartMax; - } - - CABasicAnimation *scaleAnim = [[CABasicAnimation alloc] init]; - scaleAnim.keyPath = MDCInkLayerScaleString; - scaleAnim.fromValue = @(scaleStart); - scaleAnim.toValue = @1; - scaleAnim.duration = MDCInkLayerStartScalePositionDuration; - scaleAnim.beginTime = MDCInkLayerCommonDuration; - scaleAnim.timingFunction = materialTimingFunction; - scaleAnim.fillMode = kCAFillModeForwards; - scaleAnim.removedOnCompletion = NO; - - UIBezierPath *centerPath = [UIBezierPath bezierPath]; - CGPoint startPoint = point; - CGPoint endPoint = - CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2); - [centerPath moveToPoint:startPoint]; - [centerPath addLineToPoint:endPoint]; - [centerPath closePath]; - - CAKeyframeAnimation *positionAnim = [[CAKeyframeAnimation alloc] init]; - positionAnim.keyPath = MDCInkLayerPositionString; - positionAnim.path = centerPath.CGPath; - positionAnim.keyTimes = @[ @0, @1 ]; - positionAnim.values = @[ @0, @1 ]; - positionAnim.duration = MDCInkLayerStartScalePositionDuration; - positionAnim.beginTime = MDCInkLayerCommonDuration; - positionAnim.timingFunction = materialTimingFunction; - positionAnim.fillMode = kCAFillModeForwards; - positionAnim.removedOnCompletion = NO; - - CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; - fadeInAnim.keyPath = MDCInkLayerOpacityString; - fadeInAnim.fromValue = @0; - fadeInAnim.toValue = @1; - fadeInAnim.duration = MDCInkLayerCommonDuration; - fadeInAnim.beginTime = MDCInkLayerCommonDuration; - fadeInAnim.timingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - fadeInAnim.fillMode = kCAFillModeForwards; - fadeInAnim.removedOnCompletion = NO; - - [CATransaction begin]; - CAAnimationGroup *animGroup = [[CAAnimationGroup alloc] init]; - animGroup.animations = @[ scaleAnim, positionAnim, fadeInAnim ]; - animGroup.duration = MDCInkLayerStartScalePositionDuration; - animGroup.fillMode = kCAFillModeForwards; - animGroup.removedOnCompletion = NO; - [CATransaction setCompletionBlock:^{ - self->_startAnimationActive = NO; - }]; - [self addAnimation:animGroup forKey:nil]; - [CATransaction commit]; - } - if ([self.animationDelegate respondsToSelector:@selector(inkLayerAnimationDidStart:)]) { - [self.animationDelegate inkLayerAnimationDidStart:self]; - } -} - -- (void)changeAnimationAtPoint:(CGPoint)point { - CGFloat animationDelay = 0; - if (self.startAnimationActive) { - animationDelay = - MDCInkLayerStartFadeHalfBeginTimeFadeOutDuration + MDCInkLayerStartFadeHalfDuration; - } - - BOOL viewContainsPoint = CGRectContainsPoint(self.bounds, point) ? YES : NO; - CGFloat currOpacity = self.presentationLayer.opacity; - CGFloat updatedOpacity = 0; - if (viewContainsPoint) { - updatedOpacity = 1; - } - - CABasicAnimation *changeAnim = [[CABasicAnimation alloc] init]; - changeAnim.keyPath = MDCInkLayerOpacityString; - changeAnim.fromValue = @(currOpacity); - changeAnim.toValue = @(updatedOpacity); - changeAnim.duration = MDCInkLayerCommonDuration; - changeAnim.beginTime = [self convertTime:(CACurrentMediaTime() + animationDelay) fromLayer:nil]; - changeAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - changeAnim.fillMode = kCAFillModeForwards; - changeAnim.removedOnCompletion = NO; - [self addAnimation:changeAnim forKey:nil]; -} - -- (void)endAnimationAtPoint:(CGPoint)point { - [self endInkAtPoint:point animated:YES]; -} - -- (void)endInkAtPoint:(CGPoint)point animated:(BOOL)animated { - if (self.startAnimationActive) { - self.endAnimationDelay = MDCInkLayerStartFadeHalfBeginTimeFadeOutDuration; - } - - CGFloat opacity = 1; - BOOL viewContainsPoint = CGRectContainsPoint(self.bounds, point) ? YES : NO; - if (!viewContainsPoint) { - opacity = 0; - } - - if (!animated) { - self.opacity = 0; - if ([self.animationDelegate respondsToSelector:@selector(inkLayerAnimationDidEnd:)]) { - [self.animationDelegate inkLayerAnimationDidEnd:self]; - } - [self removeFromSuperlayer]; - } else { - [CATransaction begin]; - CABasicAnimation *fadeOutAnim = [[CABasicAnimation alloc] init]; - fadeOutAnim.keyPath = MDCInkLayerOpacityString; - fadeOutAnim.fromValue = @(opacity); - fadeOutAnim.toValue = @0; - fadeOutAnim.duration = MDCInkLayerEndFadeOutDuration; - fadeOutAnim.beginTime = [self convertTime:(CACurrentMediaTime() + self.endAnimationDelay) - fromLayer:nil]; - fadeOutAnim.timingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - fadeOutAnim.fillMode = kCAFillModeForwards; - fadeOutAnim.removedOnCompletion = NO; - [CATransaction setCompletionBlock:^{ - if ([self.animationDelegate respondsToSelector:@selector(inkLayerAnimationDidEnd:)]) { - [self.animationDelegate inkLayerAnimationDidEnd:self]; - } - [self removeFromSuperlayer]; - }]; - [self addAnimation:fadeOutAnim forKey:nil]; - [CATransaction commit]; - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h deleted file mode 100644 index 8eb1797c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCInkLayerDelegate.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCInkLayer; - -/** - Delegate protocol for the MDCInkLayer. Clients may implement this protocol to receive updates when - ink layer animations start and end. - */ -@protocol MDCInkLayerDelegate - -@optional - -/** - Called when the ink ripple animation begins. - - @param inkLayer The MDCInkLayer that starts animating. - */ -- (void)inkLayerAnimationDidStart:(nonnull MDCInkLayer *)inkLayer; - -/** - Called when the ink ripple animation ends. - - @param inkLayer The MDCInkLayer that ends animating. - */ -- (void)inkLayerAnimationDidEnd:(nonnull MDCInkLayer *)inkLayer; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h deleted file mode 100644 index 263fa3b5..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer+Private.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCLegacyInkLayer.h" -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCLegacyInkLayerRippleDelegate.h" - -@class MDCLegacyInkLayerRipple; -@protocol MDCLegacyInkLayerRippleDelegate; - -@interface MDCLegacyInkLayer () - -/// A Boolean value indicating whether animations of this layer are in progress. -@property(nonatomic, assign, getter=isAnimating) BOOL animating; - -/// Enter any ink applied to the layer. Currently only exposed for testing. -- (void)enterAllInks; - -@end - -@interface MDCLegacyInkLayerRipple : CAShapeLayer -@end - -@interface MDCLegacyInkLayerForegroundRipple : MDCLegacyInkLayerRipple - -- (void)exit:(BOOL)animated; - -@end - -@interface MDCLegacyInkLayerBackgroundRipple : MDCLegacyInkLayerRipple - -- (void)exit:(BOOL)animated; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h deleted file mode 100644 index cc93304c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCLegacyInkLayerDelegate.h" - -@protocol MDCLegacyInkLayerDelegate; - -/** - A Core Animation layer that draws and animates the ink effect. - - Quick summary of how the ink ripple works: - - 1. On touch down, blast initiates from the touch point. - 2. On touch down hold, it continues to spread, but will gravitate to the center point - of the view. - 3. On touch up, the ink ripple will lose energy, opacity will start to decrease. - */ -@interface MDCLegacyInkLayer : CALayer - -/** - Ink layer animation delegate. Clients set this delegate to receive updates when ink layer - animations start and end. - */ -@property(nonatomic, weak, nullable) id animationDelegate; - -/** Clips the ripple to the bounds of the layer. */ -@property(nonatomic, assign, getter=isBounded) BOOL bounded; - -/** Maximum radius of the ink. No maximum if radius is 0 or less. This value is ignored if - @c bounded is set to |YES|.*/ -@property(nonatomic, assign) CGFloat maxRippleRadius; - -/** Set the foreground color of the ink. */ -@property(nonatomic, strong, nonnull) UIColor *inkColor; - -/** Spread duration. */ -@property(nonatomic, readonly, assign) NSTimeInterval spreadDuration; - -/** Evaporate duration */ -@property(nonatomic, readonly, assign) NSTimeInterval evaporateDuration; - -/** - Set to YES if the ink layer should be using a custom center. - */ -@property(nonatomic, assign) BOOL useCustomInkCenter; - -/** - Center point which ink gravitates towards. - - Ignored if useCustomInkCenter is not set. - */ -@property(nonatomic, assign) CGPoint customInkCenter; - -/** - Whether linear expansion should be used for the ink, rather than a Quantum curve. Useful for - ink which needs to fill the bounds of its view completely and leave those bounds at full speed. - */ -@property(nonatomic, assign) BOOL userLinearExpansion; - -/** - Reset any ink applied to the layer. - - @param animated Enables the ink ripple fade out animation. - */ -- (void)resetAllInk:(BOOL)animated; - -/** - Spreads the ink over the whole view. - - Can be called multiple times which will result in multiple ink ripples. - - @param completionBlock Block called after the completion of the animation. - @param point Point at which the ink spreads from. - */ -- (void)spreadFromPoint:(CGPoint)point completion:(void (^_Nullable)(void))completionBlock; - -/** - Dissipate ink blast, should be called on touch up. - - If there are multiple ripples at once, the oldest ripple will be evaporated. - - @param completionBlock Block called after the completion of the evaporation. - */ -- (void)evaporateWithCompletion:(void (^_Nullable)(void))completionBlock; - -/** - Dissipates the ink blast, but condenses to a point. Used for touch exit or cancel. - - If there are mulitple ripples, the oldest ripple will be evaporated. - - @param point Evaporate the ink towards the point. - @param completionBlock Block called after the completion of the evaporation. - */ -- (void)evaporateToPoint:(CGPoint)point completion:(void (^_Nullable)(void))completionBlock; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m deleted file mode 100644 index 7d29db72..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayer.m +++ /dev/null @@ -1,664 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCLegacyInkLayer.h" -#import "MDCLegacyInkLayer+Private.h" - -#import - -#import "MaterialAvailability.h" -#import "MDCLegacyInkLayerDelegate.h" -#import "MDCLegacyInkLayerRippleDelegate.h" - -static inline CGPoint MDCLegacyInkLayerInterpolatePoint(CGPoint start, - CGPoint end, - CGFloat offsetPercent) { - CGPoint centerOffsetPoint = CGPointMake(start.x + (end.x - start.x) * offsetPercent, - start.y + (end.y - start.y) * offsetPercent); - return centerOffsetPoint; -} - -static inline CGFloat MDCLegacyInkLayerRadiusBounds(CGFloat maxRippleRadius, - CGFloat inkLayerRectHypotenuse, - __unused BOOL bounded) { - if (maxRippleRadius > 0) { -#ifdef MDC_BOUNDED_INK_IGNORES_MAX_RIPPLE_RADIUS - if (!bounded) { - return maxRippleRadius; - } else { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSLog(@"Implementation of MDCInkView with |MDCInkStyle| MDCInkStyleBounded and " - @"maxRippleRadius has changed.\n\n" - @"MDCInkStyleBounded ignores maxRippleRadius. " - @"Please use |MDCInkStyle| MDCInkStyleUnbounded to continue using maxRippleRadius."); - }); - return inkLayerRectHypotenuse; - } -#else - return maxRippleRadius; -#endif - } else { - return inkLayerRectHypotenuse; - } -} - -static inline CGFloat MDCLegacyInkLayerRandom() { - const uint32_t max_value = 10000; - return (CGFloat)arc4random_uniform(max_value + 1) / max_value; -} - -static inline CGPoint MDCLegacyInkLayerRectGetCenter(CGRect rect) { - return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); -} - -static inline CGFloat MDCLegacyInkLayerRectHypotenuse(CGRect rect) { - return (CGFloat)hypot(CGRectGetWidth(rect), CGRectGetHeight(rect)); -} - -static NSString *const kInkLayerOpacity = @"opacity"; -static NSString *const kInkLayerPosition = @"position"; -static NSString *const kInkLayerScale = @"transform.scale"; - -// State tracking for ink. -typedef NS_ENUM(NSInteger, MDCInkRippleState) { - kInkRippleNone, - kInkRippleSpreading, - kInkRippleComplete, - kInkRippleCancelled, -}; - -#if MDC_AVAILABLE_SDK_IOS(10_0) -@interface MDCLegacyInkLayerRipple () -@end -#endif // MDC_AVAILABLE_SDK_IOS(10_0) - -@interface MDCLegacyInkLayerRipple () - -@property(nonatomic, assign, getter=isAnimationCleared) BOOL animationCleared; -@property(nonatomic, weak) id animationDelegate; -@property(nonatomic, assign) BOOL bounded; -@property(nonatomic, weak) CALayer *inkLayer; -@property(nonatomic, assign) CGFloat radius; -@property(nonatomic, assign) CGPoint point; -@property(nonatomic, assign) CGRect targetFrame; -@property(nonatomic, assign) MDCInkRippleState rippleState; -@property(nonatomic, strong) UIColor *color; -@end - -@implementation MDCLegacyInkLayerRipple - -- (instancetype)init { - self = [super init]; - if (self) { - _rippleState = kInkRippleNone; - _animationCleared = YES; - } - return self; -} - -- (void)setupRipple { - self.fillColor = self.color.CGColor; - CGFloat dim = self.radius * 2; - self.frame = CGRectMake(0, 0, dim, dim); - UIBezierPath *ripplePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, dim, dim)]; - self.path = ripplePath.CGPath; -} - -- (void)enter { - [self.animationDelegate animationDidStart:self]; - - _rippleState = kInkRippleSpreading; - [_inkLayer addSublayer:self]; - _animationCleared = NO; -} - -- (void)exit { - if (_rippleState != kInkRippleCancelled) { - _rippleState = kInkRippleComplete; - } -} - -- (CAKeyframeAnimation *)opacityAnimWithValues:(NSArray *)values - times:(NSArray *)times { - CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:kInkLayerOpacity]; - anim.fillMode = kCAFillModeForwards; - anim.keyTimes = times; - anim.removedOnCompletion = NO; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - anim.values = values; - return anim; -} - -- (CAKeyframeAnimation *)positionAnimWithPath:(CGPathRef)path - duration:(CGFloat)duration - timingFunction:(CAMediaTimingFunction *)timingFunction { - CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:kInkLayerPosition]; - anim.duration = duration; - anim.fillMode = kCAFillModeForwards; - anim.path = path; - anim.removedOnCompletion = NO; - anim.timingFunction = timingFunction; - return anim; -} - -- (CAKeyframeAnimation *)scaleAnimWithValues:(NSArray *)values - times:(NSArray *)times { - CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:kInkLayerScale]; - anim.fillMode = kCAFillModeForwards; - anim.keyTimes = times; - anim.removedOnCompletion = NO; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - anim.values = values; - return anim; -} - -- (CAMediaTimingFunction *)logDecelerateEasing { - // This bezier curve is an approximation of a log curve. - return - [[CAMediaTimingFunction alloc] initWithControlPoints:(float) - 0.157:(float)0.72:(float)0.386:(float)0.987]; -} - -- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)finished { - if (!self.isAnimationCleared) { - [self.animationDelegate animationDidStop:anim shapeLayer:self finished:finished]; - } -} - -@end - -static CGFloat const kInkLayerForegroundBoundedOpacityExitDuration = (CGFloat)0.4; -static CGFloat const kInkLayerForegroundBoundedPositionExitDuration = (CGFloat)0.3; -static CGFloat const kInkLayerForegroundBoundedRadiusExitDuration = (CGFloat)0.8; -static CGFloat const kInkLayerForegroundRadiusGrowthMultiplier = 350; -static CGFloat const kInkLayerForegroundUnboundedEnterDelay = (CGFloat)0.08; -static CGFloat const kInkLayerForegroundUnboundedOpacityEnterDuration = (CGFloat)0.12; -static CGFloat const kInkLayerForegroundWaveTouchDownAcceleration = 1024; -static CGFloat const kInkLayerForegroundWaveTouchUpAcceleration = 3400; -static NSString *const kInkLayerForegroundOpacityAnim = @"foregroundOpacityAnim"; -static NSString *const kInkLayerForegroundPositionAnim = @"foregroundPositionAnim"; -static NSString *const kInkLayerForegroundScaleAnim = @"foregroundScaleAnim"; - -@interface MDCLegacyInkLayerForegroundRipple () -@property(nonatomic, assign) BOOL useCustomInkCenter; -@property(nonatomic, assign) CGPoint customInkCenter; -@property(nonatomic, strong) CAKeyframeAnimation *foregroundOpacityAnim; -@property(nonatomic, strong) CAKeyframeAnimation *foregroundPositionAnim; -@property(nonatomic, strong) CAKeyframeAnimation *foregroundScaleAnim; -@end - -@implementation MDCLegacyInkLayerForegroundRipple - -- (void)setupRipple { - CGFloat random = MDCLegacyInkLayerRandom(); - self.radius = - (CGFloat)((CGFloat)0.9 + random * (CGFloat)0.1) * kInkLayerForegroundRadiusGrowthMultiplier; - [super setupRipple]; -} - -- (void)enterWithCompletion:(void (^)(void))completionBlock { - [super enter]; - - if (self.bounded) { - _foregroundOpacityAnim = [self opacityAnimWithValues:@[ @0 ] times:@[ @0 ]]; - _foregroundScaleAnim = [self scaleAnimWithValues:@[ @0 ] times:@[ @0 ]]; - } else { - _foregroundOpacityAnim = [self opacityAnimWithValues:@[ @0, @1 ] times:@[ @0, @1 ]]; - _foregroundOpacityAnim.duration = kInkLayerForegroundUnboundedOpacityEnterDuration; - - CGFloat duration = (CGFloat)sqrt(self.radius / kInkLayerForegroundWaveTouchDownAcceleration); - _foregroundScaleAnim = - [self scaleAnimWithValues:@[ @0, @1 ] - times:@[ @(kInkLayerForegroundUnboundedEnterDelay), @1 ]]; - _foregroundScaleAnim.duration = duration; - - CGFloat xOffset = self.targetFrame.origin.x - self.inkLayer.frame.origin.x; - CGFloat yOffset = self.targetFrame.origin.y - self.inkLayer.frame.origin.y; - - UIBezierPath *movePath = [UIBezierPath bezierPath]; - CGPoint startPoint = CGPointMake(self.point.x + xOffset, self.point.y + yOffset); - CGPoint endPoint = MDCLegacyInkLayerRectGetCenter(self.targetFrame); - if (self.useCustomInkCenter) { - endPoint = self.customInkCenter; - } - endPoint = CGPointMake(endPoint.x + xOffset, endPoint.y + yOffset); - [movePath moveToPoint:startPoint]; - [movePath addLineToPoint:endPoint]; - - CAMediaTimingFunction *linearTimingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - _foregroundPositionAnim = [self positionAnimWithPath:movePath.CGPath - duration:duration - timingFunction:linearTimingFunction]; - _foregroundPositionAnim.keyTimes = @[ @(kInkLayerForegroundUnboundedEnterDelay), @1 ]; - [self addAnimation:_foregroundPositionAnim forKey:kInkLayerForegroundPositionAnim]; - } - - [CATransaction begin]; - if (completionBlock) { - __weak MDCLegacyInkLayerForegroundRipple *weakSelf = self; - [CATransaction setCompletionBlock:^{ - MDCLegacyInkLayerForegroundRipple *strongSelf = weakSelf; - if (strongSelf.rippleState != kInkRippleCancelled) { - completionBlock(); - } - }]; - } - [self addAnimation:_foregroundOpacityAnim forKey:kInkLayerForegroundOpacityAnim]; - [self addAnimation:_foregroundScaleAnim forKey:kInkLayerForegroundScaleAnim]; - [CATransaction commit]; -} - -- (void)exit:(BOOL)animated { - self.rippleState = kInkRippleCancelled; - [self exit:animated completion:nil]; -} - -- (void)exit:(BOOL)animated completion:(void (^)(void))completionBlock { - [super exit]; - - if (!animated) { - [self removeAllAnimations]; - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - self.opacity = 0; - __weak MDCLegacyInkLayerForegroundRipple *weakSelf = self; - [CATransaction setCompletionBlock:^(void) { - MDCLegacyInkLayerForegroundRipple *strongSelf = weakSelf; - [strongSelf removeFromSuperlayer]; - [strongSelf.animationDelegate animationDidStop:nil shapeLayer:strongSelf finished:YES]; - }]; - [CATransaction commit]; - return; - } - - if (self.bounded) { - _foregroundOpacityAnim.values = @[ @1, @0 ]; - _foregroundOpacityAnim.duration = kInkLayerForegroundBoundedOpacityExitDuration; - - // Bounded ripples move slightly towards the center of the tap target. Unbounded ripples - // move to the center of the tap target. - - CGFloat xOffset = self.targetFrame.origin.x - self.inkLayer.frame.origin.x; - CGFloat yOffset = self.targetFrame.origin.y - self.inkLayer.frame.origin.y; - - CGPoint startPoint = CGPointMake(self.point.x + xOffset, self.point.y + yOffset); - CGPoint endPoint = MDCLegacyInkLayerRectGetCenter(self.targetFrame); - if (self.useCustomInkCenter) { - endPoint = self.customInkCenter; - } - endPoint = CGPointMake(endPoint.x + xOffset, endPoint.y + yOffset); - CGPoint centerOffsetPoint = - MDCLegacyInkLayerInterpolatePoint(startPoint, endPoint, (CGFloat)0.3); - UIBezierPath *movePath = [UIBezierPath bezierPath]; - [movePath moveToPoint:startPoint]; - [movePath addLineToPoint:centerOffsetPoint]; - - _foregroundPositionAnim = - [self positionAnimWithPath:movePath.CGPath - duration:kInkLayerForegroundBoundedPositionExitDuration - timingFunction:[self logDecelerateEasing]]; - _foregroundScaleAnim.values = @[ @0, @1 ]; - _foregroundScaleAnim.keyTimes = @[ @0, @1 ]; - _foregroundScaleAnim.duration = kInkLayerForegroundBoundedRadiusExitDuration; - } else { - NSNumber *opacityVal = [self.presentationLayer valueForKeyPath:kInkLayerOpacity]; - if (opacityVal == nil) { - opacityVal = [NSNumber numberWithFloat:0]; - } - CGFloat adjustedDuration = kInkLayerForegroundBoundedPositionExitDuration; - CGFloat normOpacityVal = opacityVal.floatValue; - CGFloat opacityDuration = normOpacityVal / 3; - _foregroundOpacityAnim.values = @[ opacityVal, @0 ]; - _foregroundOpacityAnim.duration = opacityDuration + adjustedDuration; - - NSNumber *scaleVal = [self.presentationLayer valueForKeyPath:kInkLayerScale]; - if (scaleVal == nil) { - scaleVal = [NSNumber numberWithFloat:0]; - } - CGFloat unboundedDuration = (CGFloat)sqrt(((1 - scaleVal.floatValue) * self.radius) / - (kInkLayerForegroundWaveTouchDownAcceleration + - kInkLayerForegroundWaveTouchUpAcceleration)); - _foregroundPositionAnim.duration = unboundedDuration + adjustedDuration; - _foregroundScaleAnim.values = @[ scaleVal, @1 ]; - _foregroundScaleAnim.duration = unboundedDuration + adjustedDuration; - } - - _foregroundOpacityAnim.keyTimes = @[ @0, @1 ]; - if (_foregroundOpacityAnim.duration < _foregroundScaleAnim.duration) { - _foregroundScaleAnim.delegate = self; - } else { - _foregroundOpacityAnim.delegate = self; - } - - _foregroundOpacityAnim.timingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - _foregroundPositionAnim.timingFunction = [self logDecelerateEasing]; - _foregroundScaleAnim.timingFunction = [self logDecelerateEasing]; - - [CATransaction begin]; - if (completionBlock) { - __weak MDCLegacyInkLayerForegroundRipple *weakSelf = self; - [CATransaction setCompletionBlock:^{ - MDCLegacyInkLayerForegroundRipple *strongSelf = weakSelf; - if (strongSelf.rippleState != kInkRippleCancelled) { - completionBlock(); - } - }]; - } - [self addAnimation:_foregroundOpacityAnim forKey:kInkLayerForegroundOpacityAnim]; - [self addAnimation:_foregroundPositionAnim forKey:kInkLayerForegroundPositionAnim]; - [self addAnimation:_foregroundScaleAnim forKey:kInkLayerForegroundScaleAnim]; - [CATransaction commit]; -} - -- (void)removeAllAnimations { - [super removeAllAnimations]; - _foregroundOpacityAnim = nil; - _foregroundPositionAnim = nil; - _foregroundScaleAnim = nil; - self.animationCleared = YES; -} - -@end - -static CGFloat const kInkLayerBackgroundOpacityEnterDuration = (CGFloat)0.6; -static CGFloat const kInkLayerBackgroundBaseOpacityExitDuration = (CGFloat)0.48; -static CGFloat const kInkLayerBackgroundFastEnterDuration = (CGFloat)0.12; -static NSString *const kInkLayerBackgroundOpacityAnim = @"backgroundOpacityAnim"; - -@interface MDCLegacyInkLayerBackgroundRipple () -@property(nonatomic, strong, nullable) CAKeyframeAnimation *backgroundOpacityAnim; -@end - -@implementation MDCLegacyInkLayerBackgroundRipple - -- (void)enter { - [super enter]; - _backgroundOpacityAnim = [self opacityAnimWithValues:@[ @0, @1 ] times:@[ @0, @1 ]]; - _backgroundOpacityAnim.duration = kInkLayerBackgroundOpacityEnterDuration; - [self addAnimation:_backgroundOpacityAnim forKey:kInkLayerBackgroundOpacityAnim]; -} - -- (void)exit:(BOOL)animated { - [super exit]; - - if (!animated) { - [self removeAllAnimations]; - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - self.opacity = 0; - __weak MDCLegacyInkLayerBackgroundRipple *weakSelf = self; - [CATransaction setCompletionBlock:^(void) { - MDCLegacyInkLayerBackgroundRipple *strongSelf = weakSelf; - [strongSelf removeFromSuperlayer]; - [strongSelf.animationDelegate animationDidStop:nil shapeLayer:strongSelf finished:YES]; - }]; - [CATransaction commit]; - return; - } - - NSNumber *opacityVal = [self.presentationLayer valueForKeyPath:kInkLayerOpacity]; - if (opacityVal == nil) { - opacityVal = [NSNumber numberWithFloat:0]; - } - CGFloat duration = kInkLayerBackgroundBaseOpacityExitDuration; - if (self.bounded) { - // The end (tap release) animation should continue at the opacity level of the start animation. - CGFloat enterDuration = (1 - opacityVal.floatValue / 1) * kInkLayerBackgroundFastEnterDuration; - duration += enterDuration; - _backgroundOpacityAnim = [self opacityAnimWithValues:@[ opacityVal, @1, @0 ] - times:@[ @0, @(enterDuration / duration), @1 ]]; - } else { - _backgroundOpacityAnim = [self opacityAnimWithValues:@[ opacityVal, @0 ] times:@[ @0, @1 ]]; - } - _backgroundOpacityAnim.duration = duration; - _backgroundOpacityAnim.delegate = self; - [self addAnimation:_backgroundOpacityAnim forKey:kInkLayerBackgroundOpacityAnim]; -} - -- (void)removeAllAnimations { - [super removeAllAnimations]; - _backgroundOpacityAnim = nil; - self.animationCleared = YES; -} - -@end - -@interface MDCLegacyInkLayer () - -/** - Reset the bottom-most ink applied to the layer with a completion handler to be called on completion - if applicable. - - @param animated Enables the ink ripple fade out animation. - @param completionBlock Block called after the completion of the animation. - */ -- (void)resetBottomInk:(BOOL)animated completion:(void (^)(void))completionBlock; - -/** - Reset the bottom-most ink applied to the layer with a completion handler to be called on completion - if applicable. - - @param animated Enables the ink ripple fade out animation. - @param point Evaporate the ink towards the point. - @param completionBlock Block called after the completion of the animation. - */ -- (void)resetBottomInk:(BOOL)animated - toPoint:(CGPoint)point - completion:(void (^)(void))completionBlock; - -@property(nonatomic, strong, nonnull) CAShapeLayer *compositeRipple; -@property(nonatomic, strong, nonnull) - NSMutableArray *foregroundRipples; -@property(nonatomic, strong, nonnull) - NSMutableArray *backgroundRipples; - -@end - -@implementation MDCLegacyInkLayer - -- (instancetype)init { - self = [super init]; - if (self) { - self.masksToBounds = YES; - [self commonMDCLegacyInkLayerInit]; - _animating = NO; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - - if (self) { - _animating = NO; - // Discard any sublayers, which should be the composite ripple and any active ripples - if (self.sublayers.count > 0) { - NSArray *sublayers = [self.sublayers copy]; - for (CALayer *sublayer in sublayers) { - [sublayer removeFromSuperlayer]; - } - } - [self commonMDCLegacyInkLayerInit]; - } - - return self; -} - -- (void)commonMDCLegacyInkLayerInit { - static UIColor *defaultInkColor; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - defaultInkColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.08]; - }); - - _bounded = YES; - _inkColor = defaultInkColor; - _compositeRipple = [CAShapeLayer layer]; - _foregroundRipples = [NSMutableArray array]; - _backgroundRipples = [NSMutableArray array]; - [self addSublayer:_compositeRipple]; -} - -- (void)layoutSublayers { - [super layoutSublayers]; - CGFloat radius = MDCLegacyInkLayerRadiusBounds( - _maxRippleRadius, MDCLegacyInkLayerRectHypotenuse(self.bounds) / 2, _bounded); - - CGRect rippleFrame = - CGRectMake(-(radius * 2 - self.bounds.size.width) / 2, - -(radius * 2 - self.bounds.size.height) / 2, radius * 2, radius * 2); - _compositeRipple.frame = rippleFrame; - CGRect rippleBounds = CGRectMake(0, 0, radius * 2, radius * 2); - CAShapeLayer *rippleMaskLayer = [CAShapeLayer layer]; - UIBezierPath *ripplePath = [UIBezierPath bezierPathWithOvalInRect:rippleBounds]; - rippleMaskLayer.path = ripplePath.CGPath; - _compositeRipple.mask = rippleMaskLayer; -} - -- (void)enterAllInks { - for (MDCLegacyInkLayerForegroundRipple *foregroundRipple in self.foregroundRipples) { - [foregroundRipple enterWithCompletion:nil]; - } - for (MDCLegacyInkLayerBackgroundRipple *backgroundRipple in self.backgroundRipples) { - [backgroundRipple enter]; - } -} - -- (void)resetAllInk:(BOOL)animated { - for (MDCLegacyInkLayerForegroundRipple *foregroundRipple in self.foregroundRipples) { - [foregroundRipple exit:animated]; - } - for (MDCLegacyInkLayerBackgroundRipple *backgroundRipple in self.backgroundRipples) { - [backgroundRipple exit:animated]; - } -} - -- (void)resetBottomInk:(BOOL)animated completion:(void (^)(void))completionBlock { - if (self.foregroundRipples.count > 0) { - [[self.foregroundRipples objectAtIndex:(self.foregroundRipples.count - 1)] - exit:animated - completion:completionBlock]; - } - if (self.backgroundRipples.count > 0) { - [[self.backgroundRipples objectAtIndex:(self.backgroundRipples.count - 1)] exit:animated]; - } -} - -- (void)resetBottomInk:(BOOL)animated - toPoint:(CGPoint)point - completion:(void (^)(void))completionBlock { - if (self.foregroundRipples.count > 0) { - MDCLegacyInkLayerForegroundRipple *foregroundRipple = - [self.foregroundRipples objectAtIndex:(self.foregroundRipples.count - 1)]; - foregroundRipple.point = point; - [foregroundRipple exit:animated completion:completionBlock]; - } - if (self.backgroundRipples.count > 0) { - [[self.backgroundRipples objectAtIndex:(self.backgroundRipples.count - 1)] exit:animated]; - } -} - -#pragma mark - Properties - -- (void)spreadFromPoint:(CGPoint)point completion:(void (^)(void))completionBlock { - // Create a mask layer before drawing the ink using the superlayer's shadowPath - // if it exists. This helps the FAB when it is not rectangular. - if (self.masksToBounds && self.superlayer.shadowPath) { - CAShapeLayer *mask = [CAShapeLayer layer]; - mask.path = self.superlayer.shadowPath; - mask.fillColor = [UIColor whiteColor].CGColor; - self.mask = mask; - } else { - self.mask = nil; - } - - CGFloat radius = MDCLegacyInkLayerRadiusBounds( - _maxRippleRadius, MDCLegacyInkLayerRectHypotenuse(self.bounds) / 2, _bounded); - - MDCLegacyInkLayerBackgroundRipple *backgroundRipple = - [[MDCLegacyInkLayerBackgroundRipple alloc] init]; - backgroundRipple.inkLayer = _compositeRipple; - backgroundRipple.targetFrame = self.bounds; - backgroundRipple.point = point; - backgroundRipple.color = self.inkColor; - backgroundRipple.radius = radius; - backgroundRipple.bounded = self.bounded; - backgroundRipple.animationDelegate = self; - [backgroundRipple setupRipple]; - - MDCLegacyInkLayerForegroundRipple *foregroundRipple = - [[MDCLegacyInkLayerForegroundRipple alloc] init]; - foregroundRipple.inkLayer = _compositeRipple; - foregroundRipple.targetFrame = self.bounds; - foregroundRipple.point = point; - foregroundRipple.color = self.inkColor; - foregroundRipple.radius = radius; - foregroundRipple.bounded = self.bounded; - foregroundRipple.animationDelegate = self; - foregroundRipple.useCustomInkCenter = self.useCustomInkCenter; - foregroundRipple.customInkCenter = self.customInkCenter; - [foregroundRipple setupRipple]; - - [backgroundRipple enter]; - [self.backgroundRipples addObject:backgroundRipple]; - [foregroundRipple enterWithCompletion:completionBlock]; - [self.foregroundRipples addObject:foregroundRipple]; -} - -- (void)evaporateWithCompletion:(void (^)(void))completionBlock { - [self resetBottomInk:YES completion:completionBlock]; -} - -- (void)evaporateToPoint:(CGPoint)point completion:(void (^)(void))completionBlock { - [self resetBottomInk:YES toPoint:point completion:completionBlock]; -} - -#pragma mark - MDCLegacyRippleInkLayerDelegate - -- (void)animationDidStart:(MDCLegacyInkLayerRipple *)layerRipple { - if (!self.isAnimating) { - self.animating = YES; - - if ([self.animationDelegate respondsToSelector:@selector(legacyInkLayerAnimationDidStart:)]) { - [self.animationDelegate legacyInkLayerAnimationDidStart:self]; - } - } -} - -- (void)animationDidStop:(__unused CAAnimation *)anim - shapeLayer:(MDCLegacyInkLayerRipple *)layerRipple - finished:(__unused BOOL)finished { - // Even when the ripple is "exited" without animation, we need to remove it from compositeRipple - [layerRipple removeFromSuperlayer]; - [layerRipple removeAllAnimations]; - - if ([layerRipple isKindOfClass:[MDCLegacyInkLayerForegroundRipple class]]) { - [self.foregroundRipples removeObject:(MDCLegacyInkLayerForegroundRipple *)layerRipple]; - } else if ([layerRipple isKindOfClass:[MDCLegacyInkLayerBackgroundRipple class]]) { - [self.backgroundRipples removeObject:(MDCLegacyInkLayerBackgroundRipple *)layerRipple]; - } - - // Check if all ink layer animations did finish and call animation end callback - if (self.isAnimating && self.foregroundRipples.count == 0 && self.backgroundRipples.count == 0) { - self.animating = NO; - if ([self.animationDelegate respondsToSelector:@selector(legacyInkLayerAnimationDidEnd:)]) { - [self.animationDelegate legacyInkLayerAnimationDidEnd:self]; - } - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h deleted file mode 100644 index 200e889a..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerDelegate.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCLegacyInkLayer; - -/** - Delegate protocol for the MDCLegacyInkLayer. Clients may implement this protocol to receive updates - when ink layer animations start and end. - */ -@protocol MDCLegacyInkLayerDelegate - -@optional - -/** - Called when the ink ripple animation begins. - - @param inkLayer The MDCLegacyInkLayer that starts animating. - */ -- (void)legacyInkLayerAnimationDidStart:(nonnull MDCLegacyInkLayer *)inkLayer; - -/** - Called when the ink ripple animation ends. - - @param inkLayer The MDCLegacyInkLayer that ends animating. - */ -- (void)legacyInkLayerAnimationDidEnd:(nonnull MDCLegacyInkLayer *)inkLayer; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h deleted file mode 100644 index 93320c7b..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCLegacyInkLayerRipple; - -@protocol MDCLegacyInkLayerRippleDelegate - -/// Called if MDCLegacyInkLayerRipple did start animating. -- (void)animationDidStart:(nonnull MDCLegacyInkLayerRipple *)layerRipple; - -/// Called for every MDCLegacyInkLayerRipple if an animation did end. -- (void)animationDidStop:(nullable CAAnimation *)anim - shapeLayer:(nullable CAShapeLayer *)layerRipple - finished:(BOOL)finished; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h deleted file mode 100644 index 2995a079..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCRippleTouchControllerDelegate.h" -#import "MDCRippleView.h" - -@protocol MDCRippleTouchControllerDelegate; - -/** - The MDCRippleTouchController is a convenience controller that adds all the needed touch tracking - and logic to provide the correct ripple effect based on the user interacting with the view the - ripple is added to. - */ -@interface MDCRippleTouchController : NSObject - -/** - A weak reference to the view that the ripple is added to, and that responds to the user - events. - */ -@property(nonatomic, weak, readonly, nullable) UIView *view; - -/** - The ripple view added to the view. - */ -@property(nonatomic, strong, readonly, nonnull) MDCRippleView *rippleView; - -/** - A delegate to extend the behavior of the touch controller. - */ -@property(nonatomic, weak, nullable) id delegate; - -/** - The gesture recognizer used to bind the touch events to the ripple. - */ -@property(nonatomic, strong, readonly, nonnull) UILongPressGestureRecognizer *gestureRecognizer; - -/** - If set to NO, the ripple gesture will fail and not be initiated if there are other competing - gestures that belong to a UIScrollView. This helps the ripple not be invoked if a user wants - to scroll but does so while tapping on a view that incorporates a ripple. - - Defaults to YES. - */ -@property(nonatomic, assign) BOOL shouldProcessRippleWithScrollViewGestures; - -/** - Initializes the controller and adds the initialized ripple view as a subview of the provided view. - - Note: When using this initializer, calling `addRippleToView:` isn't needed. - - @param view The view that responds to the touch events for the ripple. The ripple - is added to it as a subview. - @return an MDCRippleTouchController instance. - */ -- (nonnull instancetype)initWithView:(nonnull UIView *)view; - -/** - Initializes the controller. - - Note: To effectively use the controller a call to `addRippleToView:` is needed to provide a view - that responds to the touch events for the ripple. The ripple is added to the view as a subview. - - @return an MDCRippleTouchController instance. - */ -- (nonnull instancetype)init; - -/** - Initializes the controller and based on the deferred parameter is adding the ripple view - immediately as subview of the priovided view. - - @Note If YES is passed for the deferred parameter and - @c rippleTouchController:insertRippleView:intoView: is not implemented, the rippleview will be - automatically added as the top subview to the given view when the first tap event is happening. - When @c rippleTouchController:insertRippleView:intoView: is implemented, it's the responsibility of - the delegate to add the ripple view in the proper position within view's hierarchy if the delegate - method is called. - - @param view The view that responds to the touch events for the ripple. The ripple - is added to it as a subview. - @param deferred Wheter the insertion of the rippleView to the provided view should be - happened deferred - @return an MDCRippleTouchController instance. -*/ -- (nonnull instancetype)initWithView:(nonnull UIView *)view deferred:(BOOL)deferred; - -/** - Adds the ripple view as a subview to the provided view, and adds the ripple's gesture recognizer - to it. - - Note: This needs to be called if using the `init` initialized rather than the `initWithView:` - initializer. - - @param view The view that responds to the touch events for the ripple. The ripple - is added to it as a subview. - */ -- (void)addRippleToView:(nonnull UIView *)view; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m deleted file mode 100644 index 875d8fe2..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchController.m +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRippleTouchController.h" - -#import "MDCRippleTouchControllerDelegate.h" - -@implementation MDCRippleTouchController { - BOOL _tapWentOutsideOfBounds; - BOOL _deferred; - - struct { - unsigned int rippleTouchControllerShouldProcessRippleTouchesAtTouchLocation : 1; - unsigned int rippleTouchControllerDidProcessRippleViewAtTouchLocation : 1; - unsigned int rippleTouchControllerInsertRippleViewIntoView : 1; - unsigned int rippleTouchControllerRippleViewAtTouchLocation : 1; - } _delegateFlags; -} - -@synthesize rippleView = _rippleView; - -- (instancetype)initWithView:(UIView *)view { - return [self initWithView:view deferred:NO]; -} - -- (nonnull instancetype)initWithView:(nonnull UIView *)view deferred:(BOOL)deferred { - self = [self init]; - if (self) { - _deferred = deferred; - if (deferred) { - [self attachGestureRecognizerToView:view]; - } else { - [self configureRippleWithView:view]; - } - } - return self; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _gestureRecognizer = - [[UILongPressGestureRecognizer alloc] initWithTarget:self - action:@selector(handleRippleGesture:)]; - _gestureRecognizer.minimumPressDuration = 0; - _gestureRecognizer.delegate = self; - _gestureRecognizer.cancelsTouchesInView = NO; - _gestureRecognizer.delaysTouchesEnded = NO; - - _shouldProcessRippleWithScrollViewGestures = YES; - } - return self; -} - -- (void)dealloc { - [_view removeGestureRecognizer:_gestureRecognizer]; - _gestureRecognizer.delegate = nil; -} - -- (MDCRippleView *)rippleView { - if (_rippleView == nil) { - _rippleView = [[MDCRippleView alloc] init]; - } - return _rippleView; -} - -- (void)setDelegate:(id)delegate { - _delegate = delegate; - - // The delegate's behavior - in terms of which optional methods are deemed to be - // implemented - is cached at assignment rather than inspected on each invocation. - _delegateFlags.rippleTouchControllerShouldProcessRippleTouchesAtTouchLocation = - [_delegate respondsToSelector:@selector(rippleTouchController: - shouldProcessRippleTouchesAtTouchLocation:)]; - _delegateFlags.rippleTouchControllerDidProcessRippleViewAtTouchLocation = [_delegate - respondsToSelector:@selector(rippleTouchController:didProcessRippleView:atTouchLocation:)]; - _delegateFlags.rippleTouchControllerInsertRippleViewIntoView = - [delegate respondsToSelector:@selector(rippleTouchController:insertRippleView:intoView:)]; - _delegateFlags.rippleTouchControllerRippleViewAtTouchLocation = - [_delegate respondsToSelector:@selector(rippleTouchController:rippleViewAtTouchLocation:)]; -} - -- (void)addRippleToView:(UIView *)view { - [self configureRippleWithView:view]; -} - -- (void)configureRippleWithView:(UIView *)view { - [self attachGestureRecognizerToView:view]; - [self insertRippleViewIntoView:view]; -} - -- (void)attachGestureRecognizerToView:(UIView *)view { - [_view removeGestureRecognizer:_gestureRecognizer]; - _view = view; - [_view addGestureRecognizer:_gestureRecognizer]; -} - -- (void)insertRippleViewIntoView:(UIView *)view { - if (!_delegateFlags.rippleTouchControllerRippleViewAtTouchLocation) { - // Insert the rippleView to the _view - MDCRippleView *rippleView = self.rippleView; - if (_delegateFlags.rippleTouchControllerInsertRippleViewIntoView) { - [_delegate rippleTouchController:self insertRippleView:rippleView intoView:view]; - } else { - [_view addSubview:rippleView]; - } - rippleView.frame = view.bounds; - } -} - -- (void)handleRippleGesture:(UILongPressGestureRecognizer *)recognizer { - CGPoint touchLocation = [recognizer locationInView:_view]; - - switch (recognizer.state) { - case UIGestureRecognizerStateBegan: { - if (_delegateFlags.rippleTouchControllerRippleViewAtTouchLocation) { - _rippleView = [_delegate rippleTouchController:self - rippleViewAtTouchLocation:touchLocation]; - if (!_rippleView) { - // If we find that a return isn't enough here, we may need to disable and then - // re-enable the recognizer so there are no side effects. - return; - } - } else { - MDCRippleView *rippleView = self.rippleView; - if (_deferred && rippleView.superview != _view) { - [self insertRippleViewIntoView:_view]; - } - } - - [_rippleView beginRippleTouchDownAtPoint:[recognizer locationInView:self.rippleView] - animated:YES - completion:nil]; - if (_delegateFlags.rippleTouchControllerDidProcessRippleViewAtTouchLocation) { - [_delegate rippleTouchController:self - didProcessRippleView:_rippleView - atTouchLocation:touchLocation]; - } - break; - } - case UIGestureRecognizerStatePossible: // Ignored - break; - case UIGestureRecognizerStateChanged: { - BOOL pointContainedinBounds = CGRectContainsPoint(_view.bounds, touchLocation); - if (pointContainedinBounds && _tapWentOutsideOfBounds) { - _tapWentOutsideOfBounds = NO; - [_rippleView fadeInRippleAnimated:YES completion:nil]; - } else if (!pointContainedinBounds && !_tapWentOutsideOfBounds) { - _tapWentOutsideOfBounds = YES; - [_rippleView fadeOutRippleAnimated:YES completion:nil]; - } - break; - } - case UIGestureRecognizerStateEnded: { - [_rippleView beginRippleTouchUpAnimated:YES completion:nil]; - break; - } - case UIGestureRecognizerStateCancelled: - case UIGestureRecognizerStateFailed: { - [_rippleView cancelAllRipplesAnimated:YES completion:nil]; - break; - } - } -} - -#pragma mark - UIGestureRecognizerDelegate - -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer - shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { - if (!self.shouldProcessRippleWithScrollViewGestures && - [otherGestureRecognizer.view isKindOfClass:[UIScrollView class]] && - ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && - ![otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { - return YES; - } - return NO; -} - -- (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer - shouldRecognizeSimultaneouslyWithGestureRecognizer:(__unused UIGestureRecognizer *)other { - // Subclasses can override this to prioritize another recognizer. - return YES; -} - -- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - if (_delegateFlags.rippleTouchControllerShouldProcessRippleTouchesAtTouchLocation) { - CGPoint touchLocation = [gestureRecognizer locationInView:_view]; - return [_delegate rippleTouchController:self - shouldProcessRippleTouchesAtTouchLocation:touchLocation]; - } - return YES; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h deleted file mode 100644 index b0d40a96..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleTouchControllerDelegate.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCRippleTouchController; -@class MDCRippleView; - -/** - Delegate methods for MDCRippleTouchController. - */ -@protocol MDCRippleTouchControllerDelegate -@optional - -/** - Controls whether the ripple touch controller should process touches. - - The touch controller will query this method to determine if it should start or continue to - process touches controlling the ripple. Returning NO at the start of a gesture will prevent any - ripple from being displayed, and returning NO in the middle of a gesture will cancel that gesture - and evaporate the ripple. - - If not implemented then YES is assumed. - - @param rippleTouchController The ripple touch controller. - @param location The touch location relative to the rippleTouchController view. - @return YES if the controller should process touches at @c location. - - @see cancelRippleTouchProcessing - */ -- (BOOL)rippleTouchController:(nonnull MDCRippleTouchController *)rippleTouchController - shouldProcessRippleTouchesAtTouchLocation:(CGPoint)location; - -/** - Notifies the receiver that the ripple touch controller did process an ripple view at the - touch location. - - @param rippleTouchController The ripple touch controller. - @param rippleView The ripple view. - @param location The touch location relative to the rippleTouchController superView. - */ -- (void)rippleTouchController:(nonnull MDCRippleTouchController *)rippleTouchController - didProcessRippleView:(nonnull MDCRippleView *)rippleView - atTouchLocation:(CGPoint)location; - -/** - Provides an opportunity to add the rippleView anywhere in the given view's hierarchy. - - If this method is not implemented, the ripple view is added as a subview of the given view provided - in the controller's `addRippleToView:` method or convenience initializer `initWithView:`. - Delegates can choose to insert the ripple view anywhere in the view hierarchy. - - @param rippleTouchController The ripple touch controller. - @param rippleView The ripple view. - @param view The requested superview of the ripple view. - */ -- (void)rippleTouchController:(nonnull MDCRippleTouchController *)rippleTouchController - insertRippleView:(nonnull MDCRippleView *)rippleView - intoView:(nonnull UIView *)view; - -/** - Returns the ripple view to use for a touch located at location in rippleTouchController.view. - - If the delegate implements this method, the controller will not create a ripple view of its own and - rippleTouchController:insertRippleView:intoView: will not be called. This method allows the - delegate to control the creation and reuse of ripple views. - - @param rippleTouchController The ripple touch controller. - @param location The touch location in the coordinates of @c rippleTouchController.view. - @return A ripple view to use at the touch location. - */ -- (nullable MDCRippleView *)rippleTouchController: - (nonnull MDCRippleTouchController *)rippleTouchController - rippleViewAtTouchLocation:(CGPoint)location; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h deleted file mode 100644 index f4837ea5..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.h +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCRippleViewDelegate.h" - -@protocol MDCRippleViewDelegate; - -/** - Convenience naming for the completion blocks the ripple animation provides. - */ -typedef void (^MDCRippleCompletionBlock)(void); - -/** - The different possible ripple styles. The ripple can either be bound to the view or not. - - - MDCRippleStyleBounded: The ripple is bound to the view. - - MDCRippleStyleUnbounded: The ripple is unbounded and ripples to the size of the smallest circle - that covers the entire rectangular bounds, plus an additional 10 points. - */ -typedef NS_ENUM(NSInteger, MDCRippleStyle) { - MDCRippleStyleBounded = 0, - MDCRippleStyleUnbounded, -}; - -/** - A UIView that draws and animates the Material Design ripple effect for touch interactions. - - The Ripple is a visual flourish consisting of an animated circle with various scale, opacity and - position animations applied simultaneously to give the illusion of ink applied to a paper surface. - - Our touch feedback ripple effect is a prominent entity across all our interactable components: - i.e., buttons, cards, tab bars, list items. - - There can be multiple riples occurring at the same time, each represented by an MDCRippleLayer. - */ -@interface MDCRippleView : UIView - -/** - The ripple view delegate. - */ -@property(nonatomic, weak, nullable) id rippleViewDelegate; - -/** - The ripple style indicating if the ripple is bounded or unbounded to the view. - */ -@property(nonatomic, assign) MDCRippleStyle rippleStyle; - -/** - The ripple's color. - */ -@property(nonatomic, strong, nonnull) UIColor *rippleColor; - -/** - The maximum radius the ripple can expand to. - - @note This property is ignored if @c rippleStyle is set to @c MDCRippleStyleBounded. - */ -@property(nonatomic, assign) CGFloat maximumRadius; - -/** - The ripple color of the currently active ripple. - */ -@property(nonatomic, strong, nonnull) UIColor *activeRippleColor; - -/** - When rippleStyle is MDCRippleStyleBounded, this flag affects whether the layer's mask will use - the super view's layer.shadowPath as the mask path. - - @note This behavior only takes effect if the ripple view's parent view has a non-nil shadowPath. - - @note This behavioral flag will eventually become NO by default and then be deleted. The YES - behavior is undesired because it assumes that the frame of the ripple view always matches the - bounds of the superview. When this assumption is false, such as when the ripple's origin is - non-zero, the ripple's mask tends to be bigger than it should be resulting in an incorrectly - clipped ripple effect. Consider disabling this behavior and explicitly setting a layer mask - instead. - - Changing this value to NO does not clear the mask if it was already set. - - Default value is YES. - */ -@property(nonatomic, assign) BOOL usesSuperviewShadowLayerAsMask; - -/** - A block that is invoked when the @c MDCRippleView receives a call to @c - traitCollectionDidChange:. The block is called after the call to the superclass. - */ -@property(nonatomic, copy, nullable) void (^traitCollectionDidChangeBlock) - (MDCRippleView *_Nonnull ripple, UITraitCollection *_Nullable previousTraitCollection); - -/** - Cancels all the existing ripples. - - @param animated Whether to animate the cancellation of the ripples or not. - */ -- (void)cancelAllRipplesAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Fades the ripple in by changing its opacity. - - @param animated Whether or not the fade in should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)fadeInRippleAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Fades the ripple out by changing its opacity. - - @param animated Whether or not the fade in should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)fadeOutRippleAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Begins the ripple's touch down animation at the given point. This presents the ripple and leaves it - on the view. If animated, it animates the expanding ripple circle effect. - To then remove the ripple, `beginRippleTouchUpAnimated` needs to be called. - - @param point The point to start the ripple animation. - @param animated Whether or not the ripple should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)beginRippleTouchDownAtPoint:(CGPoint)point - animated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Begins the ripple's touch up animation. This remopves the ripple from the view. If animated, the - ripple dissolves using an animated opacity change. - - @param animated Whether or not the ripple should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)beginRippleTouchUpAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Enumerates the given view's subviews for an instance of MDCRippleView and returns it if found, or - creates and adds a new instance of MDCRippleView if not. - - This method is a convenience method for adding ripple to an arbitrary view without needing to - subclass the target view. Use this method in situations where you expect there to be many distinct - ripple views in existence for a single ripple touch controller. Example scenarios include: - - - Adding ripple to individual collection view/table view cells - - This method can be used in your MDCRippleTouchController delegate's - -rippleTouchController:rippleViewAtTouchLocation: implementation. - */ -+ (nonnull MDCRippleView *)injectedRippleViewForView:(nonnull UIView *)view; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m deleted file mode 100644 index 32124af8..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleView.m +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRippleView.h" -#import "private/MDCRippleLayer.h" - -#import "MaterialAvailability.h" -#import "MDCRippleViewDelegate.h" - -@interface MDCRippleView () - -@property(nonatomic, strong) MDCRippleLayer *activeRippleLayer; -@property(nonatomic, strong) CAShapeLayer *maskLayer; - -@end - -@interface MDCRipplePendingAnimation : NSObject - -@property(nonatomic, weak) CALayer *animationSourceLayer; -@property(nonatomic, strong) NSString *keyPath; -@property(nonatomic, strong) id fromValue; -@property(nonatomic, strong) id toValue; - -@end - -static const CGFloat kRippleDefaultAlpha = (CGFloat)0.12; -static const CGFloat kRippleFadeOutDelay = (CGFloat)0.15; - -@implementation MDCRippleView - -@synthesize activeRippleLayer = _activeRippleLayer; - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonMDCRippleViewInit]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonMDCRippleViewInit]; - } - return self; -} - -- (void)commonMDCRippleViewInit { - _usesSuperviewShadowLayerAsMask = YES; - self.userInteractionEnabled = NO; - self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - - static UIColor *defaultRippleColor; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - defaultRippleColor = [[UIColor alloc] initWithWhite:0 alpha:kRippleDefaultAlpha]; - }); - _rippleColor = defaultRippleColor; - _rippleStyle = MDCRippleStyleBounded; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - self.activeRippleLayer.fillColor = self.activeRippleColor.CGColor; -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - [super traitCollectionDidChange:previousTraitCollection]; - - if (self.traitCollectionDidChangeBlock) { - self.traitCollectionDidChangeBlock(self, previousTraitCollection); - } -} - -- (void)layoutSublayersOfLayer:(CALayer *)layer { - [super layoutSublayersOfLayer:layer]; - - NSArray *sublayers = self.layer.sublayers; - if (sublayers.count > 0) { - [self updateRippleStyle]; - } - for (CALayer *sublayer in sublayers) { - sublayer.frame = CGRectStandardize(self.bounds); - [sublayer setNeedsLayout]; - } -} - -- (void)setRippleStyle:(MDCRippleStyle)rippleStyle { - _rippleStyle = rippleStyle; - [self updateRippleStyle]; -} - -- (void)setUsesSuperviewShadowLayerAsMask:(BOOL)usesSuperviewShadowLayerAsMask { - _usesSuperviewShadowLayerAsMask = usesSuperviewShadowLayerAsMask; - - if (_usesSuperviewShadowLayerAsMask) { - [self setNeedsLayout]; - } -} - -- (void)updateRippleStyle { - self.layer.masksToBounds = (self.rippleStyle == MDCRippleStyleBounded); - if (self.rippleStyle == MDCRippleStyleBounded) { - if (self.usesSuperviewShadowLayerAsMask && self.superview.layer.shadowPath) { - if (!self.maskLayer) { - // Use mask layer when the superview has a shadowPath. - self.maskLayer = [CAShapeLayer layer]; - self.maskLayer.delegate = self; - } - self.maskLayer.path = self.superview.layer.shadowPath; - self.layer.mask = _maskLayer; - } - } else { - self.layer.mask = nil; - } -} - -- (void)cancelAllRipplesAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { - NSArray *sublayers = [self.layer.sublayers copy]; - if (animated) { - CFTimeInterval latestBeginTouchDownRippleTime = DBL_MIN; - for (CALayer *layer in sublayers) { - if ([layer isKindOfClass:[MDCRippleLayer class]]) { - MDCRippleLayer *rippleLayer = (MDCRippleLayer *)layer; - latestBeginTouchDownRippleTime = - MAX(latestBeginTouchDownRippleTime, rippleLayer.rippleTouchDownStartTime); - } - } - dispatch_group_t group = dispatch_group_create(); - for (CALayer *layer in sublayers) { - if ([layer isKindOfClass:[MDCRippleLayer class]]) { - MDCRippleLayer *rippleLayer = (MDCRippleLayer *)layer; - if (!rippleLayer.isStartAnimationActive) { - rippleLayer.rippleTouchDownStartTime = - latestBeginTouchDownRippleTime + kRippleFadeOutDelay; - } - dispatch_group_enter(group); - [rippleLayer endRippleAnimated:animated - completion:^{ - dispatch_group_leave(group); - }]; - } - } - dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - if (completion) { - completion(); - } - }); - } else { - for (CALayer *layer in sublayers) { - if ([layer isKindOfClass:[MDCRippleLayer class]]) { - MDCRippleLayer *rippleLayer = (MDCRippleLayer *)layer; - [rippleLayer removeFromSuperlayer]; - } - } - if (completion) { - completion(); - } - } -} - -- (MDCRippleLayer *)activeRippleLayer { - if (self.layer.sublayers.count < 1) { - return nil; - } - return _activeRippleLayer; -} - -- (void)setActiveRippleLayer:(MDCRippleLayer *)activeRippleLayer { - _activeRippleLayer = activeRippleLayer; - - // When the active ripple layer is set, a new ripple layer is created which takes - // its color from @c rippleColor. Therefore, @activeRippleColor now becomes that - // color. - self.activeRippleColor = self.rippleColor; -} - -- (void)setColorForRippleLayer:(MDCRippleLayer *)rippleLayer { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - if ([self.traitCollection respondsToSelector:@selector(performAsCurrentTraitCollection:)]) { - [self.traitCollection performAsCurrentTraitCollection:^{ - rippleLayer.fillColor = self.rippleColor.CGColor; - }]; - return; - } - } -#endif // MDC_AVAILABLE_SDK_IOS(13_0) - rippleLayer.fillColor = self.rippleColor.CGColor; -} - -- (void)beginRippleTouchDownAtPoint:(CGPoint)point - animated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion { - MDCRippleLayer *rippleLayer = [MDCRippleLayer layer]; - rippleLayer.rippleLayerDelegate = self; - [self updateRippleStyle]; - [self setColorForRippleLayer:rippleLayer]; - rippleLayer.frame = self.bounds; - if (self.rippleStyle == MDCRippleStyleUnbounded) { - rippleLayer.maximumRadius = self.maximumRadius; - } - [self.layer addSublayer:rippleLayer]; - [rippleLayer startRippleAtPoint:point animated:animated completion:completion]; - self.activeRippleLayer = rippleLayer; -} - -- (void)beginRippleTouchUpAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion { - // If all ripple animations are already cancelled and removed from the superlayer call the - // short circuit and call the completion handler directly. - if (self.activeRippleLayer == nil) { - if (completion) { - completion(); - } - return; - } - [self.activeRippleLayer endRippleAnimated:animated completion:completion]; -} - -- (void)fadeInRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { - // If all ripple animations are already cancelled and removed from the superlayer call the - // short circuit and call the completion handler directly. - if (self.activeRippleLayer == nil) { - if (completion) { - completion(); - } - return; - } - [self.activeRippleLayer fadeInRippleAnimated:animated completion:completion]; -} - -- (void)fadeOutRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { - // If all ripple animations are already cancelled and removed from the superlayer call the - // short circuit and call the completion handler directly. - if (self.activeRippleLayer == nil) { - if (completion) { - completion(); - } - return; - } - [self.activeRippleLayer fadeOutRippleAnimated:animated completion:completion]; -} - -- (void)setActiveRippleColor:(UIColor *)activeRippleColor { - _activeRippleColor = activeRippleColor; - self.activeRippleLayer.fillColor = activeRippleColor.CGColor; -} - -#pragma mark - MDCRippleLayerDelegate - -- (void)rippleLayerTouchDownAnimationDidBegin:(MDCRippleLayer *)rippleLayer { - if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchDownAnimationDidBegin:)]) { - [self.rippleViewDelegate rippleTouchDownAnimationDidBegin:self]; - } -} - -- (void)rippleLayerTouchDownAnimationDidEnd:(MDCRippleLayer *)rippleLayer { - if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchDownAnimationDidEnd:)]) { - [self.rippleViewDelegate rippleTouchDownAnimationDidEnd:self]; - } -} - -- (void)rippleLayerTouchUpAnimationDidBegin:(MDCRippleLayer *)rippleLayer { - if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchUpAnimationDidBegin:)]) { - [self.rippleViewDelegate rippleTouchUpAnimationDidBegin:self]; - } -} - -- (void)rippleLayerTouchUpAnimationDidEnd:(MDCRippleLayer *)rippleLayer { - if ([self.rippleViewDelegate respondsToSelector:@selector(rippleTouchUpAnimationDidEnd:)]) { - [self.rippleViewDelegate rippleTouchUpAnimationDidEnd:self]; - } -} - -#pragma mark - CALayerDelegate - -- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { - if ([event isEqualToString:@"path"] || [event isEqualToString:@"shadowPath"]) { - // We have to create a pending animation because if we are inside a UIKit animation block we - // won't know any properties of the animation block until it is commited. - MDCRipplePendingAnimation *pendingAnim = [[MDCRipplePendingAnimation alloc] init]; - pendingAnim.animationSourceLayer = self.superview.layer; - pendingAnim.fromValue = [layer.presentationLayer valueForKey:event]; - pendingAnim.toValue = nil; - pendingAnim.keyPath = event; - - return pendingAnim; - } - return nil; -} - -#pragma mark - Convenience API - -+ (MDCRippleView *)injectedRippleViewForView:(UIView *)view { - for (MDCRippleView *subview in view.subviews) { - if ([subview isKindOfClass:[MDCRippleView class]]) { - return subview; - } - } - - MDCRippleView *newRippleView = [[MDCRippleView alloc] initWithFrame:view.bounds]; - newRippleView.autoresizingMask = - UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [view addSubview:newRippleView]; - return newRippleView; -} - -@end - -@implementation MDCRipplePendingAnimation - -- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict { - if (![anObject isKindOfClass:[CAShapeLayer class]]) { - return; - } - - // In order to synchronize our animation with UIKit animations we have to fetch the resizing - // animation created by UIKit and copy the configuration to our custom animation. - CAShapeLayer *layer = (CAShapeLayer *)anObject; - CAAnimation *boundsAction = [self.animationSourceLayer animationForKey:@"bounds.size"]; - BOOL isBasicAnimation = [boundsAction isKindOfClass:[CABasicAnimation class]]; - if (!isBasicAnimation) { - NSAssert(isBasicAnimation || !boundsAction, - @"This animation synchronization does not support a bounds size change that " - @"isn't of a CABasicAnimation type."); - return; - } - CABasicAnimation *animation = (CABasicAnimation *)[boundsAction copy]; - animation.keyPath = self.keyPath; - animation.fromValue = self.fromValue; - animation.toValue = self.toValue; - - [layer addAnimation:animation forKey:event]; -} -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h deleted file mode 100644 index 7952b406..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCRippleViewDelegate.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCRippleView; - -/** - The ripple view delegate protocol. Clients may implement this protocol to receive updates on - the ripple's animation lifecycle. - */ -@protocol MDCRippleViewDelegate - -@optional - -/** - Called when the ripple view began its touch down animation. - - @param rippleView The MDCRippleView. - */ -- (void)rippleTouchDownAnimationDidBegin:(nonnull MDCRippleView *)rippleView; - -/** - Called when the ripple view ended its touch down animation. - - @param rippleView The MDCRippleView. - */ -- (void)rippleTouchDownAnimationDidEnd:(nonnull MDCRippleView *)rippleView; - -/** - Called when the ripple view began its touch up animation. - - @param rippleView The MDCRippleView. - */ -- (void)rippleTouchUpAnimationDidBegin:(nonnull MDCRippleView *)rippleView; - -/** - Called when the ripple view ended its touch up animation. - - @param rippleView The MDCRippleView. - */ -- (void)rippleTouchUpAnimationDidEnd:(nonnull MDCRippleView *)rippleView; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h deleted file mode 100644 index 354517e1..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRippleView.h" - -/** - Provides the current state of the ripple. The ripple is either in its normal state, or in the - selected state where the ripple remains spread on the view. - - - MDCRippleStateNormal: The ripple isn't currently presented. - - MDCRippleStateHighlighted: The ripple is activated and shown. - - MDCRippleStateSelected: The ripple is in the selected state. - - MDCRippleStateDragged: The ripple is in the dragged state. - */ -typedef NS_OPTIONS(NSInteger, MDCRippleState) { - MDCRippleStateNormal = 0, - MDCRippleStateHighlighted = 1 << 0, - MDCRippleStateSelected = 1 << 1, - MDCRippleStateDragged = 1 << 2, -}; - -/** - This class subclasses MDCRippleView which provides the Ripple functionality, and adds - to it support for states. By providing states, you can set the overlay color for each state, and - change the state of the RippleView by using its highlighted/selected/dragged API. - - This class, as opposed to MDCRippleView is less passive in its approach and listens to touches to - be able to record the last touch and initiate the Ripple from there, as opposed to receiving - the touch as input. It also is able to identify when a tap goes off/on the view, and fades in/out - the ripple accordingly. - - Lastly, the implementation for states follows Material Guidelines closely and is built to work - well with UICollectionViewCells, UITableViewCells, and UIControls. Therefore MDCStatefulRippleView - should be used when one wants to leverage the Material state system and should ideally be - configured/set alongside the UIKit APIs (i.e. UIControlState or cell's setSelected/setHighlighted). - */ -__attribute__((objc_subclassing_restricted)) @interface MDCStatefulRippleView : MDCRippleView - -/** - This BOOL is set to YES if the ripple is currently selected, or NO otherwise. - It only has significance if allowsSelection is set to YES. - - Defaults to NO. - */ -@property(nonatomic, getter=isSelected) BOOL selected; - -/** - This BOOL is set to YES if the ripple is currently highlighted, or NO otherwise. - - Note: The reason for not calling this property `highlighted` is due to UIKit's internal logic of - crawling and setting all subviews of a UICollectionViewCell to highlighted when a cell goes - into the selected state. Because we want the ripple view to imitate the state of the cell itself - when inserted into a cell, and not become the state of the cell's subviews, we have named the - property `rippleHighlighted` to make sure the state isn't altered by UIKit. - - Defaults to NO. - */ -@property(nonatomic, getter=isRippleHighlighted) BOOL rippleHighlighted; - -/** - This BOOL is set to YES if the ripple is currently dragged, or NO otherwise. - This state is only triggered manually by setting this property to YES. - - Defaults to NO. - */ -@property(nonatomic, getter=isDragged) BOOL dragged; - -/** - This BOOL is set to YES if the ripple allows selection, or NO otherwise. - - Note: If allowsSelection is set to NO, it will also set selected to NO if selected was YES prior. - - Defaults to NO. - */ -@property(nonatomic) BOOL allowsSelection; - -/** - Sets the color of the ripple for state. - - @param rippleColor The ripple color to set the ripple to. - @param state The state of the ripple in which to set the ripple color. - */ -- (void)setRippleColor:(nullable UIColor *)rippleColor forState:(MDCRippleState)state; - -/** - Gets the ripple color for the given state. - - @param state The ripple's state. - @return the color of the ripple for state. - */ -- (nullable UIColor *)rippleColorForState:(MDCRippleState)state; - -/** - The next three methods are important to get the correct behavior and functionality - for the stateful ripple. - The methods need to be invoked in the corresponding `touchesBegan`, `touchesMoved`, - `touchesEnded`, and `touchesCancelled` in the superview of this view. - More detailed information can be found for each method below. - */ -#pragma mark - Superview Touch Handling - -/** - The stateful ripple view should receive the initial touch so it knows where to initiate the - ripple effect from. It also lets the ripple view's `setHighlighted` know if it has been triggered - due to a touch. - - @param touches The touches, as provided by the superview's `touchesBegan:withEvent:`. - @param event The event, as provided by the superview's `touchesBegan:withEvent:`. - */ -- (void)touchesBegan:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; - -/** - The stateful ripple view should fade in an out as a held touch goes in and out of the view. - To identify that a touch is held and then moved we need to have the superview pass the touch to - the ripple so it can react appropriately. - - This class needs to be invoked in the `touchesMoved:withEvent:` of its superview before super is - called. This is because otherwise `setHighlighted` will be triggered prior to knowing if the - touch is outside the bounds or not and won't be able to act accordingly. - - @param touches The touches, as provided by the superview's `touchesMoved:withEvent:`. - @param event The event, as provided by the superview's `touchesMoved:withEvent:`. - */ -- (void)touchesMoved:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; - -/** - The stateful ripple view needs to identify an end of a touch for two reasons: - 1. To know the touch has ended so if `setHighlighted` isn't triggered by a touch, it shouldn't - animate the ripple. - 2. To dissolve the existing ripple if the touch is let go outside the hit target of the superview. - - This class needs to be invoked in the `touchesEnded:withEvent:` of its superview before super is - called. - - @param touches The touches, as provided by the superview's `touchesEnded:withEvent:`. - @param event The event, as provided by the superview's `touchesEnded:withEvent:`. - */ -- (void)touchesEnded:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; - -/** - The stateful ripple view needs to identify a cancellation of a touch for two reason: - 1. To know the touch has ended so if `setHighlighted` isn't triggered by a touch, it shouldn't - animate the ripple. - 2. To dissolve the existing ripple if the touch gets cancelled. - - This class needs to be invoked in the `touchesCancelled:withEvent:` of its superview before super - is called. - - @param touches The touches, as provided by the superview's `touchesCancelled:withEvent:`. - @param event The event, as provided by the superview's `touchesCancelled:withEvent:`. - */ -- (void)touchesCancelled:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m deleted file mode 100644 index 2f0342dd..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MDCStatefulRippleView.m +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCStatefulRippleView.h" -#import "private/MDCRippleLayer.h" - -static const CGFloat kDefaultRippleAlpha = (CGFloat)0.12; -static const CGFloat kDefaultRippleSelectedAlpha = (CGFloat)0.08; -static const CGFloat kDefaultRippleDraggedAlpha = (CGFloat)0.08; - -static UIColor *RippleSelectedColor(void) { - return [UIColor colorWithRed:(CGFloat)0.384 green:0 blue:(CGFloat)0.933 alpha:1]; -} - -@interface MDCStatefulRippleView () -@property(nonatomic, strong) MDCRippleLayer *activeRippleLayer; -@end - -@implementation MDCStatefulRippleView { - NSMutableDictionary *_rippleColors; - BOOL _tapWentOutsideOfBounds; - BOOL _tapWentInsideOfBounds; - BOOL _didReceiveTouch; - CGPoint _lastTouch; -} - -@dynamic activeRippleLayer; - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonMDCStatefulRippleViewInit]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)coder { - self = [super initWithCoder:coder]; - if (self) { - [self commonMDCStatefulRippleViewInit]; - } - return self; -} - -- (void)commonMDCStatefulRippleViewInit { - if (_rippleColors == nil) { - _rippleColors = [NSMutableDictionary dictionary]; - UIColor *selectionColor = RippleSelectedColor(); - _rippleColors[@(MDCRippleStateNormal)] = [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha]; - _rippleColors[@(MDCRippleStateHighlighted)] = [UIColor colorWithWhite:0 - alpha:kDefaultRippleAlpha]; - _rippleColors[@(MDCRippleStateSelected)] = - [selectionColor colorWithAlphaComponent:kDefaultRippleSelectedAlpha]; - _rippleColors[@(MDCRippleStateSelected | MDCRippleStateHighlighted)] = - [selectionColor colorWithAlphaComponent:kDefaultRippleAlpha]; - _rippleColors[@(MDCRippleStateDragged)] = [UIColor colorWithWhite:0 - alpha:kDefaultRippleDraggedAlpha]; - _rippleColors[@(MDCRippleStateDragged | MDCRippleStateHighlighted)] = - [UIColor colorWithWhite:0 alpha:kDefaultRippleDraggedAlpha]; - _rippleColors[@(MDCRippleStateSelected | MDCRippleStateDragged)] = - [selectionColor colorWithAlphaComponent:kDefaultRippleDraggedAlpha]; - } -} - -- (UIColor *)rippleColorForState:(MDCRippleState)state { - UIColor *rippleColor = _rippleColors[@(state)]; - if (rippleColor == nil && (state & MDCRippleStateDragged) != 0) { - rippleColor = _rippleColors[@(MDCRippleStateDragged)]; - } else if (rippleColor == nil && (state & MDCRippleStateSelected) != 0) { - rippleColor = _rippleColors[@(MDCRippleStateSelected)]; - } - - if (rippleColor == nil) { - rippleColor = _rippleColors[@(MDCRippleStateNormal)]; - } - return rippleColor; -} - -- (void)updateRippleColor { - UIColor *rippleColor = [self rippleColorForState:self.state]; - [self setRippleColor:rippleColor]; -} - -- (void)updateActiveRippleColor { - UIColor *rippleColor = [self rippleColorForState:self.state]; - [self setActiveRippleColor:rippleColor]; -} - -- (void)setRippleColor:(UIColor *)rippleColor forState:(MDCRippleState)state { - _rippleColors[@(state)] = rippleColor; - - [self updateRippleColor]; -} - -- (MDCRippleState)state { - NSInteger state = 0; - if (self.selected) { - state |= MDCRippleStateSelected; - } - if (self.rippleHighlighted) { - state |= MDCRippleStateHighlighted; - } - if (self.dragged) { - state |= MDCRippleStateDragged; - } - return state; -} - -- (void)setAllowsSelection:(BOOL)allowsSelection { - if (!allowsSelection && self.selected) { - self.selected = NO; - } - _allowsSelection = allowsSelection; -} - -- (void)setSelected:(BOOL)selected { - if (!self.allowsSelection) { - // If we disallow selection we don't want to apply any visual or state changes for selection. - return; - } - if (_tapWentOutsideOfBounds) { - // If the tap goes outside of bounds when a selection state change is triggered, we want to - // return early and not issue the selection state change as guidelines dictate that if a tap is - // let go outside the bounds, it should not trigger an action like issuing - // a selection/deselection. - return; - } - if (selected == _selected && self.activeRippleLayer) { - // If selected is already set to YES, and there is already an active ripple layer apparent, - // we want to return early so we don't add multiple selected overlays, as there can be only one. - return; - } - _selected = selected; - // Go into the selected state visually. - if (selected) { - if (!self.activeRippleLayer) { - // If we go into the selected state but a ripple layer doesn't exist yet, it means we went - // into this state without initially creating the ripple overlay by going through the - // highlighted state. This usually occurs when cells are reused and the selected state is - // manually set to show the cell's existing state. - [self updateRippleColor]; - [self beginRippleTouchDownAtPoint:_lastTouch animated:NO completion:nil]; - } else { - [self updateActiveRippleColor]; - } - } else { - // If we are no longer selecting, we cancel all the ripples. - [self updateRippleColor]; - [self cancelAllRipplesAnimated:YES completion:nil]; - } -} - -- (void)setRippleHighlighted:(BOOL)rippleHighlighted { - if (rippleHighlighted == _rippleHighlighted) { - return; - } - _rippleHighlighted = rippleHighlighted; - // Go into the highlighted state visually. - if (rippleHighlighted && !_tapWentInsideOfBounds) { - // If ripple becomes highlighted we initiate a ripple with animation. - [self updateRippleColor]; - [self beginRippleTouchDownAtPoint:_lastTouch animated:_didReceiveTouch completion:nil]; - } else if (!rippleHighlighted) { - // In cases where the ripple stops being highlighted, we can only dissolve the ripple if we are - // not going into selection (as in that case it will stay and become a selected color and be an - // overlay), or when it is already selected and therefore it means it will stay selected and - // the ripple should act similarly to going in and out of highlighted and offer a standard - // ripple touch feedback on top of the selected overlay. - BOOL notAllowingSelectionOrAlreadySelected = !self.allowsSelection || self.selected; - - // We should dissolve the ripple in these cases: - // 1. where this is a normal tap going in and out of highlighted indicating a ripple effect, - // same goes to when there is already a selected overlay on top of it. - // 2. when also we aren't currently in dragged because in dragged we keep the overlay there and - // when dragged is set to NO it releases all the overlays. - // 3. lastly also when the tap isn't currently out of the bounds of the surface, as in that case - // the behavior of the ripple returns to its original state as releasing outside the bounds - // acts as "no action was done". - BOOL shouldDissolveRipple = - notAllowingSelectionOrAlreadySelected && !self.dragged && !_tapWentOutsideOfBounds; - - if (shouldDissolveRipple) { - // We dissolve the ripple when highlighted is NO, unless we are going into - // selection or dragging. - [self updateRippleColor]; - [self beginRippleTouchUpAnimated:YES completion:nil]; - } - } -} - -- (void)setDragged:(BOOL)dragged { - if (dragged == _dragged) { - return; - } - _dragged = dragged; - // Go into the dragged state visually. - if (dragged) { - if (!self.activeRippleLayer) { - // If we go into the dragged state manually, without coming from the highlighted state, - // We present the ripple overlay instantly without animation. - [self updateRippleColor]; - [self beginRippleTouchDownAtPoint:_lastTouch animated:NO completion:nil]; - } else { - [self updateActiveRippleColor]; - } - } else { - // If we are no longer dragging, we cancel all the ripples. - [self updateRippleColor]; - [self cancelAllRipplesAnimated:YES completion:nil]; - } -} - -#pragma mark - Superview Touch Handling - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - UITouch *touch = touches.anyObject; - CGPoint point = [touch locationInView:self]; - // Once we find that the tap is inside the hit area insets of the encapsulating view (superview - // of the ripple view), we would want to capture the touch from where to initiate the ripple, - // and also initialize the values to indicate there was a touch, and the held tap's location - // to fade the ripple in and out if the help tap goes inside or outside the hit area. - _lastTouch = point; - if (!_didReceiveTouch) { - _didReceiveTouch = YES; - _tapWentInsideOfBounds = NO; - _tapWentOutsideOfBounds = NO; - } -} - -- (BOOL)pointInsideSuperview:(CGPoint)point withEvent:(UIEvent *)event { - return [self.superview pointInside:point withEvent:event]; -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - // When the touch is held and moved outside and inside the bounds of the surface, - // the ripple should gracefully fade out and in accordingly. - UITouch *touch = touches.anyObject; - CGPoint point = [touch locationInView:self]; - BOOL pointContainedInSuperview = [self pointInsideSuperview:point withEvent:event]; - if (pointContainedInSuperview && _tapWentOutsideOfBounds) { - _tapWentInsideOfBounds = YES; - _tapWentOutsideOfBounds = NO; - [self fadeInRippleAnimated:YES completion:nil]; - } else if (!pointContainedInSuperview && !_tapWentOutsideOfBounds) { - _tapWentOutsideOfBounds = YES; - [self fadeOutRippleAnimated:YES completion:nil]; - } -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - _didReceiveTouch = NO; - if (_tapWentOutsideOfBounds) { - [self beginRippleTouchUpAnimated:NO completion:nil]; - } -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - _didReceiveTouch = NO; - [self beginRippleTouchUpAnimated:YES completion:nil]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h deleted file mode 100644 index 55e16cbc..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/MaterialRipple.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRippleTouchController.h" -#import "MDCRippleTouchControllerDelegate.h" -#import "MDCRippleView.h" -#import "MDCRippleViewDelegate.h" -#import "MDCStatefulRippleView.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h deleted file mode 100644 index 37d07dcb..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -// TODO(b/151929968): Delete import of delegate headers when client code has been migrated to no -// longer import delegates as transitive dependencies. -#import "MDCRippleLayerDelegate.h" - -/** - Convenience naming for the completion blocks the ripple animation provides. - */ -typedef void (^MDCRippleCompletionBlock)(void); - -@protocol MDCRippleLayerDelegate; - -/** - The Ripple Layer presents and animates the ripple. There can be multiple Ripple Layers - as sublayers for MDCRippleView. The Ripple Layer subclasses CAShapeLayer to leverage the path - property so we can conveniently draw the ripple circle. - */ -@interface MDCRippleLayer : CAShapeLayer - -/** - The ripple layer delegate. - */ -@property(nonatomic, weak, nullable) id rippleLayerDelegate; - -/** - A bool indicating if the start animation is currently active for this ripple layer. - */ -@property(nonatomic, assign, readonly, getter=isStartAnimationActive) BOOL startAnimationActive; - -/** - The ripple's touch down animation start time. It is measured in seconds - as the current absolute time when the animation begins. - */ -@property(nonatomic, assign) CFTimeInterval rippleTouchDownStartTime; - -/** - The radius the ripple expands to when activated. - - @note This only impacts new ripples, if a ripple is already being animated this property will have - no impact. - */ -@property(nonatomic, assign) CGFloat maximumRadius; - -/** - Starts the ripple at the given point. - - @param point The point to start the ripple animation. - @param animated Whether or not the ripple should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)startRippleAtPoint:(CGPoint)point - animated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Ends the ripple. - - @param animated Whether or not the ripple should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)endRippleAnimated:(BOOL)animated completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Fades the ripple in by changing the layer's opacity. - - @param animated Whether or not the fade in should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)fadeInRippleAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; - -/** - Fades the ripple out by changing the layer's opacity. - - @param animated Whether or not the fade out should be animated or not. - @param completion A completion block called after the completion of the animation. - */ -- (void)fadeOutRippleAnimated:(BOOL)animated - completion:(nullable MDCRippleCompletionBlock)completion; -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m deleted file mode 100644 index d8ef82d8..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayer.m +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRippleLayer.h" - -#import "MaterialAnimationTiming.h" -#import "MDCRippleLayerDelegate.h" - -static const CGFloat kExpandRippleBeyondSurface = 10; -static const CGFloat kRippleStartingScale = (CGFloat)0.6; -static const CGFloat kRippleTouchDownDuration = (CGFloat)0.225; -static const CGFloat kRippleTouchUpDuration = (CGFloat)0.15; -static const CGFloat kRippleFadeInDuration = (CGFloat)0.075; -static const CGFloat kRippleFadeOutDuration = (CGFloat)0.075; -static const CGFloat kRippleFadeOutDelay = (CGFloat)0.15; - -static NSString *const kRippleLayerOpacityString = @"opacity"; -static NSString *const kRippleLayerPositionString = @"position"; -static NSString *const kRippleLayerScaleString = @"transform.scale"; - -static CGFloat GetInitialRippleRadius(CGRect rect) { - return MAX(CGRectGetWidth(rect), CGRectGetHeight(rect)) * kRippleStartingScale / 2.f; -} - -static CGFloat GetFinalRippleRadius(CGRect rect) { - return (CGFloat)(hypot(CGRectGetMidX(rect), CGRectGetMidY(rect)) + kExpandRippleBeyondSurface); -} - -@implementation MDCRippleLayer - -- (void)setNeedsLayout { - [super setNeedsLayout]; - - [self calculateRadiusAndSetPath]; - self.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); -} - -- (void)calculateRadiusAndSetPath { - [self setPathFromRadii:[self calculateRadius]]; -} - -- (void)setPathFromRadii:(CGFloat)radius { - CGRect ovalRect = CGRectMake(CGRectGetMidX(self.bounds) - radius, - CGRectGetMidY(self.bounds) - radius, radius * 2, radius * 2); - UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:ovalRect]; - self.path = circlePath.CGPath; -} - -- (CGFloat)calculateRadius { - return self.maximumRadius > 0 ? self.maximumRadius : GetFinalRippleRadius(self.bounds); -} - -- (void)startRippleAtPoint:(CGPoint)point - animated:(BOOL)animated - completion:(MDCRippleCompletionBlock)completion { - [self.rippleLayerDelegate rippleLayerTouchDownAnimationDidBegin:self]; - CGFloat finalRadius = [self calculateRadius]; - [self setPathFromRadii:finalRadius]; - self.opacity = 1; - self.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); - if (!animated) { - if (completion) { - completion(); - } - [self.rippleLayerDelegate rippleLayerTouchDownAnimationDidEnd:self]; - } else { - _startAnimationActive = YES; - - CGFloat startingScale = GetInitialRippleRadius(self.bounds) / finalRadius; - CABasicAnimation *scaleAnim = [[CABasicAnimation alloc] init]; - scaleAnim.keyPath = kRippleLayerScaleString; - scaleAnim.fromValue = @(startingScale); - scaleAnim.toValue = @1; - scaleAnim.timingFunction = - [CAMediaTimingFunction mdc_functionWithType:MDCAnimationTimingFunctionStandard]; - - UIBezierPath *centerPath = [UIBezierPath bezierPath]; - CGPoint startPoint = point; - CGPoint endPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); - [centerPath moveToPoint:startPoint]; - [centerPath addLineToPoint:endPoint]; - [centerPath closePath]; - - CAKeyframeAnimation *positionAnim = [[CAKeyframeAnimation alloc] init]; - positionAnim.keyPath = kRippleLayerPositionString; - positionAnim.path = centerPath.CGPath; - positionAnim.keyTimes = @[ @0, @1 ]; - positionAnim.values = @[ @0, @1 ]; - positionAnim.timingFunction = - [CAMediaTimingFunction mdc_functionWithType:MDCAnimationTimingFunctionStandard]; - - CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; - fadeInAnim.keyPath = kRippleLayerOpacityString; - fadeInAnim.fromValue = @0; - fadeInAnim.toValue = @1; - fadeInAnim.duration = kRippleFadeInDuration; - fadeInAnim.timingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - - [CATransaction begin]; - CAAnimationGroup *animGroup = [[CAAnimationGroup alloc] init]; - animGroup.animations = @[ scaleAnim, positionAnim, fadeInAnim ]; - animGroup.duration = kRippleTouchDownDuration; - [CATransaction setCompletionBlock:^{ - self->_startAnimationActive = NO; - if (completion) { - completion(); - } - [self.rippleLayerDelegate rippleLayerTouchDownAnimationDidEnd:self]; - }]; - [self addAnimation:animGroup forKey:nil]; - _rippleTouchDownStartTime = CACurrentMediaTime(); - [CATransaction commit]; - } -} - -- (void)fadeInRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { - [CATransaction begin]; - CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; - fadeInAnim.keyPath = kRippleLayerOpacityString; - fadeInAnim.fromValue = @0; - fadeInAnim.toValue = @1; - fadeInAnim.duration = animated ? kRippleFadeInDuration : 0; - fadeInAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - fadeInAnim.fillMode = kCAFillModeForwards; - fadeInAnim.removedOnCompletion = NO; - [CATransaction setCompletionBlock:^{ - if (completion) { - completion(); - } - }]; - [self addAnimation:fadeInAnim forKey:nil]; - [CATransaction commit]; -} - -- (void)fadeOutRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { - [CATransaction begin]; - CABasicAnimation *fadeInAnim = [[CABasicAnimation alloc] init]; - fadeInAnim.keyPath = kRippleLayerOpacityString; - fadeInAnim.fromValue = @1; - fadeInAnim.toValue = @0; - fadeInAnim.duration = animated ? kRippleFadeOutDuration : 0; - fadeInAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - fadeInAnim.fillMode = kCAFillModeForwards; - fadeInAnim.removedOnCompletion = NO; - [CATransaction setCompletionBlock:^{ - if (completion) { - completion(); - } - }]; - [self addAnimation:fadeInAnim forKey:nil]; - [CATransaction commit]; -} - -- (void)endRippleAnimated:(BOOL)animated completion:(MDCRippleCompletionBlock)completion { - CGFloat delay = 0; - if (self.startAnimationActive) { - delay = kRippleFadeOutDelay; - } - [self.rippleLayerDelegate rippleLayerTouchUpAnimationDidBegin:self]; - [CATransaction begin]; - CABasicAnimation *fadeOutAnim = [[CABasicAnimation alloc] init]; - fadeOutAnim.keyPath = kRippleLayerOpacityString; - fadeOutAnim.fromValue = @1; - fadeOutAnim.toValue = @0; - fadeOutAnim.duration = animated ? kRippleTouchUpDuration : 0; - fadeOutAnim.beginTime = [self convertTime:_rippleTouchDownStartTime + delay fromLayer:nil]; - fadeOutAnim.timingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - fadeOutAnim.fillMode = kCAFillModeForwards; - fadeOutAnim.removedOnCompletion = NO; - [CATransaction setCompletionBlock:^{ - if (completion) { - completion(); - } - [self.rippleLayerDelegate rippleLayerTouchUpAnimationDidEnd:self]; - [self removeFromSuperlayer]; - }]; - [self addAnimation:fadeOutAnim forKey:nil]; - [CATransaction commit]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h deleted file mode 100644 index eb2b08b3..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Ripple/src/private/MDCRippleLayerDelegate.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCRippleLayer; - -/** - The ripple layer delegate protocol to let MDCRippleView know of the layer's - ripple animation timeline. - */ -@protocol MDCRippleLayerDelegate - -/** - Called when the ripple layer began its touch down animation. - - @param rippleLayer The MDCRippleLayer. - */ -- (void)rippleLayerTouchDownAnimationDidBegin:(nonnull MDCRippleLayer *)rippleLayer; - -/** - Called when the ripple layer ended its touch down animation. - - @param rippleLayer The MDCRippleLayer. - */ -- (void)rippleLayerTouchDownAnimationDidEnd:(nonnull MDCRippleLayer *)rippleLayer; - -/** - Called when the ripple layer began its touch up animation. - - @param rippleLayer The MDCRippleLayer. - */ -- (void)rippleLayerTouchUpAnimationDidBegin:(nonnull MDCRippleLayer *)rippleLayer; - -/** - Called when the ripple layer ended its touch up animation. - - @param rippleLayer The MDCRippleLayer. - */ -- (void)rippleLayerTouchUpAnimationDidEnd:(nonnull MDCRippleLayer *)rippleLayer; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h deleted file mode 100644 index deaa46c4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - An immutable shadow object that consists of shadow properties (opacity, radius, offset). - - To generate a shadow instance, please use the MDCShadowBuilder APIs. - */ -__attribute__((objc_subclassing_restricted)) @interface MDCShadow : NSObject - -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** CALayer.shadowOpacity */ -@property(nonatomic, readonly) CGFloat opacity; - -/** CALayer.shadowRadius */ -@property(nonatomic, readonly) CGFloat radius; - -/** CALayer.shadowOffset */ -@property(nonatomic, readonly) CGSize offset; - -@end - -/** - Mutable builder to construct immutable `MDCShadow` objects. - */ -__attribute__((objc_subclassing_restricted)) @interface MDCShadowBuilder : NSObject - -/** CALayer.shadowOpacity */ -@property(nonatomic) CGFloat opacity; - -/** CALayer.shadowRadius */ -@property(nonatomic) CGFloat radius; - -/** CALayer.shadowOffset */ -@property(nonatomic) CGSize offset; - -/** Returns an immutable value type containing a snapshot of the values in this object. */ -- (nonnull MDCShadow *)build; - -/** Returns a builder with the provided opacity, radius, and offset properties. */ -+ (nonnull MDCShadowBuilder *)builderWithOpacity:(CGFloat)opacity - radius:(CGFloat)radius - offset:(CGSize)offset; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m deleted file mode 100644 index 94a61441..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadow.m +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShadow.h" - -@implementation MDCShadow - -- (instancetype)initWithOpacity:(CGFloat)opacity radius:(CGFloat)radius offset:(CGSize)offset { - self = [super init]; - if (self) { - _opacity = opacity; - _radius = radius; - _offset = offset; - } - return self; -} - -- (BOOL)isEqual:(id)other { - if (self == other) { - return YES; - } - if (![other isKindOfClass:[MDCShadow class]]) { - return NO; - } - MDCShadow *otherShadow = other; - return _opacity == otherShadow.opacity && _radius == otherShadow.radius && - CGSizeEqualToSize(_offset, otherShadow.offset); -} - -- (NSUInteger)hash { - const NSUInteger kPrime = 31; - NSUInteger result = 1; - result = result * kPrime + (NSUInteger)_opacity; - result = result * kPrime + (NSUInteger)_radius; - result = result * kPrime + (NSUInteger)(_offset.width); - result = result * kPrime + (NSUInteger)(_offset.height); - return result; -} - -@end - -@implementation MDCShadowBuilder - -- (MDCShadow *)build { - return [[MDCShadow alloc] initWithOpacity:self.opacity radius:self.radius offset:self.offset]; -} - -+ (MDCShadowBuilder *)builderWithOpacity:(CGFloat)opacity - radius:(CGFloat)radius - offset:(CGSize)offset { - MDCShadowBuilder *builder = [[MDCShadowBuilder alloc] init]; - builder.opacity = opacity; - builder.radius = radius; - builder.offset = offset; - return builder; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h deleted file mode 100644 index d69730cb..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCShadow.h" - -/** - An immutable shadows collection object that encapsulates a storage of shadows correlating to - elevation values. - - To apply a shadow on a view, please see and use the C methods @c MDCConfigureShadowForView and - @c MDCConfigureShadowForViewWithPath. - */ -__attribute__((objc_subclassing_restricted)) @interface MDCShadowsCollection : NSObject - -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** - Returns an MDCShadow instance representing the shadow properties for the given elevation (in - points) by fetching from the @c shadowsCollection container holding elevation to MDCShadow instance - bindings. - - Note: If the provided elevation is not stored, the shadow properties that are set are of the - nearest elevation above the provided elevation. i.e. if elevations 1 and 3 are set, and an - elevation of 2 is provided as input, the shadow properties set will be of elevation 3. If the - provided elevation is above the highest elevation value that is set, then the shadow properties set - will be of the highest elevation. - */ -- (nonnull MDCShadow *)shadowForElevation:(CGFloat)elevation; - -@end - -/** - A shadows collection object builder that generates an MDCShadowsCollection instance using the @c - build method. The object allows to add shadows for a given elevation. - - To ensure no nullability situations, please instantiate the builder using @c - builderWithShadow:forElevation and provide an initial MDCShadow instance. - */ -__attribute__((objc_subclassing_restricted)) @interface MDCShadowsCollectionBuilder : NSObject - -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** - Adds a shadow for the given elevation. - - @param shadow An MDCShadow object consisting of shadow properties such as radius, opacity, and - offset. - @param elevation The elevation provided in points (dp). - */ -- (void)addShadow:(MDCShadow *_Nonnull)shadow forElevation:(CGFloat)elevation; - -/** - Adds a dictionary of shadows with their correlating elevations to the shadows collection. - - @param shadowsForElevations A dictionary holding NSNumber elevation value keys, and their values - are MDCShadow objects correlating to their elevation key. - */ -- (void)addShadowsForElevations: - (NSDictionary *_Nonnull)shadowsForElevations; - -/** - Creates an initial MDCShadowsCollectionBuilder by providing it an initial MDCShadow object and its - correlating elevation. - - @param shadow An MDCShadow object consisting of shadow properties such as radius, opacity, and - offset. - @param elevation The elevation provided in points (dp). - */ -+ (nonnull MDCShadowsCollectionBuilder *)builderWithShadow:(MDCShadow *_Nonnull)shadow - forElevation:(CGFloat)elevation; - -/** - Builds and returns an MDCShadowsCollection instance given the provided shadows using the - @c addShadow:forElevation and @c addShadowsForElevations: APIs. - */ -- (nonnull MDCShadowsCollection *)build; - -@end - -/** - Given a view, MDCShadow instance, and a shadow color (e.g. `MDCShadowColor()`), updates the shadow - properties of `view.layer`: - - * shadowColor - * shadowOpacity - * shadowRadius - * shadowOffset - * shadowPath - - `shadowPath` will be set to the current bounds of the given view (including rounded - corners if set on view.layer). - - TODO(b/182581383): maskedCorners, cornerCurve, and cornerCurveExpansionFactor are not - yet supported. - - Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` - whenever the view's bounds change. - */ -FOUNDATION_EXTERN void MDCConfigureShadowForView(UIView *_Nonnull view, MDCShadow *_Nonnull shadow, - UIColor *_Nonnull shadowColor) - NS_SWIFT_NAME(MDCConfigureShadow(for:shadow:color:)); - -/** - Given a view, MDCShadow instance, a shadow color (e.g. `MDCShadowColor()`), and a `path` in the - view's coordinate space representing the shape of the view, updates the shadow properties of - `view.layer`: - - * shadowColor - * shadowOpacity - * shadowRadius - * shadowOffset - * shadowPath - - Call this function from your `UIView` subclass's `-layoutSubviews` to update `shadowPath` - whenever the view's bounds or shape changes. - */ -FOUNDATION_EXTERN void MDCConfigureShadowForViewWithPath(UIView *_Nonnull view, - MDCShadow *_Nonnull shadow, - UIColor *_Nonnull shadowColor, - CGPathRef _Nonnull path) - NS_SWIFT_NAME(MDCConfigureShadow(for:shadow:color:path:)); - -/** - Default color for a Material shadow. On iOS >= 13, this is a dynamic color. - */ -FOUNDATION_EXTERN UIColor *_Nonnull MDCShadowColor(void); - -/** - Returns an MDCShadowsCollection instance with predefined defaults of shadow properties (opacity, - radius, offset) for elevations. - */ -FOUNDATION_EXTERN MDCShadowsCollection *_Nonnull MDCShadowsCollectionDefault(void); diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m deleted file mode 100644 index 5b22fca3..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MDCShadowsCollection.m +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShadowsCollection.h" - -#import "MaterialAvailability.h" -#import "MDCShadow.h" - -@implementation MDCShadowsCollection { - NSDictionary *_shadowValuesForElevation; - NSArray *_orderedKeys; -} - -- (instancetype)initWithShadowValuesForElevation: - (NSDictionary *)shadowValuesForElevation { - self = [super init]; - if (self) { - _shadowValuesForElevation = [shadowValuesForElevation copy]; - _orderedKeys = [shadowValuesForElevation.allKeys sortedArrayUsingSelector:@selector(compare:)]; - } - return self; -} - -- (MDCShadow *)shadowForElevation:(CGFloat)elevation { - NSUInteger lookupIndex = [self indexInOrderedKeysOfGivenElevation:elevation]; - // If the value is larger than the largest value in the array, we will return the highest value in - // the array. - if (lookupIndex >= _orderedKeys.count) { - lookupIndex = _orderedKeys.count - 1; - } - - NSNumber *key = _orderedKeys[lookupIndex]; - return [_shadowValuesForElevation objectForKey:key]; -} - -- (NSUInteger)indexInOrderedKeysOfGivenElevation:(CGFloat)elevation { - NSNumber *num = @(elevation); - NSUInteger index = - [_orderedKeys indexOfObject:num - inSortedRange:NSMakeRange(0, _orderedKeys.count) - options:NSBinarySearchingInsertionIndex - usingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) { - return [num1 compare:num2]; - }]; - return index; -} - -@end - -@implementation MDCShadowsCollectionBuilder { - NSMutableDictionary *_shadowValuesForElevation; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _shadowValuesForElevation = [[NSMutableDictionary alloc] init]; - } - return self; -} - -+ (MDCShadowsCollectionBuilder *)builderWithShadow:(MDCShadow *)shadow - forElevation:(CGFloat)elevation { - MDCShadowsCollectionBuilder *builder = [[self alloc] init]; - [builder addShadow:shadow forElevation:elevation]; - return builder; -} - -- (void)addShadow:(MDCShadow *)shadow forElevation:(CGFloat)elevation { - [_shadowValuesForElevation setObject:shadow forKey:@(elevation)]; -} - -- (void)addShadowsForElevations:(NSDictionary *)shadowsForElevations { - [_shadowValuesForElevation addEntriesFromDictionary:shadowsForElevations]; -} - -- (MDCShadowsCollection *)build { - return [[MDCShadowsCollection alloc] initWithShadowValuesForElevation:_shadowValuesForElevation]; -} - -@end - -static UIColor *LightStyleShadowColor(void) { - static UIColor *lightStyleShadowColor; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - lightStyleShadowColor = [UIColor colorWithRed:0.235 green:0.251 blue:0.263 alpha:1]; - }); - return lightStyleShadowColor; -} - -UIColor *MDCShadowColor(void) { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - return [UIColor colorWithDynamicProvider:^(UITraitCollection *traitCollection) { - switch (traitCollection.userInterfaceStyle) { - case UIUserInterfaceStyleUnspecified: - /* FALLTHROUGH - TODO(b/185199658): Migrate to proper fallthrough logic */ - case UIUserInterfaceStyleLight: - return LightStyleShadowColor(); - case UIUserInterfaceStyleDark: - return UIColor.blackColor; - } - __builtin_unreachable(); - }]; - } -#endif // MDC_AVAILABLE_SDK_IOS(13_0) - return LightStyleShadowColor(); -} - -MDCShadowsCollection *MDCShadowsCollectionDefault(void) { - static MDCShadowsCollection *shadowsCollection; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - MDCShadow *shadow = [[MDCShadowBuilder builderWithOpacity:0 radius:0 - offset:CGSizeMake(0, 0)] build]; - MDCShadowsCollectionBuilder *shadowsBuilder = - [MDCShadowsCollectionBuilder builderWithShadow:shadow forElevation:0]; - NSDictionary *shadowValuesForElevation = @{ - @1 : [[MDCShadowBuilder builderWithOpacity:0.43 radius:2.5 offset:CGSizeMake(0, 1)] build], - @3 : [[MDCShadowBuilder builderWithOpacity:0.4 radius:3.25 offset:CGSizeMake(0, 1.25)] build], - @6 : [[MDCShadowBuilder builderWithOpacity:0.34 radius:4.75 - offset:CGSizeMake(0, 2.25)] build], - @8 : [[MDCShadowBuilder builderWithOpacity:0.42 radius:6 offset:CGSizeMake(0, 3)] build], - @12 : [[MDCShadowBuilder builderWithOpacity:0.4 radius:7.25 offset:CGSizeMake(0, 5)] build], - }; - [shadowsBuilder addShadowsForElevations:shadowValuesForElevation]; - shadowsCollection = [shadowsBuilder build]; - }); - return shadowsCollection; -} - -void MDCConfigureShadowForView(UIView *view, MDCShadow *shadow, UIColor *shadowColor) { - // The bezierPathWithRoundedRect API supports both a cornerRadius of 0 (created just a square - // path) and also rounded corners where the cornerRadius is >0. - UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:view.bounds - cornerRadius:view.layer.cornerRadius]; - - MDCConfigureShadowForViewWithPath(view, shadow, shadowColor, path.CGPath); -} - -void MDCConfigureShadowForViewWithPath(UIView *view, MDCShadow *shadow, UIColor *shadowColor, - CGPathRef path) { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(ios 13.0, *)) { - shadowColor = [shadowColor resolvedColorWithTraitCollection:view.traitCollection]; - } -#endif // MDC_AVAILABLE_SDK_IOS(13_0) - view.layer.shadowColor = shadowColor.CGColor; - view.layer.shadowOpacity = (float)shadow.opacity; - view.layer.shadowRadius = shadow.radius; - view.layer.shadowOffset = shadow.offset; - view.layer.shadowPath = path; -} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h deleted file mode 100644 index c66cffe4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shadow/src/MaterialShadow.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights -// Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShadow.h" -#import "MDCShadowsCollection.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h deleted file mode 100644 index 3f1a13d5..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MDCShadowElevations.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#ifdef NS_TYPED_EXTENSIBLE_ENUM // This macro is introduced in Xcode 9. -#define MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM NS_TYPED_EXTENSIBLE_ENUM -#elif __has_attribute(swift_wrapper) // Backwards compatibility for Xcode 8. -#define MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM __attribute__((swift_wrapper(struct))) -#else -#define MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM -#endif - -/** - Constants for elevation: the relative depth, or distance, between two surfaces along the z-axis. - https://material.io/go/design-elevation - */ -NS_SWIFT_NAME(ShadowElevation) -typedef CGFloat MDCShadowElevation MDC_SHADOW_ELEVATION_TYPED_EXTENSIBLE_ENUM; - -/** The shadow elevation of the app bar. */ -static const MDCShadowElevation MDCShadowElevationAppBar = (CGFloat)4.0; - -/** The shadow elevation of the Bottom App Bar. */ -static const MDCShadowElevation MDCShadowElevationBottomAppBar = (CGFloat)8.0; - -/** The shadow elevation of the Bottom App Bar. */ -static const MDCShadowElevation MDCShadowElevationBottomNavigationBar = (CGFloat)8.0; - -/** The shadow elevation of a card in its picked up state. */ -static const MDCShadowElevation MDCShadowElevationCardPickedUp = (CGFloat)8.0; - -/** The shadow elevation of a card in its resting state. */ -static const MDCShadowElevation MDCShadowElevationCardResting = (CGFloat)2.0; - -/** The shadow elevation of dialogs. */ -static const MDCShadowElevation MDCShadowElevationDialog = (CGFloat)24.0; - -/** The shadow elevation of the floating action button in its pressed state. */ -static const MDCShadowElevation MDCShadowElevationFABPressed = (CGFloat)12.0; - -/** The shadow elevation of the floating action button in its resting state. */ -static const MDCShadowElevation MDCShadowElevationFABResting = (CGFloat)6.0; - -/** The shadow elevation of a menu. */ -static const MDCShadowElevation MDCShadowElevationMenu = (CGFloat)8.0; - -/** The shadow elevation of a modal bottom sheet. */ -static const MDCShadowElevation MDCShadowElevationModalActionSheet = (CGFloat)8.0; - -/** The shadow elevation of a modal bottom sheet. */ -static const MDCShadowElevation MDCShadowElevationModalBottomSheet = (CGFloat)16.0; - -/** The shadow elevation of the navigation drawer. */ -static const MDCShadowElevation MDCShadowElevationNavDrawer = (CGFloat)16.0; - -/** No shadow elevation at all. */ -static const MDCShadowElevation MDCShadowElevationNone = (CGFloat)0.0; - -/** The shadow elevation of a picker. */ -static const MDCShadowElevation MDCShadowElevationPicker = (CGFloat)24.0; - -/** The shadow elevation of the quick entry in the scrolled state. */ -static const MDCShadowElevation MDCShadowElevationQuickEntry = (CGFloat)3.0; - -/** The shadow elevation of the quick entry in the resting state. */ -static const MDCShadowElevation MDCShadowElevationQuickEntryResting = (CGFloat)2.0; - -/** The shadow elevation of a raised button in the pressed state. */ -static const MDCShadowElevation MDCShadowElevationRaisedButtonPressed = (CGFloat)8.0; - -/** The shadow elevation of a raised button in the resting state. */ -static const MDCShadowElevation MDCShadowElevationRaisedButtonResting = (CGFloat)2.0; - -/** The shadow elevation of a refresh indicator. */ -static const MDCShadowElevation MDCShadowElevationRefresh = (CGFloat)3.0; - -/** The shadow elevation of the right drawer. */ -static const MDCShadowElevation MDCShadowElevationRightDrawer = (CGFloat)16.0; - -/** The shadow elevation of the search bar in the resting state. */ -static const MDCShadowElevation MDCShadowElevationSearchBarResting = (CGFloat)2.0; - -/** The shadow elevation of the search bar in the scrolled state. */ -static const MDCShadowElevation MDCShadowElevationSearchBarScrolled = (CGFloat)3.0; - -/** The shadow elevation of the snackbar. */ -static const MDCShadowElevation MDCShadowElevationSnackbar = (CGFloat)6.0; - -/** The shadow elevation of a sub menu (+1 for each additional sub menu). */ -static const MDCShadowElevation MDCShadowElevationSubMenu = (CGFloat)9.0; - -/** The shadow elevation of a switch. */ -static const MDCShadowElevation MDCShadowElevationSwitch = (CGFloat)1.0; diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h deleted file mode 100644 index becb2bce..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevations.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShadowElevations.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m deleted file mode 100644 index 75cef75c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowElevations/src/MaterialShadowElevationsDummy.m +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - This file exists to keep pod lib lint passing - */ diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h deleted file mode 100644 index 9657f1d1..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import "MaterialShadowElevations.h" - -/** - Metrics of the Material shadow effect. - - These can be used if you require your own shadow implementation but want to match the material - spec. - */ -@interface MDCShadowMetrics : NSObject -@property(nonatomic, readonly) CGFloat topShadowRadius; -@property(nonatomic, readonly) CGSize topShadowOffset; -@property(nonatomic, readonly) float topShadowOpacity; -@property(nonatomic, readonly) CGFloat bottomShadowRadius; -@property(nonatomic, readonly) CGSize bottomShadowOffset; -@property(nonatomic, readonly) float bottomShadowOpacity; - -/** - The shadow metrics for manually creating shadows given an elevation. - - @param elevation The shadow's elevation in points. - @return The shadow metrics. - */ -+ (nonnull MDCShadowMetrics *)metricsWithElevation:(CGFloat)elevation; -@end - -/** - The Material shadow effect. - - @see - https://material.io/guidelines/what-is-material/elevation-shadows.html#elevation-shadows-shadows - - Consider rasterizing your MDCShadowLayer if your view will not generally be animating or - changing size. If you need to animate a rasterized MDCShadowLayer, disable rasterization first. - - For example, if self's layerClass is MDCShadowLayer, you might introduce the following code: - - self.layer.shouldRasterize = YES; - self.layer.rasterizationScale = [UIScreen mainScreen].scale; - */ -@interface MDCShadowLayer : CALayer - -/** - The elevation of the layer in points. - - The higher the elevation, the more spread out the shadow is. This is distinct from the layer's - zPosition which can be used to order overlapping layers, but will have no affect on the size of - the shadow. - - Negative values act as if zero were specified. - - The default value is 0. - */ -@property(nonatomic, assign) MDCShadowElevation elevation; - -/** - Whether to apply the "cutout" shadow layer mask. - - If enabled, then a mask is created to ensure the interior, non-shadow part of the layer is visible. - - Default is YES. Not animatable. - */ -@property(nonatomic, getter=isShadowMaskEnabled, assign) BOOL shadowMaskEnabled; - -/** - Animates the layer's corner radius - - @note At the end of the animation the corner radius is set to your desired corner radius. - - @param cornerRadius The desired corner radius at the end of the animation - @param timingFunction The timing function you desire for the animation - @param duration The duration of the animation - */ -- (void)animateCornerRadius:(CGFloat)cornerRadius - withTimingFunction:(nonnull CAMediaTimingFunction *)timingFunction - duration:(NSTimeInterval)duration; - -@end - -/** - Subclasses can depend on MDCShadowLayer implementing CALayerDelegate actionForLayer:forKey: in - order to implicitly animate 'path' or 'shadowPath' on sublayers. - */ -@interface MDCShadowLayer (Subclassing) - -/** - Override point. - Called by the shadow layer before the instance lays out its sublayers. - */ -- (void)prepareShadowPath; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m deleted file mode 100644 index 19de7d65..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MDCShadowLayer.m +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShadowLayer.h" - -static const CGFloat kShadowElevationDialog = 24.0; -static const float kKeyShadowOpacity = (float)0.26; -static const float kAmbientShadowOpacity = (float)0.08; - -@interface MDCPendingAnimation : NSObject -@property(nonatomic, weak) CALayer *animationSourceLayer; -@property(nonatomic, strong) NSString *keyPath; -@property(nonatomic, strong) id fromValue; -@property(nonatomic, strong) id toValue; -@end - -@implementation MDCShadowMetrics - -+ (MDCShadowMetrics *)metricsWithElevation:(CGFloat)elevation { - if (0.0 < elevation) { - return [[MDCShadowMetrics alloc] initWithElevation:elevation]; - } else { - return [MDCShadowMetrics emptyShadowMetrics]; - } -} - -- (MDCShadowMetrics *)initWithElevation:(CGFloat)elevation { - self = [super init]; - if (self) { - _topShadowRadius = [MDCShadowMetrics ambientShadowBlur:elevation]; - _topShadowOffset = CGSizeMake(0.0, 0.0); - _topShadowOpacity = kAmbientShadowOpacity; - _bottomShadowRadius = [MDCShadowMetrics keyShadowBlur:elevation]; - _bottomShadowOffset = CGSizeMake(0.0, [MDCShadowMetrics keyShadowYOff:elevation]); - _bottomShadowOpacity = kKeyShadowOpacity; - } - return self; -} - -+ (MDCShadowMetrics *)emptyShadowMetrics { - static MDCShadowMetrics *emptyShadowMetrics; - static dispatch_once_t once; - dispatch_once(&once, ^{ - emptyShadowMetrics = [[MDCShadowMetrics alloc] init]; - emptyShadowMetrics->_topShadowRadius = (CGFloat)0.0; - emptyShadowMetrics->_topShadowOffset = CGSizeMake(0.0, 0.0); - emptyShadowMetrics->_topShadowOpacity = 0; - emptyShadowMetrics->_bottomShadowRadius = (CGFloat)0.0; - emptyShadowMetrics->_bottomShadowOffset = CGSizeMake(0.0, 0.0); - emptyShadowMetrics->_bottomShadowOpacity = 0; - }); - - return emptyShadowMetrics; -} - -+ (CGFloat)ambientShadowBlur:(CGFloat)points { - CGFloat blur = (CGFloat)0.889544 * points - (CGFloat)0.003701; - return blur; -} - -+ (CGFloat)keyShadowBlur:(CGFloat)points { - CGFloat blur = (CGFloat)0.666920 * points - (CGFloat)0.001648; - return blur; -} - -+ (CGFloat)keyShadowYOff:(CGFloat)points { - CGFloat yOff = (CGFloat)1.23118 * points - (CGFloat)0.03933; - return yOff; -} - -@end - -@interface MDCShadowLayer () - -@property(nonatomic, strong) CAShapeLayer *topShadow; -@property(nonatomic, strong) CAShapeLayer *bottomShadow; -@property(nonatomic, strong) CAShapeLayer *topShadowMask; -@property(nonatomic, strong) CAShapeLayer *bottomShadowMask; - -@end - -@implementation MDCShadowLayer { - BOOL _shadowPathIsInvalid; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _elevation = 0; - _shadowMaskEnabled = YES; - _shadowPathIsInvalid = YES; - - [self commonMDCShadowLayerInit]; - } - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonMDCShadowLayerInit]; - } - return self; -} - -- (instancetype)initWithLayer:(id)layer { - if (self = [super initWithLayer:layer]) { - if ([layer isKindOfClass:[MDCShadowLayer class]]) { - MDCShadowLayer *otherLayer = (MDCShadowLayer *)layer; - _elevation = otherLayer.elevation; - _shadowMaskEnabled = otherLayer.isShadowMaskEnabled; - _bottomShadow = [[CAShapeLayer alloc] initWithLayer:otherLayer.bottomShadow]; - _topShadow = [[CAShapeLayer alloc] initWithLayer:otherLayer.topShadow]; - _topShadowMask = [[CAShapeLayer alloc] initWithLayer:otherLayer.topShadowMask]; - _bottomShadowMask = [[CAShapeLayer alloc] initWithLayer:otherLayer.bottomShadowMask]; - [self commonMDCShadowLayerInit]; - } - } - return self; -} - -/** - commonMDCShadowLayerInit creates additional layers based on the values of _elevation and - _shadowMaskEnabled. - */ -- (void)commonMDCShadowLayerInit { - if (!_bottomShadow) { - _bottomShadow = [CAShapeLayer layer]; - _bottomShadow.backgroundColor = [UIColor clearColor].CGColor; - _bottomShadow.shadowColor = [UIColor blackColor].CGColor; - _bottomShadow.delegate = self; - [self addSublayer:_bottomShadow]; - } - - if (!_topShadow) { - _topShadow = [CAShapeLayer layer]; - _topShadow.backgroundColor = [UIColor clearColor].CGColor; - _topShadow.shadowColor = [UIColor blackColor].CGColor; - _topShadow.delegate = self; - [self addSublayer:_topShadow]; - } - - // Setup shadow layer state based off _elevation and _shadowMaskEnabled - MDCShadowMetrics *shadowMetrics = [MDCShadowMetrics metricsWithElevation:_elevation]; - _topShadow.shadowOffset = shadowMetrics.topShadowOffset; - _topShadow.shadowRadius = shadowMetrics.topShadowRadius; - _topShadow.shadowOpacity = shadowMetrics.topShadowOpacity; - _bottomShadow.shadowOffset = shadowMetrics.bottomShadowOffset; - _bottomShadow.shadowRadius = shadowMetrics.bottomShadowRadius; - _bottomShadow.shadowOpacity = shadowMetrics.bottomShadowOpacity; - - if (!_topShadowMask) { - _topShadowMask = [CAShapeLayer layer]; - _topShadowMask.delegate = self; - } - if (!_bottomShadowMask) { - _bottomShadowMask = [CAShapeLayer layer]; - _bottomShadowMask.delegate = self; - } - - // TODO(#1021): We shouldn't be calling property accessors in an init method. - if (_shadowMaskEnabled) { - [self configureShadowLayerMaskForLayer:_topShadowMask]; - [self configureShadowLayerMaskForLayer:_bottomShadowMask]; - _topShadow.mask = _topShadowMask; - _bottomShadow.mask = _bottomShadowMask; - } -} - -- (void)layoutSublayers { - [super layoutSublayers]; - - [self prepareShadowPath]; - [self commonLayoutSublayers]; -} - -- (void)setBounds:(CGRect)bounds { - BOOL sizeChanged = !CGSizeEqualToSize(self.bounds.size, bounds.size); - [super setBounds:bounds]; - if (sizeChanged) { - _shadowPathIsInvalid = YES; - [self setNeedsLayout]; - } -} - -- (void)prepareShadowPath { - // This method is meant to be overriden by its subclasses. -} - -#pragma mark - CALayer change monitoring. - -/** Returns a shadowPath based on the layer properties. */ -- (UIBezierPath *)defaultShadowPath { - CGFloat cornerRadius = self.cornerRadius; - if (0.0 < cornerRadius) { - return [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; - } - return [UIBezierPath bezierPathWithRect:self.bounds]; -} - -- (void)setCornerRadius:(CGFloat)cornerRadius { - super.cornerRadius = cornerRadius; - - _topShadow.cornerRadius = cornerRadius; - _bottomShadow.cornerRadius = cornerRadius; - if (_shadowMaskEnabled) { - [self configureShadowLayerMaskForLayer:_topShadowMask]; - [self configureShadowLayerMaskForLayer:_bottomShadowMask]; - _topShadow.mask = _topShadowMask; - _bottomShadow.mask = _bottomShadowMask; - } -} - -- (void)setShadowPath:(CGPathRef)shadowPath { - super.shadowPath = shadowPath; - _topShadow.shadowPath = shadowPath; - _bottomShadow.shadowPath = shadowPath; - if (_shadowMaskEnabled) { - [self configureShadowLayerMaskForLayer:_topShadowMask]; - [self configureShadowLayerMaskForLayer:_bottomShadowMask]; - } -} - -- (void)setShadowColor:(CGColorRef)shadowColor { - super.shadowColor = shadowColor; - _topShadow.shadowColor = shadowColor; - _bottomShadow.shadowColor = shadowColor; -} - -#pragma mark - shouldRasterize forwarding - -- (void)setShouldRasterize:(BOOL)shouldRasterize { - [super setShouldRasterize:shouldRasterize]; - _topShadow.shouldRasterize = shouldRasterize; - _bottomShadow.shouldRasterize = shouldRasterize; -} - -#pragma mark - Shadow Spread - -// Returns how far aware the shadow is spread from the edge of the layer. -+ (CGSize)shadowSpreadForElevation:(CGFloat)elevation { - MDCShadowMetrics *metrics = [MDCShadowMetrics metricsWithElevation:elevation]; - - CGSize shadowSpread = CGSizeZero; - shadowSpread.width = MAX(metrics.topShadowRadius, metrics.bottomShadowRadius) + - MAX(metrics.topShadowOffset.width, metrics.bottomShadowOffset.width); - shadowSpread.height = MAX(metrics.topShadowRadius, metrics.bottomShadowRadius) + - MAX(metrics.topShadowOffset.height, metrics.bottomShadowOffset.height); - - return shadowSpread; -} - -#pragma mark - Pseudo Shadow Masks - -- (void)setShadowMaskEnabled:(BOOL)shadowMaskEnabled { - _shadowMaskEnabled = shadowMaskEnabled; - if (_shadowMaskEnabled) { - [self configureShadowLayerMaskForLayer:_topShadowMask]; - [self configureShadowLayerMaskForLayer:_bottomShadowMask]; - _topShadow.mask = _topShadowMask; - _bottomShadow.mask = _bottomShadowMask; - } else { - _topShadow.mask = nil; - _bottomShadow.mask = nil; - } -} - -// Creates a layer mask that has a hole cut inside so that the original contents -// of the view is no obscured by the shadow the top/bottom pseudo shadow layers -// cast. -- (void)configureShadowLayerMaskForLayer:(CAShapeLayer *)maskLayer { - UIBezierPath *path = [self outerMaskPath]; - UIBezierPath *innerPath = nil; - if (self.shadowPath != nil) { - innerPath = [UIBezierPath bezierPathWithCGPath:(_Nonnull CGPathRef)self.shadowPath]; - } else if (self.cornerRadius > 0) { - innerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.cornerRadius]; - } else { - innerPath = [UIBezierPath bezierPathWithRect:self.bounds]; - } - [path appendPath:innerPath]; - [path setUsesEvenOddFillRule:YES]; - - maskLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); - maskLayer.bounds = [self maskRect]; - maskLayer.path = path.CGPath; - maskLayer.fillRule = kCAFillRuleEvenOdd; - maskLayer.fillColor = [UIColor blackColor].CGColor; -} - -- (CGRect)maskRect { - CGSize shadowSpread = [MDCShadowLayer shadowSpreadForElevation:kShadowElevationDialog]; - CGRect bounds = self.bounds; - return CGRectInset(bounds, -shadowSpread.width * 2, -shadowSpread.height * 2); -} - -- (UIBezierPath *)outerMaskPath { - return [UIBezierPath bezierPathWithRect:[self maskRect]]; -} - -- (void)setElevation:(CGFloat)elevation { - _elevation = elevation; - - MDCShadowMetrics *shadowMetrics = [MDCShadowMetrics metricsWithElevation:elevation]; - - _topShadow.shadowOffset = shadowMetrics.topShadowOffset; - _topShadow.shadowRadius = shadowMetrics.topShadowRadius; - _topShadow.shadowOpacity = shadowMetrics.topShadowOpacity; - _bottomShadow.shadowOffset = shadowMetrics.bottomShadowOffset; - _bottomShadow.shadowRadius = shadowMetrics.bottomShadowRadius; - _bottomShadow.shadowOpacity = shadowMetrics.bottomShadowOpacity; -} - -#pragma mark - CALayerDelegate - -- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { - if ([event isEqualToString:@"path"] || [event isEqualToString:@"shadowPath"]) { - // We have to create a pending animation because if we are inside a UIKit animation block we - // won't know any properties of the animation block until it is commited. - MDCPendingAnimation *pendingAnim = [[MDCPendingAnimation alloc] init]; - pendingAnim.animationSourceLayer = self; - pendingAnim.fromValue = [layer.presentationLayer valueForKey:event]; - pendingAnim.toValue = nil; - pendingAnim.keyPath = event; - - return pendingAnim; - } - return nil; -} - -#pragma mark - Private - -- (void)commonLayoutSublayers { - CGRect bounds = self.bounds; - - _bottomShadow.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - _bottomShadow.bounds = bounds; - _topShadow.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - _topShadow.bounds = bounds; - - if (_shadowMaskEnabled) { - [self configureShadowLayerMaskForLayer:_topShadowMask]; - [self configureShadowLayerMaskForLayer:_bottomShadowMask]; - } - // Enforce shadowPaths because otherwise no shadows can be drawn. If a shadowPath - // is already set, use that, otherwise fallback to just a regular rect because path. - if (!_bottomShadow.shadowPath || _shadowPathIsInvalid) { - if (self.shadowPath) { - _bottomShadow.shadowPath = self.shadowPath; - } else { - _bottomShadow.shadowPath = [self defaultShadowPath].CGPath; - } - } - if (!_topShadow.shadowPath || _shadowPathIsInvalid) { - if (self.shadowPath) { - _topShadow.shadowPath = self.shadowPath; - } else { - _topShadow.shadowPath = [self defaultShadowPath].CGPath; - } - } - _shadowPathIsInvalid = NO; -} - -- (void)animateCornerRadius:(CGFloat)cornerRadius - withTimingFunction:(CAMediaTimingFunction *)timingFunction - duration:(NSTimeInterval)duration { - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - CGFloat currentCornerRadius = (self.cornerRadius <= 0) ? (CGFloat)0.001 : self.cornerRadius; - CGFloat newCornerRadius = (cornerRadius <= 0) ? (CGFloat)0.001 : cornerRadius; - // Create the paths - UIBezierPath *currentLayerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds - cornerRadius:currentCornerRadius]; - UIBezierPath *newLayerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds - cornerRadius:newCornerRadius]; - - UIBezierPath *currentMaskPath = [self outerMaskPath]; - [currentMaskPath appendPath:currentLayerPath]; - currentMaskPath.usesEvenOddFillRule = YES; - - UIBezierPath *newMaskPath = [self outerMaskPath]; - [newMaskPath appendPath:newLayerPath]; - newMaskPath.usesEvenOddFillRule = YES; - - // Animate the top layers - NSString *shadowPathKey = @"shadowPath"; - CABasicAnimation *topLayerAnimation = [CABasicAnimation animationWithKeyPath:shadowPathKey]; - topLayerAnimation.fromValue = (__bridge id)currentLayerPath.CGPath; - topLayerAnimation.toValue = (__bridge id)newLayerPath.CGPath; - topLayerAnimation.duration = duration; - topLayerAnimation.timingFunction = timingFunction; - self.topShadow.shadowPath = newLayerPath.CGPath; - [self.topShadow addAnimation:topLayerAnimation forKey:shadowPathKey]; - CABasicAnimation *bottomLayerAnimation = [CABasicAnimation animationWithKeyPath:shadowPathKey]; - bottomLayerAnimation.fromValue = (__bridge id)currentLayerPath.CGPath; - bottomLayerAnimation.toValue = (__bridge id)newLayerPath.CGPath; - bottomLayerAnimation.duration = duration; - bottomLayerAnimation.timingFunction = timingFunction; - self.bottomShadow.shadowPath = newLayerPath.CGPath; - [self.bottomShadow addAnimation:bottomLayerAnimation forKey:shadowPathKey]; - - // Animate the masks - if (self.shadowMaskEnabled) { - NSString *pathKey = @"path"; - CABasicAnimation *topMaskLayerAnimation = [CABasicAnimation animationWithKeyPath:pathKey]; - topMaskLayerAnimation.fromValue = (__bridge id)currentMaskPath.CGPath; - topMaskLayerAnimation.toValue = (__bridge id)newMaskPath.CGPath; - topMaskLayerAnimation.duration = duration; - topMaskLayerAnimation.timingFunction = timingFunction; - self.topShadowMask.path = newMaskPath.CGPath; - [self.topShadowMask addAnimation:topMaskLayerAnimation forKey:pathKey]; - CABasicAnimation *bottomMaskLayerAnimation = [CABasicAnimation animationWithKeyPath:pathKey]; - bottomMaskLayerAnimation.fromValue = (__bridge id)currentMaskPath.CGPath; - bottomMaskLayerAnimation.toValue = (__bridge id)newMaskPath.CGPath; - bottomMaskLayerAnimation.duration = duration; - bottomMaskLayerAnimation.timingFunction = timingFunction; - self.bottomShadowMask.path = newMaskPath.CGPath; - [self.bottomShadowMask addAnimation:bottomMaskLayerAnimation forKey:pathKey]; - } - - // Animate the corner radius - CABasicAnimation *cornerRadiusAnimation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"]; - cornerRadiusAnimation.fromValue = @((CGFloat)currentCornerRadius); - cornerRadiusAnimation.toValue = @((CGFloat)newCornerRadius); - cornerRadiusAnimation.duration = duration; - cornerRadiusAnimation.timingFunction = timingFunction; - self.cornerRadius = cornerRadius; - [self addAnimation:cornerRadiusAnimation forKey:@"cornerRadius"]; - [CATransaction commit]; -} - -@end - -@implementation MDCPendingAnimation - -- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict { - if ([anObject isKindOfClass:[CAShapeLayer class]]) { - CAShapeLayer *layer = (CAShapeLayer *)anObject; - - // In order to synchronize our animation with UIKit animations we have to fetch the resizing - // animation created by UIKit and copy the configuration to our custom animation. - CAAnimation *boundsAction = [self.animationSourceLayer animationForKey:@"bounds.size"]; - if (!boundsAction) { - // Headless layers will animate bounds directly instead of decomposing - // bounds.size/bounds.position. A headless layer is a CALayer without a delegate (usually - // would be a UIView). - boundsAction = [self.animationSourceLayer animationForKey:@"bounds"]; - } - if ([boundsAction isKindOfClass:[CABasicAnimation class]]) { - CABasicAnimation *animation = (CABasicAnimation *)[boundsAction copy]; - animation.keyPath = self.keyPath; - animation.fromValue = self.fromValue; - animation.toValue = self.toValue; - - [layer addAnimation:animation forKey:event]; - } - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h deleted file mode 100644 index a03c2166..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShadowLayer/src/MaterialShadowLayer.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShadowLayer.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h deleted file mode 100644 index 9a2a094c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MaterialShapes.h" - -#import "MDCCurvedCornerTreatment.h" -#import "MDCCutCornerTreatment.h" -#import "MDCRoundedCornerTreatment.h" - -@interface MDCCornerTreatment (CornerTypeInitalizer) - -/** - Initialize and return an MDCCornerTreatment as an MDCRoundedCornerTreatment. - - @param value The radius to set the rounded corner to. - @return an MDCRoundedCornerTreatment. - */ -+ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value; - -/** - Initialize and return an MDCCornerTreatment as an MDCRoundedCornerTreatment. - - @param value The radius to set the rounded corner to. - @param valueType The value type in which the value is set as. It can be sent either as an - absolute value, or a percentage value (0.0 - 1.0) of the height of the surface. - @return an MDCRoundedCornerTreatment. - */ -+ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value - valueType:(MDCCornerTreatmentValueType)valueType; - -/** - Initialize and return an MDCCornerTreatment as an MDCCutCornerTreatment. - - @param value The cut to set the cut corner to. - @return an MDCCutCornerTreatment. - */ -+ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value; - -/** - Initialize and return an MDCCornerTreatment as an MDCRoundedCornerTreatment. - - @param value The cut to set the cut corner to. - @param valueType The value type in which the value is set as. It can be sent either as an - absolute value, or a percentage value (0.0 - 1.0) of the height of the surface. - @return an MDCCutCornerTreatment. - */ -+ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value - valueType:(MDCCornerTreatmentValueType)valueType; - -/** - Initialize and return an MDCCornerTreatment as an MDCCurvedCornerTreatment. - - @param value The size to set the curved corner to. - @return an MDCCurvedCornerTreatment. - */ -+ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value; - -/** - Initialize and return an MDCCornerTreatment as an MDCCurvedCornerTreatment. - - @param value The curve to set the curved corner to. - @param valueType The value type in which the value is set as. It can be sent either as an - absolute value, or a percentage value (0.0 - 1.0) of the height of the surface. - @return an MDCCurvedCornerTreatment. - */ -+ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value - valueType:(MDCCornerTreatmentValueType)valueType; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m deleted file mode 100644 index a9664576..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCornerTreatment+CornerTypeInitalizer.h" - -#import "MDCCurvedCornerTreatment.h" - -#import "MDCCutCornerTreatment.h" - -#import "MDCRoundedCornerTreatment.h" - -@implementation MDCCornerTreatment (CornerTypeInitalizer) - -+ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value { - return [[MDCRoundedCornerTreatment alloc] initWithRadius:value]; -} - -+ (MDCRoundedCornerTreatment *)cornerWithRadius:(CGFloat)value - valueType:(MDCCornerTreatmentValueType)valueType { - MDCRoundedCornerTreatment *roundedCornerTreatment = - [MDCRoundedCornerTreatment cornerWithRadius:value]; - roundedCornerTreatment.valueType = valueType; - return roundedCornerTreatment; -} - -+ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value { - return [[MDCCutCornerTreatment alloc] initWithCut:value]; -} - -+ (MDCCutCornerTreatment *)cornerWithCut:(CGFloat)value - valueType:(MDCCornerTreatmentValueType)valueType { - MDCCutCornerTreatment *cutCornerTreatment = [MDCCutCornerTreatment cornerWithCut:value]; - cutCornerTreatment.valueType = valueType; - return cutCornerTreatment; -} - -+ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value { - return [[MDCCurvedCornerTreatment alloc] initWithSize:value]; -} - -+ (MDCCurvedCornerTreatment *)cornerWithCurve:(CGSize)value - valueType:(MDCCornerTreatmentValueType)valueType { - MDCCurvedCornerTreatment *curvedCornerTreatment = - [MDCCurvedCornerTreatment cornerWithCurve:value]; - curvedCornerTreatment.valueType = valueType; - return curvedCornerTreatment; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h deleted file mode 100644 index c6b02073..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialShapes.h" - -/** - A curved corner treatment. Distinct from MDCRoundedCornerTreatment in that MDCurvedCornerTreatment - also supports asymmetric curved corners. - */ -@interface MDCCurvedCornerTreatment : MDCCornerTreatment - -/** - The size of the curve. - */ -@property(nonatomic, assign) CGSize size; - -/** - Initializes an MDCCurvedCornerTreatment instance with a given corner size. - */ -- (nonnull instancetype)initWithSize:(CGSize)size NS_DESIGNATED_INITIALIZER; - -/** - Initializes an MDCCurvedCornerTreatment instance with a corner size of zero. - */ -- (nonnull instancetype)init; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m deleted file mode 100644 index 363d248c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedCornerTreatment.m +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCurvedCornerTreatment.h" - -#import "MaterialShapes.h" - -@implementation MDCCurvedCornerTreatment - -- (instancetype)init { - return [self initWithSize:CGSizeZero]; -} - -- (instancetype)initWithSize:(CGSize)size { - if (self = [super init]) { - _size = size; - } - return self; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle { - return [self pathGeneratorForCornerWithAngle:angle andCurve:_size]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle forViewSize:(CGSize)viewSize { - CGSize normalizedCurve = - CGSizeMake(_size.width * viewSize.height, _size.height * viewSize.height); - return [self pathGeneratorForCornerWithAngle:angle andCurve:normalizedCurve]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle andCurve:(CGSize)curve { - MDCPathGenerator *path = - [MDCPathGenerator pathGeneratorWithStartPoint:CGPointMake(0, curve.height)]; - [path addQuadCurveWithControlPoint:CGPointZero toPoint:CGPointMake(curve.width, 0)]; - return path; -} - -- (id)copyWithZone:(NSZone *)zone { - MDCCurvedCornerTreatment *copy = [super copyWithZone:zone]; - copy.size = _size; - return copy; -} - -- (BOOL)isEqual:(id)object { - if (object == self) { - return YES; - } else if (![super isEqual:object]) { - return NO; - } - if (!object || ![[object class] isEqual:[self class]]) { - return NO; - } - MDCCurvedCornerTreatment *otherCurvedCorner = (MDCCurvedCornerTreatment *)object; - return CGSizeEqualToSize(self.size, otherCurvedCorner.size); -} - -- (NSUInteger)hash { - return @(self.size.height).hash ^ @(self.size.width).hash ^ (NSUInteger)self.valueType; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h deleted file mode 100644 index 8fce5cf3..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#import "MaterialShapes.h" - -/** - A curved rectangle shape generator. - */ -@interface MDCCurvedRectShapeGenerator : NSObject - -/** - The size of the curved corner. - */ -@property(nonatomic, assign) CGSize cornerSize; - -/** - Initializes an MDCCurvedRectShapeGenerator instance with a given cornerSize. - */ -- (instancetype)initWithCornerSize:(CGSize)cornerSize NS_DESIGNATED_INITIALIZER; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m deleted file mode 100644 index ea2df710..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCurvedRectShapeGenerator.h" - -#import "MDCCurvedCornerTreatment.h" -#import "MaterialShapes.h" - -@implementation MDCCurvedRectShapeGenerator { - MDCRectangleShapeGenerator *_rectGenerator; - MDCCurvedCornerTreatment *_widthHeightCorner; - MDCCurvedCornerTreatment *_heightWidthCorner; -} - -- (instancetype)init { - return [self initWithCornerSize:CGSizeMake(0, 0)]; -} - -- (instancetype)initWithCornerSize:(CGSize)cornerSize { - if (self = [super init]) { - [self commonInit]; - - self.cornerSize = cornerSize; - } - return self; -} - -- (void)commonInit { - _rectGenerator = [[MDCRectangleShapeGenerator alloc] init]; - - _widthHeightCorner = [[MDCCurvedCornerTreatment alloc] init]; - _heightWidthCorner = [[MDCCurvedCornerTreatment alloc] init]; - - _rectGenerator.topLeftCorner = _widthHeightCorner; - _rectGenerator.topRightCorner = _heightWidthCorner; - _rectGenerator.bottomRightCorner = _widthHeightCorner; - _rectGenerator.bottomLeftCorner = _heightWidthCorner; -} - -- (void)setCornerSize:(CGSize)cornerSize { - _cornerSize = cornerSize; - - _widthHeightCorner.size = _cornerSize; - _heightWidthCorner.size = CGSizeMake(cornerSize.height, cornerSize.width); -} - -- (id)copyWithZone:(nullable NSZone *)__unused zone { - MDCCurvedRectShapeGenerator *copy = [[[self class] alloc] init]; - copy.cornerSize = self.cornerSize; - return copy; -} - -- (CGPathRef)pathForSize:(CGSize)size { - return [_rectGenerator pathForSize:size]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h deleted file mode 100644 index dcacfcb6..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialShapes.h" - -/** - A cut corner treatment subclassing MDCCornerTreatment. - This can be used to set corners in MDCRectangleShapeGenerator. - */ -@interface MDCCutCornerTreatment : MDCCornerTreatment - -/** - The cut of the corner. - - The value of the cut defines by how many UI points starting from the edge of the corner and going - equal distance on the X axis and the Y axis will the corner be cut. - - As an example if the shape is a square with a size of 100x100, and we have all its corners set - with MDCCutCornerTreatment and a cut value of 50 then the final result will be a diamond with a - size of 50x50. - +--------------+ /\ - | | / \ 50 - | | / \ - | | 100 ---> / \ - | | \ / - | | \ / - | | \ / 50 - +--------------+ \/ - 100 - - */ -@property(nonatomic, assign) CGFloat cut; - -/** - Initializes an MDCCutCornerTreatment instance with a given cut. - */ -- (nonnull instancetype)initWithCut:(CGFloat)cut NS_DESIGNATED_INITIALIZER; - -/** - Initializes an MDCCutCornerTreatment instance with a cut of zero. - */ -- (nonnull instancetype)init; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m deleted file mode 100644 index fbd5d16f..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCCutCornerTreatment.m +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCutCornerTreatment.h" - -#import "MaterialShapes.h" - -static NSString *const MDCCutCornerTreatmentCutKey = @"MDCCutCornerTreatmentCutKey"; - -@implementation MDCCutCornerTreatment - -- (instancetype)init { - return [self initWithCut:0]; -} - -- (instancetype)initWithCut:(CGFloat)cut { - if (self = [super init]) { - _cut = cut; - } - return self; -} - -- (id)copyWithZone:(NSZone *)zone { - MDCCutCornerTreatment *copy = [super copyWithZone:zone]; - copy.cut = _cut; - return copy; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle { - return [self pathGeneratorForCornerWithAngle:angle andCut:_cut]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle forViewSize:(CGSize)viewSize { - CGFloat normalizedCut = _cut * viewSize.height; - return [self pathGeneratorForCornerWithAngle:angle andCut:normalizedCut]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle andCut:(CGFloat)cut { - MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointMake(0, cut)]; - [path addLineToPoint:CGPointMake(cut, 0)]; - return path; -} - -- (BOOL)isEqual:(id)object { - if (object == self) { - return YES; - } else if (![super isEqual:object]) { - return NO; - } - if (!object || ![[object class] isEqual:[self class]]) { - return NO; - } - MDCCutCornerTreatment *otherCutCorner = (MDCCutCornerTreatment *)object; - return self.cut == otherCutCorner.cut; -} - -- (NSUInteger)hash { - return @(self.cut).hash ^ (NSUInteger)self.valueType; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h deleted file mode 100644 index 3aa0fefd..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialShapes.h" - -/** - A pill shape generator. Rounds the corners such that the shorter sides of the generated shape are - entirely rounded. - */ -@interface MDCPillShapeGenerator : NSObject -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m deleted file mode 100644 index dbcaceaf..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCPillShapeGenerator.m +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCPillShapeGenerator.h" - -#import "MDCRoundedCornerTreatment.h" -#import "MaterialShapes.h" - -@implementation MDCPillShapeGenerator { - MDCRectangleShapeGenerator *_rectangleGenerator; - MDCRoundedCornerTreatment *_cornerShape; -} - -- (instancetype)init { - if (self = [super init]) { - [self commonInit]; - } - return self; -} - -- (id)copyWithZone:(NSZone *)__unused zone { - return [[[self class] alloc] init]; -} - -- (void)commonInit { - _cornerShape = [[MDCRoundedCornerTreatment alloc] init]; - _rectangleGenerator = [[MDCRectangleShapeGenerator alloc] init]; - [_rectangleGenerator setCorners:_cornerShape]; -} - -- (CGPathRef)pathForSize:(CGSize)size { - CGFloat radius = (CGFloat)0.5 * MIN(fabs(size.width), fabs(size.height)); - if (radius > 0) { - [_rectangleGenerator setCorners:[[MDCRoundedCornerTreatment alloc] initWithRadius:radius]]; - } - return [_rectangleGenerator pathForSize:size]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h deleted file mode 100644 index 55cf6c7b..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#import - -#import "MaterialShapes.h" - -/** - A rounded corner treatment. - */ -@interface MDCRoundedCornerTreatment : MDCCornerTreatment - -/** - The radius of the corner. - */ -@property(nonatomic, assign) CGFloat radius; - -/** - Initializes an MDCRoundedCornerTreatment instance with a given radius. - */ -- (nonnull instancetype)initWithRadius:(CGFloat)radius NS_DESIGNATED_INITIALIZER; - -/** - Initializes an MDCRoundedCornerTreatment instance with a radius of zero. - */ -- (nonnull instancetype)init; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m deleted file mode 100644 index e6390806..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCRoundedCornerTreatment.m +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRoundedCornerTreatment.h" - -#import "MaterialShapes.h" - -@implementation MDCRoundedCornerTreatment - -- (instancetype)init { - return [self initWithRadius:0]; -} - -- (instancetype)initWithRadius:(CGFloat)radius { - if (self = [super init]) { - _radius = radius; - } - return self; -} - -- (id)copyWithZone:(NSZone *)zone { - MDCRoundedCornerTreatment *copy = [super copyWithZone:zone]; - copy.radius = _radius; - return copy; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle { - return [self pathGeneratorForCornerWithAngle:angle andRadius:_radius]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle forViewSize:(CGSize)viewSize { - CGFloat normalizedRadius = _radius * viewSize.height; - return [self pathGeneratorForCornerWithAngle:angle andRadius:normalizedRadius]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle andRadius:(CGFloat)radius { - MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointMake(0, radius)]; - [path addArcWithTangentPoint:CGPointZero - toPoint:CGPointMake(sin(angle) * radius, cos(angle) * radius) - radius:radius]; - return path; -} - -- (BOOL)isEqual:(id)object { - if (object == self) { - return YES; - } else if (![super isEqual:object]) { - return NO; - } - if (!object || ![[object class] isEqual:[self class]]) { - return NO; - } - MDCRoundedCornerTreatment *otherRoundedCorner = (MDCRoundedCornerTreatment *)object; - return self.radius == otherRoundedCorner.radius; -} - -- (NSUInteger)hash { - return @(self.radius).hash ^ (NSUInteger)self.valueType; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h deleted file mode 100644 index 7230e3d8..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#import -#import - -#import "MaterialShapes.h" - -/** - A slanted rectangle shape generator. - - Creates rectangles with the vertical edges at a slant. - */ -@interface MDCSlantedRectShapeGenerator : NSObject - -/** - The horizontal offset of the corners. - */ -@property(nonatomic, assign) CGFloat slant; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m deleted file mode 100644 index 2dbca16b..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCSlantedRectShapeGenerator.h" - -#import "MaterialShapes.h" - -@implementation MDCSlantedRectShapeGenerator { - MDCRectangleShapeGenerator *_rectangleGenerator; -} - -- (instancetype)init { - if (self = [super init]) { - [self commonMDCSlantedRectShapeGeneratorInit]; - } - return self; -} - -- (void)commonMDCSlantedRectShapeGeneratorInit { - _rectangleGenerator = [[MDCRectangleShapeGenerator alloc] init]; -} - -- (id)copyWithZone:(NSZone *)__unused zone { - MDCSlantedRectShapeGenerator *copy = [[[self class] alloc] init]; - copy.slant = self.slant; - return copy; -} - -- (void)setSlant:(CGFloat)slant { - _slant = slant; - - _rectangleGenerator.topLeftCornerOffset = CGPointMake(slant, 0); - _rectangleGenerator.topRightCornerOffset = CGPointMake(slant, 0); - _rectangleGenerator.bottomLeftCornerOffset = CGPointMake(-slant, 0); - _rectangleGenerator.bottomRightCornerOffset = CGPointMake(-slant, 0); -} - -- (CGPathRef)pathForSize:(CGSize)size { - return [_rectangleGenerator pathForSize:size]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h deleted file mode 100644 index 1e4a0d1e..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialShapes.h" - -typedef enum : NSUInteger { - MDCTriangleEdgeStyleHandle, - MDCTriangleEdgeStyleCut, -} MDCTriangleEdgeStyle; - -/** - An edge treatment that adds a triangle-shaped cut or handle to the edge. - */ -@interface MDCTriangleEdgeTreatment : MDCEdgeTreatment - -/** - The size of the triangle shape. - */ -@property(nonatomic, assign) CGFloat size; - -/** - The style of the triangle shape. - */ -@property(nonatomic, assign) MDCTriangleEdgeStyle style; - -/** - Initializes an MDCTriangleEdgeTreatment with a given size and style. - */ -- (nonnull instancetype)initWithSize:(CGFloat)size - style:(MDCTriangleEdgeStyle)style NS_DESIGNATED_INITIALIZER; - -- (nonnull instancetype)init NS_UNAVAILABLE; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m deleted file mode 100644 index 08123125..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCTriangleEdgeTreatment.h" - -#import "MaterialShapes.h" - -@implementation MDCTriangleEdgeTreatment - -- (instancetype)initWithSize:(CGFloat)size style:(MDCTriangleEdgeStyle)style { - if (self = [super init]) { - _size = size; - _style = style; - } - return self; -} - -- (MDCPathGenerator *)pathGeneratorForEdgeWithLength:(CGFloat)length { - BOOL isCut = (self.style == MDCTriangleEdgeStyleCut); - MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; - [path addLineToPoint:CGPointMake(length / 2 - _size, 0)]; - [path addLineToPoint:CGPointMake(length / 2, isCut ? _size : -_size)]; - [path addLineToPoint:CGPointMake(length / 2 + _size, 0)]; - [path addLineToPoint:CGPointMake(length, 0)]; - return path; -} - -- (id)copyWithZone:(NSZone *)__unused zone { - return [[[self class] alloc] initWithSize:_size style:_style]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h deleted file mode 100644 index 1dc2f630..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/ShapeLibrary/src/MaterialShapeLibrary.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCornerTreatment+CornerTypeInitalizer.h" -#import "MDCCurvedCornerTreatment.h" -#import "MDCCurvedRectShapeGenerator.h" -#import "MDCCutCornerTreatment.h" -#import "MDCPillShapeGenerator.h" -#import "MDCRoundedCornerTreatment.h" -#import "MDCSlantedRectShapeGenerator.h" -#import "MDCTriangleEdgeTreatment.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h deleted file mode 100644 index ea5cc277..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCPathGenerator; - -/** - This enum consists of the different types of shape values that can be provided. - - - MDCCornerTreatmentValueTypeAbsolute: If an absolute corner value is provided. - - MDCCornerTreatmentValueTypePercentage: If a relative corner value is provided. - - See MDCShapeCorner's @c size property for additional details. - */ -typedef NS_ENUM(NSInteger, MDCCornerTreatmentValueType) { - MDCCornerTreatmentValueTypeAbsolute, - MDCCornerTreatmentValueTypePercentage, -}; - -/** - MDCCornerTreatment is a factory for creating MDCPathGenerators that represent - the path of a corner. - - MDCCornerTreatments should only generate corners in the top-left quadrant (i.e. - the top-left corner of a rectangle). MDCShapeModel will translate the generated - MDCPathGenerator to the expected position and rotation. - */ -@interface MDCCornerTreatment : NSObject - -/** - The value type of our corner treatment. - - When MDCCornerTreatmentValueType is MDCCornerTreatmentValueTypeAbsolute, then the accepted corner - values are an absolute size. - When MDCShapeSizeType is MDCCornerTreatmentValueTypePercentage, values are expected to be in the - range of 0 to 1 (0% - 100%). These values are percentages based on the height of the surface. - */ -@property(assign, nonatomic) MDCCornerTreatmentValueType valueType; - -- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER; - -/** - Creates an MDCPathGenerator object for a corner with the provided angle. - - @param angle The internal angle of the corner in radians. Typically M_PI/2. - */ -- (nonnull MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle; - -/** - Creates an MDCPathGenerator object for a corner with the provided angle. - Given that the provided valueType is MDCCornerTreatmentValueTypePercentage, we also need - the size of the view to calculate the corner size percentage relative to the view height. - - @param angle the internal angle of the corner in radius. Typically M_PI/2. - @param size the size of the view. - @return returns an MDCPathGenerator. - */ -- (nonnull MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)angle - forViewSize:(CGSize)size; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m deleted file mode 100644 index bc8934aa..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCCornerTreatment.m +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCornerTreatment.h" - -#import "MDCPathGenerator.h" - -@implementation MDCCornerTreatment - -- (instancetype)init { - _valueType = MDCCornerTreatmentValueTypeAbsolute; - return [super init]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)__unused angle { - return [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; -} - -- (MDCPathGenerator *)pathGeneratorForCornerWithAngle:(CGFloat)__unused angle - forViewSize:(CGSize)__unused viewSize { - return [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; -} - -- (id)copyWithZone:(nullable NSZone *)__unused zone { - MDCCornerTreatment *copy = [[[self class] alloc] init]; - copy.valueType = _valueType; - return copy; -} - -- (BOOL)isEqual:(id)object { - if (object == self) { - return YES; - } - if (!object || ![[object class] isEqual:[self class]]) { - return NO; - } - MDCCornerTreatment *otherCorner = (MDCCornerTreatment *)object; - return self.valueType == otherCorner.valueType; -} - -- (NSUInteger)hash { - return (NSUInteger)self.valueType; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h deleted file mode 100644 index be646ff0..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@class MDCPathGenerator; - -/** - MDCEdgeTreatment is a factory for creating MDCPathGenerators that represent the - path of a edge. - - MDCEdgeTreaments only generate in the top quadrant (i.e. the top edge of a - rectangle). MDCShapeModel will transform the generated MDCPathGenerator to the - expected position and rotation. - */ -@interface MDCEdgeTreatment : NSObject - -- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER; - -/** - Generates an MDCPathGenerator object for an edge with the provided length. - - @param length The length of the edge. - */ -- (nonnull MDCPathGenerator *)pathGeneratorForEdgeWithLength:(CGFloat)length; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m deleted file mode 100644 index 26d24f11..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCEdgeTreatment.m +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCEdgeTreatment.h" - -#import "MDCPathGenerator.h" - -@implementation MDCEdgeTreatment - -- (instancetype)init { - return [super init]; -} - -- (MDCPathGenerator *)pathGeneratorForEdgeWithLength:(CGFloat)length { - MDCPathGenerator *path = [MDCPathGenerator pathGeneratorWithStartPoint:CGPointZero]; - [path addLineToPoint:CGPointMake(length, 0)]; - return path; -} - -- (id)copyWithZone:(nullable NSZone *)__unused zone { - return [[[self class] alloc] init]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h deleted file mode 100644 index d8f204b9..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -/** - MDCPathGenerator is a factory for creating CGPaths. Describe your path with the - lineTo and addArc... methods, then call appendToCGPath to append them to a - CGPath. - - @note MDCPathGenerators always start at (0, 0) and end at @c endPoint. - */ -@interface MDCPathGenerator : NSObject - -/** - The start point of the generated paths. - */ -@property(nonatomic, readonly) CGPoint startPoint; - -/** - The end point of the generated paths. - */ -@property(nonatomic, readonly) CGPoint endPoint; - -/** - Returns an initialized MDCPathGenerator instance with a startPoint of CGPointZero. - */ -+ (nonnull instancetype)pathGenerator; - -/** - Returns an initialized MDCPathGenerator instance. - - @param startPoint The start point of the generated paths. - */ -+ (nonnull instancetype)pathGeneratorWithStartPoint:(CGPoint)startPoint; - -/** - Appends a straight line segment to the path generator. Analogous to - CGPathAddLineToPoint. - - @param point The end point of the line segment. - */ -- (void)addLineToPoint:(CGPoint)point; - -/** - Appends an arc to the path generator, possibly preceded by a straight line - segment. Analogous to CGPathAddArc. - - @param center The center of the arc. - @param radius The radius of the arc. - @param startAngle The start angle of the arc. - @param endAngle The end angle of the arc. - @param clockwise Whether the arc is clockwise (YES) or counterclockwise (NO). - */ -- (void)addArcWithCenter:(CGPoint)center - radius:(CGFloat)radius - startAngle:(CGFloat)startAngle - endAngle:(CGFloat)endAngle - clockwise:(BOOL)clockwise; - -/** - Appends an arc to the path generator, possibly preceded by a straight line - segment. Analogous to CGPathAddArcToPoint. The arc will be drawn inside the - corner created by the start of the arc, tangentPoint and toPoint. - - @param tangentPoint The corner of the arc. - @param toPoint The final point of the arc. - @param radius The radius inside of the arc. - */ -- (void)addArcWithTangentPoint:(CGPoint)tangentPoint - toPoint:(CGPoint)toPoint - radius:(CGFloat)radius; - -/** - Appends a cubic Bézier curve to the path generator. - - @param controlPoint1 The first control point - @param controlPoint2 The second control point - @param toPoint The end of the curve - */ -- (void)addCurveWithControlPoint1:(CGPoint)controlPoint1 - controlPoint2:(CGPoint)controlPoint2 - toPoint:(CGPoint)toPoint; - -/** - Appends a quadratic Bézier curve to the path generator. - - @param controlPoint The control point - @param toPoint The end of the curve - */ -- (void)addQuadCurveWithControlPoint:(CGPoint)controlPoint toPoint:(CGPoint)toPoint; - -/** - Appends the recorded path operations to a CGPath using the provided transform. - - @param cgPath A mutable CGPath to which the saved path operations will be - appended. - @param transform The transform applied ot each path operation before appending - them to the CGPath. - */ -- (void)appendToCGPath:(nonnull CGMutablePathRef)cgPath - transform:(nullable CGAffineTransform *)transform; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m deleted file mode 100644 index 095a2ad5..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCPathGenerator.m +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCPathGenerator.h" - - -@interface MDCPathCommand : NSObject -- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform; -@end - -@interface MDCPathLineCommand : MDCPathCommand -@property(nonatomic, assign) CGPoint point; -@end - -@interface MDCPathArcCommand : MDCPathCommand -@property(nonatomic, assign) CGPoint point; -@property(nonatomic, assign) CGFloat radius; -@property(nonatomic, assign) CGFloat startAngle; -@property(nonatomic, assign) CGFloat endAngle; -@property(nonatomic, assign) BOOL clockwise; -@end - -@interface MDCPathArcToCommand : MDCPathCommand -@property(nonatomic, assign) CGPoint start; -@property(nonatomic, assign) CGPoint end; -@property(nonatomic, assign) CGFloat radius; -@end - -@interface MDCPathCurveCommand : MDCPathCommand -@property(nonatomic, assign) CGPoint control1; -@property(nonatomic, assign) CGPoint control2; -@property(nonatomic, assign) CGPoint end; -@end - -@interface MDCPathQuadCurveCommand : MDCPathCommand -@property(nonatomic, assign) CGPoint control; -@property(nonatomic, assign) CGPoint end; -@end - -@implementation MDCPathGenerator { - NSMutableArray *_operations; - CGPoint _startPoint; - CGPoint _endPoint; -} - -+ (nonnull instancetype)pathGenerator { - return [[self alloc] initWithStartPoint:CGPointZero]; -} - -+ (instancetype)pathGeneratorWithStartPoint:(CGPoint)start { - return [[self alloc] initWithStartPoint:start]; -} - -- (instancetype)initWithStartPoint:(CGPoint)start { - if (self = [super init]) { - _operations = [NSMutableArray array]; - - _startPoint = start; - _endPoint = start; - } - return self; -} - -- (void)addLineToPoint:(CGPoint)point { - MDCPathLineCommand *op = [[MDCPathLineCommand alloc] init]; - op.point = point; - [_operations addObject:op]; - - _endPoint = point; -} - -- (void)addArcWithCenter:(CGPoint)center - radius:(CGFloat)radius - startAngle:(CGFloat)startAngle - endAngle:(CGFloat)endAngle - clockwise:(BOOL)clockwise { - MDCPathArcCommand *op = [[MDCPathArcCommand alloc] init]; - op.point = center; - op.radius = radius; - op.startAngle = startAngle; - op.endAngle = endAngle; - op.clockwise = clockwise; - [_operations addObject:op]; - - _endPoint = - CGPointMake(center.x + radius * cos(endAngle), center.y + radius * sin(endAngle)); -} - -- (void)addArcWithTangentPoint:(CGPoint)tangentPoint - toPoint:(CGPoint)toPoint - radius:(CGFloat)radius { - MDCPathArcToCommand *op = [[MDCPathArcToCommand alloc] init]; - op.start = tangentPoint; - op.end = toPoint; - op.radius = radius; - [_operations addObject:op]; - - _endPoint = toPoint; -} - -- (void)addCurveWithControlPoint1:(CGPoint)controlPoint1 - controlPoint2:(CGPoint)controlPoint2 - toPoint:(CGPoint)toPoint { - MDCPathCurveCommand *op = [[MDCPathCurveCommand alloc] init]; - op.control1 = controlPoint1; - op.control2 = controlPoint2; - op.end = toPoint; - [_operations addObject:op]; - - _endPoint = toPoint; -} - -- (void)addQuadCurveWithControlPoint:(CGPoint)controlPoint toPoint:(CGPoint)toPoint { - MDCPathQuadCurveCommand *op = [[MDCPathQuadCurveCommand alloc] init]; - op.control = controlPoint; - op.end = toPoint; - [_operations addObject:op]; - - _endPoint = toPoint; -} - -- (void)appendToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { - for (MDCPathCommand *op in _operations) { - [op applyToCGPath:cgPath transform:transform]; - } -} - -@end - -@implementation MDCPathCommand - -- (void)applyToCGPath:(CGMutablePathRef)__unused cgPath - transform:(CGAffineTransform *)__unused transform { - // no-op -} - -@end - -@implementation MDCPathLineCommand - -- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { - CGPathAddLineToPoint(cgPath, transform, self.point.x, self.point.y); -} - -@end - -@implementation MDCPathArcCommand -- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { - CGPathAddArc(cgPath, transform, self.point.x, self.point.y, self.radius, self.startAngle, - self.endAngle, self.clockwise); -} -@end - -@implementation MDCPathArcToCommand - -- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { - CGPathAddArcToPoint(cgPath, transform, self.start.x, self.start.y, self.end.x, self.end.y, - self.radius); -} - -@end - -@implementation MDCPathCurveCommand - -- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { - CGPathAddCurveToPoint(cgPath, transform, self.control1.x, self.control1.y, self.control2.x, - self.control2.y, self.end.x, self.end.y); -} - -@end - -@implementation MDCPathQuadCurveCommand - -- (void)applyToCGPath:(CGMutablePathRef)cgPath transform:(CGAffineTransform *)transform { - CGPathAddQuadCurveToPoint(cgPath, transform, self.control.x, self.control.y, self.end.x, - self.end.y); -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h deleted file mode 100644 index 100338c6..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCShapeGenerating.h" - -@class MDCCornerTreatment; -@class MDCEdgeTreatment; - -/** - An MDCShapeGenerating for creating shaped rectanglular CGPaths. - - By default MDCRectangleShapeGenerator creates rectanglular CGPaths. Set the corner and edge - treatments to shape parts of the generated path. - */ -@interface MDCRectangleShapeGenerator : NSObject - -/** - The corner treatments to apply to each corner. - */ -@property(nonatomic, strong) MDCCornerTreatment *topLeftCorner; -@property(nonatomic, strong) MDCCornerTreatment *topRightCorner; -@property(nonatomic, strong) MDCCornerTreatment *bottomLeftCorner; -@property(nonatomic, strong) MDCCornerTreatment *bottomRightCorner; - -/** - The offsets to apply to each corner. - */ -@property(nonatomic, assign) CGPoint topLeftCornerOffset; -@property(nonatomic, assign) CGPoint topRightCornerOffset; -@property(nonatomic, assign) CGPoint bottomLeftCornerOffset; -@property(nonatomic, assign) CGPoint bottomRightCornerOffset; - -/** - The edge treatments to apply to each edge. - */ -@property(nonatomic, strong) MDCEdgeTreatment *topEdge; -@property(nonatomic, strong) MDCEdgeTreatment *rightEdge; -@property(nonatomic, strong) MDCEdgeTreatment *bottomEdge; -@property(nonatomic, strong) MDCEdgeTreatment *leftEdge; - -/** - Convenience to set all corners to the same MDCCornerTreatment instance. - */ -- (void)setCorners:(MDCCornerTreatment *)cornerShape; - -/** - Conveninece to set all edge treatments to the same MDCEdgeTreatment instance. - */ -- (void)setEdges:(MDCEdgeTreatment *)edgeShape; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m deleted file mode 100644 index 41ee45e2..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCRectangleShapeGenerator.m +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCRectangleShapeGenerator.h" - -#import "MDCCornerTreatment.h" -#import "MDCEdgeTreatment.h" -#import "MDCPathGenerator.h" - -static inline CGFloat CGPointDistanceToPoint(CGPoint a, CGPoint b) { - return hypot(a.x - b.x, a.y - b.y); -} - -// Edges in clockwise order -typedef enum : NSUInteger { - MDCShapeEdgeTop = 0, - MDCShapeEdgeRight, - MDCShapeEdgeBottom, - MDCShapeEdgeLeft, -} MDCShapeEdgePosition; - -// Corners in clockwise order -typedef enum : NSUInteger { - MDCShapeCornerTopLeft = 0, - MDCShapeCornerTopRight, - MDCShapeCornerBottomRight, - MDCShapeCornerBottomLeft, -} MDCShapeCornerPosition; - -@implementation MDCRectangleShapeGenerator - -- (instancetype)init { - if (self = [super init]) { - [self setEdges:[[MDCEdgeTreatment alloc] init]]; - [self setCorners:[[MDCCornerTreatment alloc] init]]; - } - return self; -} - -- (id)copyWithZone:(NSZone *)zone { - MDCRectangleShapeGenerator *copy = [[[self class] alloc] init]; - - copy.topLeftCorner = [copy.topLeftCorner copyWithZone:zone]; - copy.topRightCorner = [copy.topRightCorner copyWithZone:zone]; - copy.bottomRightCorner = [copy.bottomRightCorner copyWithZone:zone]; - copy.bottomLeftCorner = [copy.bottomLeftCorner copyWithZone:zone]; - - copy.topLeftCornerOffset = copy.topLeftCornerOffset; - copy.topRightCornerOffset = copy.topRightCornerOffset; - copy.bottomRightCornerOffset = copy.bottomRightCornerOffset; - copy.bottomLeftCornerOffset = copy.bottomLeftCornerOffset; - - copy.topEdge = [copy.topEdge copyWithZone:zone]; - copy.rightEdge = [copy.rightEdge copyWithZone:zone]; - copy.bottomEdge = [copy.bottomEdge copyWithZone:zone]; - copy.leftEdge = [copy.leftEdge copyWithZone:zone]; - - return copy; -} - -- (void)setCorners:(MDCCornerTreatment *)cornerShape { - self.topLeftCorner = [cornerShape copy]; - self.topRightCorner = [cornerShape copy]; - self.bottomRightCorner = [cornerShape copy]; - self.bottomLeftCorner = [cornerShape copy]; -} - -- (void)setEdges:(MDCEdgeTreatment *)edgeShape { - self.topEdge = [edgeShape copy]; - self.rightEdge = [edgeShape copy]; - self.bottomEdge = [edgeShape copy]; - self.leftEdge = [edgeShape copy]; -} - -- (MDCCornerTreatment *)cornerTreatmentForPosition:(MDCShapeCornerPosition)position { - switch (position) { - case MDCShapeCornerTopLeft: - return self.topLeftCorner; - case MDCShapeCornerTopRight: - return self.topRightCorner; - case MDCShapeCornerBottomLeft: - return self.bottomLeftCorner; - case MDCShapeCornerBottomRight: - return self.bottomRightCorner; - } -} - -- (CGPoint)cornerOffsetForPosition:(MDCShapeCornerPosition)position { - switch (position) { - case MDCShapeCornerTopLeft: - return self.topLeftCornerOffset; - case MDCShapeCornerTopRight: - return self.topRightCornerOffset; - case MDCShapeCornerBottomLeft: - return self.bottomLeftCornerOffset; - case MDCShapeCornerBottomRight: - return self.bottomRightCornerOffset; - } -} - -- (MDCEdgeTreatment *)edgeTreatmentForPosition:(MDCShapeEdgePosition)position { - switch (position) { - case MDCShapeEdgeTop: - return self.topEdge; - case MDCShapeEdgeLeft: - return self.leftEdge; - case MDCShapeEdgeRight: - return self.rightEdge; - case MDCShapeEdgeBottom: - return self.bottomEdge; - } -} - -- (CGPathRef)pathForSize:(CGSize)size { - CGMutablePathRef path = CGPathCreateMutable(); - MDCPathGenerator *cornerPaths[4]; - CGAffineTransform cornerTransforms[4]; - CGAffineTransform edgeTransforms[4]; - CGFloat edgeAngles[4]; - CGFloat edgeLengths[4]; - - // Start by getting the path of each corner and calculating edge angles. - for (NSInteger i = 0; i < 4; i++) { - MDCCornerTreatment *cornerShape = [self cornerTreatmentForPosition:i]; - CGFloat cornerAngle = [self angleOfCorner:i forViewSize:size]; - if (cornerShape.valueType == MDCCornerTreatmentValueTypeAbsolute) { - cornerPaths[i] = [cornerShape pathGeneratorForCornerWithAngle:cornerAngle]; - } else if (cornerShape.valueType == MDCCornerTreatmentValueTypePercentage) { - cornerPaths[i] = [cornerShape pathGeneratorForCornerWithAngle:cornerAngle forViewSize:size]; - } - edgeAngles[i] = [self angleOfEdge:i forViewSize:size]; - } - - // Create transformation matrices for each corner and edge - for (NSInteger i = 0; i < 4; i++) { - CGPoint cornerCoords = [self cornerCoordsForPosition:i forViewSize:size]; - CGAffineTransform cornerTransform = - CGAffineTransformMakeTranslation(cornerCoords.x, cornerCoords.y); - CGFloat prevEdgeAngle = edgeAngles[(i + 4 - 1) % 4]; - // We add 90 degrees (M_PI_2) here because the corner starts rotated from the edge. - cornerTransform = CGAffineTransformRotate(cornerTransform, prevEdgeAngle + (CGFloat)M_PI_2); - cornerTransforms[i] = cornerTransform; - - CGPoint edgeStartPoint = - CGPointApplyAffineTransform(cornerPaths[i].endPoint, cornerTransforms[i]); - CGAffineTransform edgeTransform = - CGAffineTransformMakeTranslation(edgeStartPoint.x, edgeStartPoint.y); - CGFloat edgeAngle = edgeAngles[i]; - edgeTransform = CGAffineTransformRotate(edgeTransform, edgeAngle); - edgeTransforms[i] = edgeTransform; - } - - // Calculate the length of each edge using the transformed corner paths. - for (NSInteger i = 0; i < 4; i++) { - NSInteger next = (i + 1) % 4; - CGPoint edgeStartPoint = - CGPointApplyAffineTransform(cornerPaths[i].endPoint, cornerTransforms[i]); - CGPoint edgeEndPoint = - CGPointApplyAffineTransform(cornerPaths[next].startPoint, cornerTransforms[next]); - edgeLengths[i] = CGPointDistanceToPoint(edgeStartPoint, edgeEndPoint); - } - - // Draw the first corner manually because we have to MoveToPoint to start the path. - CGPathMoveToPoint(path, &cornerTransforms[0], cornerPaths[0].startPoint.x, - cornerPaths[0].startPoint.y); - [cornerPaths[0] appendToCGPath:path transform:&cornerTransforms[0]]; - - // Draw the remaining three corners joined by edges. - for (NSInteger i = 1; i < 4; i++) { - // draw the edge from the previous point to the current point - MDCEdgeTreatment *edge = [self edgeTreatmentForPosition:(i - 1)]; - MDCPathGenerator *edgePath = [edge pathGeneratorForEdgeWithLength:edgeLengths[i - 1]]; - [edgePath appendToCGPath:path transform:&edgeTransforms[i - 1]]; - - MDCPathGenerator *cornerPath = cornerPaths[i]; - [cornerPath appendToCGPath:path transform:&cornerTransforms[i]]; - } - - // Draw final edge back to first point. - MDCEdgeTreatment *edge = [self edgeTreatmentForPosition:3]; - MDCPathGenerator *edgePath = [edge pathGeneratorForEdgeWithLength:edgeLengths[3]]; - [edgePath appendToCGPath:path transform:&edgeTransforms[3]]; - - CGPathCloseSubpath(path); - - return CFAutorelease(path); -} - -- (CGFloat)angleOfCorner:(MDCShapeCornerPosition)cornerPosition forViewSize:(CGSize)size { - CGPoint prevCornerCoord = [self cornerCoordsForPosition:(cornerPosition - 1 + 4) % 4 - forViewSize:size]; - CGPoint nextCornerCoord = [self cornerCoordsForPosition:(cornerPosition + 1) % 4 - forViewSize:size]; - CGPoint cornerCoord = [self cornerCoordsForPosition:cornerPosition forViewSize:size]; - CGPoint prevVector = - CGPointMake(prevCornerCoord.x - cornerCoord.x, prevCornerCoord.y - cornerCoord.y); - CGPoint nextVector = - CGPointMake(nextCornerCoord.x - cornerCoord.x, nextCornerCoord.y - cornerCoord.y); - CGFloat prevAngle = atan2(prevVector.y, prevVector.x); - CGFloat nextAngle = atan2(nextVector.y, nextVector.x); - CGFloat angle = prevAngle - nextAngle; - if (angle < 0) - angle += (CGFloat)(2 * M_PI); - return angle; -} - -- (CGFloat)angleOfEdge:(MDCShapeEdgePosition)edgePosition forViewSize:(CGSize)size { - MDCShapeCornerPosition startCornerPosition = (MDCShapeCornerPosition)edgePosition; - MDCShapeCornerPosition endCornerPosition = (startCornerPosition + 1) % 4; - CGPoint startCornerCoord = [self cornerCoordsForPosition:startCornerPosition forViewSize:size]; - CGPoint endCornerCoord = [self cornerCoordsForPosition:endCornerPosition forViewSize:size]; - - CGPoint edgeVector = - CGPointMake(endCornerCoord.x - startCornerCoord.x, endCornerCoord.y - startCornerCoord.y); - return atan2(edgeVector.y, edgeVector.x); -} - -- (CGPoint)cornerCoordsForPosition:(MDCShapeCornerPosition)cornerPosition - forViewSize:(CGSize)viewSize { - CGPoint offset = [self cornerOffsetForPosition:cornerPosition]; - CGPoint translation; - switch (cornerPosition) { - case MDCShapeCornerTopLeft: - translation = CGPointMake(0, 0); - break; - case MDCShapeCornerTopRight: - translation = CGPointMake(viewSize.width, 0); - break; - case MDCShapeCornerBottomLeft: - translation = CGPointMake(0, viewSize.height); - break; - case MDCShapeCornerBottomRight: - translation = CGPointMake(viewSize.width, viewSize.height); - break; - } - - return CGPointMake(offset.x + translation.x, offset.y + translation.y); -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h deleted file mode 100644 index be235c10..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeGenerating.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - A protocol for objects that create closed CGPaths of varying sizes. - */ -@protocol MDCShapeGenerating - -/** - Creates a CGPath for the given size. - - @param size The expected size of the generated path. - @return CGPathRef A closed path of the provided size. If size is empty, may return NULL. - */ -- (nullable CGPathRef)pathForSize:(CGSize)size; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h deleted file mode 100644 index 37685471..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -@protocol MDCShapeGenerating; - -/* - Shape support addition to a layer. - - This class is used to add Shape support to a given view by providing it with the view's main layer. - Having shape support means that by setting a shapeGenerator, you are able to have a custom shape - using a custom path for that view. - - To have your instantiating view's backgroundColor, borderColor, and borderWidth also follow the - custom shape, your view must have these APIs internally set and get from MDCShapeMediator's - corresponding shapedBackgroundColor, shapedBorderColor, and shapedBorderWidth properties. - - This class exists to supersede MDCShapedShadowLayer which previously added shape support by - overriding the layerClass and subclassing MDCShadowLayer, which are both behaviors we want to no - longer support due to their complexity and constraints. - */ -__attribute__((objc_subclassing_restricted)) @interface MDCShapeMediator : NSObject - -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** - Designated initializer for a view requiring shape support to initialize with the view’s Core - Animation layer used for rendering (view.layer). - - The MDCShapeMediator class then keeps a weak reference to the layer and adds the required sublayers - in order to support custom shapes using custom paths. It also updates the layer's properties to - have the border and background properties as well as its shadow path be up to date with the shape's - custom path it gets through the shapeGenerator API. - */ -- (nonnull instancetype)initWithViewLayer:(nonnull CALayer *)viewLayer NS_DESIGNATED_INITIALIZER; - -/** - Call this function in layoutSubviews of the viewLayer's view to allow the shape generator to - generate the proper shape path when the bounds are now apparent. - */ -- (void)layoutShapedSublayers; - -/** - The layer which the shape logic sits on. Assign the parent layer using @c initWithLayer: - */ -@property(nonatomic, readonly, weak, nullable) CALayer *viewLayer; - -/* - Sets the shaped background color of the layer. - - Use shapedBackgroundColor instead of backgroundColor to ensure the background appears correct with - or without a valid shape. - - @note If you set shapedBackgroundColor, you should not manually write to backgroundColor or - fillColor. - */ -@property(nonatomic, copy, nullable) UIColor *shapedBackgroundColor; - -/* - Sets the shaped border color of the layer. - - Use shapedBorderColor instead of borderColor to ensure the border appears correct with or without - a valid shape. - - @note If you set shapedBorderColor, you should not manually write to borderColor. - */ -@property(nonatomic, copy, nullable) UIColor *shapedBorderColor; - -/* - Sets the shaped border width of the layer. - - Use shapedBorderWidth instead of borderWidth to ensure the border appears correct with or without - a valid shape. - - @note If you set shapedBorderWidth, you should not manually write to borderWidth. - */ -@property(nonatomic, assign) CGFloat shapedBorderWidth; - -/* - The MDCShapeGenerating object used to set the shape's path and shadow path. - - The path will be set upon assignment of this property and whenever layoutSublayers is called. - */ -@property(nonatomic, strong, nullable) id shapeGenerator; - -/* - The created CAShapeLayer representing the generated shape path for the implementing UIView - from the shapeGenerator. - - This layer is exposed to easily mask subviews of the implementing UIView so they won't spill - outside the layer to fit the bounds. - */ -@property(nonatomic, strong, nonnull) CAShapeLayer *shapeLayer; - -/* - A sublayer of @c shapeLayer that is responsible for the background color of the shape layer. - - The colorLayer imitates the path of shapeLayer and is added as a sublayer. It is updated when - shapedBackgroundColor is set on the layer. - */ -@property(nonatomic, strong, nonnull) CAShapeLayer *colorLayer; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m deleted file mode 100644 index b057bb35..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapeMediator.m +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2021-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShapeMediator.h" - -#import "MDCShapeGenerating.h" -#import "MaterialColor.h" - -// An epsilon for use with width/height values. -static const CGFloat kDimensionalEpsilon = 0.001; - -@implementation MDCShapeMediator - -- (instancetype)initWithViewLayer:(CALayer *)viewLayer { - self = [super init]; - if (self) { - _viewLayer = viewLayer; - _viewLayer.backgroundColor = [UIColor clearColor].CGColor; - _colorLayer = [CAShapeLayer layer]; - _colorLayer.delegate = (id)_viewLayer; - _shapeLayer = [CAShapeLayer layer]; - [_viewLayer insertSublayer:_colorLayer atIndex:0]; - } - return self; -} - -- (void)layoutShapedSublayers { - CGRect bounds = _viewLayer.bounds; - - CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - _colorLayer.position = center; - _colorLayer.bounds = bounds; - - [self prepareShadowPath]; -} - -- (void)prepareShadowPath { - if (self.shapeGenerator) { - CGRect standardizedBounds = CGRectStandardize(_viewLayer.bounds); - self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; - } -} - -- (void)setShapeGenerator:(id)shapeGenerator { - _shapeGenerator = shapeGenerator; - - CGRect standardizedBounds = CGRectStandardize(_viewLayer.bounds); - self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; -} - -- (void)setPath:(CGPathRef)path { - _viewLayer.shadowPath = path; - _colorLayer.path = path; - _shapeLayer.path = path; - - if (CGPathIsEmpty(path)) { - _viewLayer.backgroundColor = self.shapedBackgroundColor.CGColor; - _viewLayer.borderColor = self.shapedBorderColor.CGColor; - _viewLayer.borderWidth = self.shapedBorderWidth; - - _colorLayer.fillColor = nil; - _colorLayer.strokeColor = nil; - _colorLayer.lineWidth = 0; - } else { - _viewLayer.backgroundColor = nil; - _viewLayer.borderColor = nil; - _viewLayer.borderWidth = 0; - - _colorLayer.fillColor = self.shapedBackgroundColor.CGColor; - _colorLayer.strokeColor = self.shapedBorderColor.CGColor; - _colorLayer.lineWidth = self.shapedBorderWidth; - [self generateColorPathGivenLineWidth]; - } -} - -- (void)generateColorPathGivenLineWidth { - if (CGPathIsEmpty(self.path) || _colorLayer.lineWidth <= 0) { - _colorLayer.path = _viewLayer.shadowPath; - _shapeLayer.path = _viewLayer.shadowPath; - return; - } - CGFloat halfOfBorderWidth = self.shapedBorderWidth / 2.f; - CGAffineTransform colorLayerTransform = [self generateTransformInsetByValue:halfOfBorderWidth]; - CGPathRef colorLayerPath = - CGPathCreateCopyByTransformingPath(_viewLayer.shadowPath, &colorLayerTransform); - _colorLayer.path = colorLayerPath; - CGPathRelease(colorLayerPath); - // The shape layer is used to provide the user a mask for their content, which means also - // show the full border. Because the border is shown half outside and half inside - // the color layer path, we must inset the shape layer by the full border width. - CGAffineTransform shapeLayerTransform = - [self generateTransformInsetByValue:self.shapedBorderWidth]; - CGPathRef shapeLayerPath = - CGPathCreateCopyByTransformingPath(_viewLayer.shadowPath, &shapeLayerTransform); - _shapeLayer.path = shapeLayerPath; - CGPathRelease(shapeLayerPath); -} - -- (CGAffineTransform)generateTransformInsetByValue:(CGFloat)value { - // Use the identitfy transfrom when inset is less than Epsilon. - if (value < kDimensionalEpsilon) { - return CGAffineTransformIdentity; - } - - // Use the path's boundingBox to get the proportion of inset value, - // because this tranform is expected to be applied on a CGPath. - CGRect pathBoundingBox = CGPathGetPathBoundingBox(_viewLayer.shadowPath); - CGRect pathStandardizedBounds = CGRectStandardize(pathBoundingBox); - - if (CGRectGetWidth(pathStandardizedBounds) < kDimensionalEpsilon || - CGRectGetHeight(pathStandardizedBounds) < kDimensionalEpsilon) { - return CGAffineTransformIdentity; - } - - CGRect insetBounds = CGRectInset(pathStandardizedBounds, value, value); - CGFloat width = CGRectGetWidth(pathStandardizedBounds); - CGFloat height = CGRectGetHeight(pathStandardizedBounds); - CGFloat pathCenterX = CGRectGetMidX(pathStandardizedBounds); - CGFloat pathCenterY = CGRectGetMidY(pathStandardizedBounds); - // Calculate the shifted center and re-center it by applying a translation transform. - // value * 2 represents the accumulated borderWidth on each side, value * 2 / width - // represents the proportion of accumulated borderWidth in path bounds, which is also - // the value used for scale transform. - // The shiftWidth represents the shifted length horizontally on the center. - CGFloat shiftWidth = value * 2 / width * pathCenterX; - // Same calculation for height. - CGFloat shiftHeight = value * 2 / height * pathCenterY; - CGAffineTransform transform = CGAffineTransformMakeTranslation(shiftWidth, shiftHeight); - transform = CGAffineTransformScale(transform, CGRectGetWidth(insetBounds) / width, - CGRectGetHeight(insetBounds) / height); - return transform; -} - -- (CGPathRef)path { - return _colorLayer.path; -} - -- (void)setShapedBackgroundColor:(UIColor *)shapedBackgroundColor { - _shapedBackgroundColor = shapedBackgroundColor; - - if ([_viewLayer.delegate isKindOfClass:[UIView class]]) { - UIView *view = (UIView *)_viewLayer.delegate; - _shapedBackgroundColor = - [_shapedBackgroundColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; - } - - if (CGPathIsEmpty(self.path)) { - _viewLayer.backgroundColor = _shapedBackgroundColor.CGColor; - _colorLayer.fillColor = nil; - } else { - _viewLayer.backgroundColor = nil; - _colorLayer.fillColor = _shapedBackgroundColor.CGColor; - } -} - -- (void)setShapedBorderColor:(UIColor *)shapedBorderColor { - _shapedBorderColor = shapedBorderColor; - - if ([_viewLayer.delegate isKindOfClass:[UIView class]]) { - UIView *view = (UIView *)_viewLayer.delegate; - _shapedBorderColor = - [_shapedBorderColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; - } - if (CGPathIsEmpty(self.path)) { - _viewLayer.borderColor = _shapedBorderColor.CGColor; - _colorLayer.strokeColor = nil; - } else { - _viewLayer.borderColor = nil; - _colorLayer.strokeColor = _shapedBorderColor.CGColor; - } -} - -- (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth { - _shapedBorderWidth = shapedBorderWidth; - - if (CGPathIsEmpty(self.path)) { - _viewLayer.borderWidth = _shapedBorderWidth; - _colorLayer.lineWidth = 0; - } else { - _viewLayer.borderWidth = 0; - _colorLayer.lineWidth = _shapedBorderWidth; - [self generateColorPathGivenLineWidth]; - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h deleted file mode 100644 index 056583b5..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialShadowLayer.h" - -@protocol MDCShapeGenerating; - -/* - A shaped and Material-shadowed layer. - */ -@interface MDCShapedShadowLayer : MDCShadowLayer - -/* - Sets the shaped background color of the layer. - - Use shapedBackgroundColor instead of backgroundColor to ensure the background appears correct with - or without a valid shape. - - @note If you set shapedBackgroundColor, you should not manually write to backgroundColor or - fillColor. - */ -@property(nonatomic, copy, nullable) UIColor *shapedBackgroundColor; - -/* - Sets the shaped border color of the layer. - - Use shapedBorderColor instead of borderColor to ensure the border appears correct with or without - a valid shape. - - @note If you set shapedBorderColor, you should not manually write to borderColor. - */ -@property(nonatomic, copy, nullable) UIColor *shapedBorderColor; - -/* - Sets the shaped border width of the layer. - - Use shapedBorderWidth instead of borderWidth to ensure the border appears correct with or without - a valid shape. - - @note If you set shapedBorderWidth, you should not manually write to borderWidth. - */ -@property(nonatomic, assign) CGFloat shapedBorderWidth; - -/* - The MDCShapeGenerating object used to set the shape's path and shadow path. - - The path will be set upon assignment of this property and whenever layoutSublayers is called. - */ -@property(nonatomic, strong, nullable) id shapeGenerator; - -/* - The created CAShapeLayer representing the generated shape path for the implementing UIView - from the shapeGenerator. - - This layer is exposed to easily mask subviews of the implementing UIView so they won't spill - outside the layer to fit the bounds. - */ -@property(nonatomic, strong, nonnull) CAShapeLayer *shapeLayer; - -/* - A sublayer of @c shapeLayer that is responsible for the background color of the shape layer. - - The colorLayer imitates the path of shapeLayer and is added as a sublayer. It is updated when - shapedBackgroundColor is set on the layer. - */ -@property(nonatomic, strong, nonnull) CAShapeLayer *colorLayer; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m deleted file mode 100644 index 09628f65..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedShadowLayer.m +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShapedShadowLayer.h" - -#import "MDCShapeGenerating.h" -#import "MaterialColor.h" - -// An epsilon for use with width/height values. -static const CGFloat kDimensionalEpsilon = 0.001; - -@implementation MDCShapedShadowLayer - -- (instancetype)init { - self = [super init]; - if (self) { - [self commonMDCShapedShadowLayerInit]; - } - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonMDCShapedShadowLayerInit]; - } - return self; -} - -- (instancetype)initWithLayer:(id)layer { - self = [super initWithLayer:layer]; - if (self && [self isKindOfClass:[MDCShapedShadowLayer class]]) { - MDCShapedShadowLayer *otherLayer = (MDCShapedShadowLayer *)layer; - - _shapeGenerator = [otherLayer.shapeGenerator copyWithZone:NULL]; - // We don't need to copy fillColor because that gets copied by [super initWithLayer:]. - - // [CALayer initWithLayer:] copies all sublayers, so we have to manually fetch our CAShapeLayer. - CALayer *sublayer = [[self sublayers] firstObject]; - if ([sublayer isKindOfClass:[CAShapeLayer class]]) { - _colorLayer = (CAShapeLayer *)sublayer; - } - } - return self; -} - -- (void)commonMDCShapedShadowLayerInit { - self.backgroundColor = [UIColor clearColor].CGColor; - _colorLayer = [CAShapeLayer layer]; - _colorLayer.delegate = self; - _shapeLayer = [CAShapeLayer layer]; - [self addSublayer:_colorLayer]; -} - -- (void)layoutSublayers { - [super layoutSublayers]; - - CGRect bounds = self.bounds; - CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - _colorLayer.position = center; - _colorLayer.bounds = bounds; -} - -- (void)prepareShadowPath { - if (self.shapeGenerator) { - CGRect standardizedBounds = CGRectStandardize(self.bounds); - self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; - } -} - -- (void)setShapeGenerator:(id)shapeGenerator { - _shapeGenerator = shapeGenerator; - - CGRect standardizedBounds = CGRectStandardize(self.bounds); - self.path = [self.shapeGenerator pathForSize:standardizedBounds.size]; -} - -- (void)setPath:(CGPathRef)path { - self.shadowPath = path; - _colorLayer.path = path; - _shapeLayer.path = path; - - if (CGPathIsEmpty(path)) { - self.backgroundColor = self.shapedBackgroundColor.CGColor; - self.borderColor = self.shapedBorderColor.CGColor; - self.borderWidth = self.shapedBorderWidth; - - _colorLayer.fillColor = nil; - _colorLayer.strokeColor = nil; - _colorLayer.lineWidth = 0; - } else { - self.backgroundColor = nil; - self.borderColor = nil; - self.borderWidth = 0; - - _colorLayer.fillColor = self.shapedBackgroundColor.CGColor; - _colorLayer.strokeColor = self.shapedBorderColor.CGColor; - _colorLayer.lineWidth = self.shapedBorderWidth; - [self generateColorPathGivenLineWidth]; - } -} - -- (void)generateColorPathGivenLineWidth { - if (CGPathIsEmpty(self.path) || _colorLayer.lineWidth <= 0) { - _colorLayer.path = self.shadowPath; - _shapeLayer.path = self.shadowPath; - return; - } - CGFloat halfOfBorderWidth = self.shapedBorderWidth / 2.f; - CGAffineTransform colorLayerTransform = [self generateTransformInsetByValue:halfOfBorderWidth]; - CGPathRef colorLayerPath = - CGPathCreateCopyByTransformingPath(self.shadowPath, &colorLayerTransform); - _colorLayer.path = colorLayerPath; - CGPathRelease(colorLayerPath); - // The shape layer is used to provide the user a mask for their content, which means also - // show the full border. Because the border is shown half outside and half inside - // the color layer path, we must inset the shape layer by the full border width. - CGAffineTransform shapeLayerTransform = - [self generateTransformInsetByValue:self.shapedBorderWidth]; - CGPathRef shapeLayerPath = - CGPathCreateCopyByTransformingPath(self.shadowPath, &shapeLayerTransform); - _shapeLayer.path = shapeLayerPath; - CGPathRelease(shapeLayerPath); -} - -- (CGAffineTransform)generateTransformInsetByValue:(CGFloat)value { - // Use the identitfy transfrom when inset is less than Epsilon. - if (value < kDimensionalEpsilon) { - return CGAffineTransformIdentity; - } - - // Use the path's boundingBox to get the proportion of inset value, - // because this tranform is expected to be applied on a CGPath. - CGRect pathBoundingBox = CGPathGetPathBoundingBox(self.shadowPath); - CGRect pathStandardizedBounds = CGRectStandardize(pathBoundingBox); - - if (CGRectGetWidth(pathStandardizedBounds) < kDimensionalEpsilon || - CGRectGetHeight(pathStandardizedBounds) < kDimensionalEpsilon) { - return CGAffineTransformIdentity; - } - - CGRect insetBounds = CGRectInset(pathStandardizedBounds, value, value); - CGFloat width = CGRectGetWidth(pathStandardizedBounds); - CGFloat height = CGRectGetHeight(pathStandardizedBounds); - CGFloat pathCenterX = CGRectGetMidX(pathStandardizedBounds); - CGFloat pathCenterY = CGRectGetMidY(pathStandardizedBounds); - // Calculate the shifted center and re-center it by applying a translation transform. - // value * 2 represents the accumulated borderWidth on each side, value * 2 / width - // represents the proportion of accumulated borderWidth in path bounds, which is also - // the value used for scale transform. - // The shiftWidth represents the shifted length horizontally on the center. - CGFloat shiftWidth = value * 2 / width * pathCenterX; - // Same calculation for height. - CGFloat shiftHeight = value * 2 / height * pathCenterY; - CGAffineTransform transform = CGAffineTransformMakeTranslation(shiftWidth, shiftHeight); - transform = CGAffineTransformScale(transform, CGRectGetWidth(insetBounds) / width, - CGRectGetHeight(insetBounds) / height); - return transform; -} - -- (CGPathRef)path { - return _colorLayer.path; -} - -- (void)setShapedBackgroundColor:(UIColor *)shapedBackgroundColor { - _shapedBackgroundColor = shapedBackgroundColor; - - if ([self.delegate isKindOfClass:[UIView class]]) { - UIView *view = (UIView *)self.delegate; - _shapedBackgroundColor = - [_shapedBackgroundColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; - } - - if (CGPathIsEmpty(self.path)) { - self.backgroundColor = _shapedBackgroundColor.CGColor; - _colorLayer.fillColor = nil; - } else { - self.backgroundColor = nil; - _colorLayer.fillColor = _shapedBackgroundColor.CGColor; - } -} - -- (void)setShapedBorderColor:(UIColor *)shapedBorderColor { - _shapedBorderColor = shapedBorderColor; - - if ([self.delegate isKindOfClass:[UIView class]]) { - UIView *view = (UIView *)self.delegate; - _shapedBorderColor = - [_shapedBorderColor mdc_resolvedColorWithTraitCollection:view.traitCollection]; - } - if (CGPathIsEmpty(self.path)) { - self.borderColor = _shapedBorderColor.CGColor; - _colorLayer.strokeColor = nil; - } else { - self.borderColor = nil; - _colorLayer.strokeColor = _shapedBorderColor.CGColor; - } -} - -- (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth { - _shapedBorderWidth = shapedBorderWidth; - - if (CGPathIsEmpty(self.path)) { - self.borderWidth = _shapedBorderWidth; - _colorLayer.lineWidth = 0; - } else { - self.borderWidth = 0; - _colorLayer.lineWidth = _shapedBorderWidth; - [self generateColorPathGivenLineWidth]; - } -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h deleted file mode 100644 index 82036c3d..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialShadowElevations.h" - -@protocol MDCShapeGenerating; - -/** - MDCShapedView is a primitive view class which makes it easy to style the shape - of the view's edges and corners. - - MDCShapedView manages mapping the paths generated by the shapeGenerator to the - backing CAShapeLayer whenever the view is resized. - */ -@interface MDCShapedView : UIView - -/** - The elevation of the layer in points. - - The higher the elevation, the more spread out the shadow is. This is distinct from the layer's - zPosition which can be used to order overlapping layers, but will have no affect on the size of - the shadow. - - Negative values act as if zero were specified. - */ -@property(nonatomic, assign) MDCShadowElevation elevation; - -/** - The shape generator used to generate a new CGPath whenever the view is resized. - */ -@property(nonatomic, strong, nullable) IBOutlet id shapeGenerator; - -/** - The stroke color of the shape generated by shapeGenerator. - - The default is nil, which results in no stroke being drawn. - */ -@property(nonatomic, copy, nullable) UIColor *shapedBorderColor; - -/** - The stroke width of the shape generated by shapeGenerator. - - The default is 0, which results in no stroke being drawn. - */ -@property(nonatomic, assign) CGFloat shapedBorderWidth; - -/** - Initializes an MDCShapedView. - - @param frame The frame of the shaped view. - @param shapeGenerator The shape generator used to set the shape of the view. - */ -- (nonnull instancetype)initWithFrame:(CGRect)frame - shapeGenerator:(nullable id)shapeGenerator - NS_DESIGNATED_INITIALIZER; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m deleted file mode 100644 index 2774d26c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MDCShapedView.m +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCShapedView.h" - -#import "MDCShapedShadowLayer.h" - -@interface MDCShapedView () -@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer; -@property(nonatomic, readonly) CGSize pathSize; -@end - -@implementation MDCShapedView - -@dynamic layer; - -+ (Class)layerClass { - return [MDCShapedShadowLayer class]; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" -// https://stackoverflow.com/questions/24458608/convenience-initializer-missing-a-self-call-to-another-initializer -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - return [super initWithCoder:aDecoder]; -} -#pragma clang diagnostic pop - -- (nonnull instancetype)initWithFrame:(CGRect)frame { - return [self initWithFrame:frame shapeGenerator:nil]; -} - -- (nonnull instancetype)initWithFrame:(CGRect)frame - shapeGenerator:(nullable id)shapeGenerator { - if (self = [super initWithFrame:frame]) { - self.layer.shapeGenerator = shapeGenerator; - } - return self; -} - -- (void)setElevation:(CGFloat)elevation { - self.layer.elevation = elevation; -} - -- (CGFloat)elevation { - return self.layer.elevation; -} - -- (void)setShapeGenerator:(id)shapeGenerator { - self.layer.shapeGenerator = shapeGenerator; -} - -- (id)shapeGenerator { - return self.layer.shapeGenerator; -} - -- (UIColor *)shapedBorderColor { - return self.layer.shapedBorderColor; -} - -- (void)setShapedBorderColor:(UIColor *)shapedBorderColor { - self.layer.shapedBorderColor = shapedBorderColor; -} - -- (CGFloat)shapedBorderWidth { - return self.layer.shapedBorderWidth; -} - -- (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth { - self.layer.shapedBorderWidth = shapedBorderWidth; -} - -// MDCShapedView captures backgroundColor assigments so that they can be set to the -// MDCShapedShadowLayer fillColor. If we don't do this the background of the layer will obscure any -// shapes drawn by the shape layer. -- (void)setBackgroundColor:(UIColor *)backgroundColor { - // We intentionally capture this and don't send it to super so that the UIView backgroundColor is - // fixed to [UIColor clearColor]. - self.layer.shapedBackgroundColor = backgroundColor; -} - -- (UIColor *)backgroundColor { - return self.layer.shapedBackgroundColor; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h deleted file mode 100644 index c68ff07e..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Shapes/src/MaterialShapes.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCCornerTreatment.h" -#import "MDCEdgeTreatment.h" -#import "MDCPathGenerator.h" -#import "MDCRectangleShapeGenerator.h" -#import "MDCShapeGenerating.h" -#import "MDCShapeMediator.h" -#import "MDCShapedShadowLayer.h" -#import "MDCShapedView.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h deleted file mode 100644 index c1c69055..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - Set of constants that describe Material's text styles. - - These are similar, but not quite equivalent, to Apple's UIFontTextStyle. - */ -typedef NSString *_Nonnull MDCTextStyle NS_TYPED_EXTENSIBLE_ENUM; - -UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline1; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline2; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline3; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline4; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline5; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleHeadline6; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleSubtitle1; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleSubtitle2; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleBody1; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleBody2; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleButton; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleCaption; -UIKIT_EXTERN MDCTextStyle const MDCTextStyleOverline; - -/** - MDCFontScaler attaches a scaling curve to a UIFont via an associated object on that font instance. - - Instances of fonts processed through MDCFontScaler will have an associated dictionary that maps - UIFontTextStyle to Font Size. Category methods on UIFont allow clients to get instances of - resized fonts based on this associated dictionary. Note that an instance of MDCFontScaler is - NOT attached to the processed font. - - This interface is similar to UIFontMetrics, but the fonts returned from MDCFontScaler do *not* - automatically adjust when the device's text size / content size category is changed. - */ -@interface MDCFontScaler : NSObject - -/** - Initializes a font scaler object with the specified text style. - - @param textStyle The style that will be used to determine the scaling curver associated with the - returned font. For example, MaterialTextStyleBody1. - @return An initialized font scaler object. - */ -- (nonnull instancetype)initForMaterialTextStyle:(MDCTextStyle)textStyle NS_DESIGNATED_INITIALIZER; - -/** - Creates and returns a font scaler object with the specified text style. - - @param textStyle The style that will be used to determine the scaling curver associated with the - returned font. For example, MaterialTextStyleBody1. - @return An initialized font scaler object. - */ -+ (nonnull instancetype)scalerForMaterialTextStyle:(MDCTextStyle)textStyle; - -- (nonnull instancetype)init NS_UNAVAILABLE; - -/** - Returns an instance of the specified font with an associated scaling curve. - - @param font The base font to use when applying the scaling curve. - @return An instance of the specified font with an associated scaling curve, and scaled to the - current Dynamic Type setting. - */ -- (nonnull UIFont *)scaledFontWithFont:(nonnull UIFont *)font; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m deleted file mode 100644 index 9da0813b..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontScaler.m +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFontScaler.h" - -#import - -#import "UIFont+MaterialScalable.h" -#import "private/MDCTypographyUtilities.h" - -MDCTextStyle const MDCTextStyleHeadline1 = @"MDC.TextStyle.Headline1"; -MDCTextStyle const MDCTextStyleHeadline2 = @"MDC.TextStyle.Headline2"; -MDCTextStyle const MDCTextStyleHeadline3 = @"MDC.TextStyle.Headline3"; -MDCTextStyle const MDCTextStyleHeadline4 = @"MDC.TextStyle.Headline4"; -MDCTextStyle const MDCTextStyleHeadline5 = @"MDC.TextStyle.Headline5"; -MDCTextStyle const MDCTextStyleHeadline6 = @"MDC.TextStyle.Headline6"; -MDCTextStyle const MDCTextStyleSubtitle1 = @"MDC.TextStyle.Subtitle1"; -MDCTextStyle const MDCTextStyleSubtitle2 = @"MDC.TextStyle.Subtitle2"; -MDCTextStyle const MDCTextStyleBody1 = @"MDC.TextStyle.Body1"; -MDCTextStyle const MDCTextStyleBody2 = @"MDC.TextStyle.Body2"; -MDCTextStyle const MDCTextStyleButton = @"MDC.TextStyle.Button"; -MDCTextStyle const MDCTextStyleCaption = @"MDC.TextStyle.Caption"; -MDCTextStyle const MDCTextStyleOverline = @"MDC.TextStyle.Overline"; - -@implementation MDCFontScaler { - NSDictionary *_scalingCurve; - MDCTextStyle _textStyle; -} - -+ (instancetype)scalerForMaterialTextStyle:(MDCTextStyle)textStyle { - return [[MDCFontScaler alloc] initForMaterialTextStyle:textStyle]; -} - -- (instancetype)initForMaterialTextStyle:(MDCTextStyle)textStyle { - self = [super init]; - if (self) { - _textStyle = [textStyle copy]; - - // NOTE: All scaling curves MUST include a full set of values for ALL UIContentSizeCategory - // values. This values must not decrease as the category size increases. To put it another - // way, the value for UIContentSizeCategoryLarge must not be smaller than the value for - // UIContentSizeCategoryMedium. - if ([MDCTextStyleHeadline1 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @84, - UIContentSizeCategorySmall : @88, - UIContentSizeCategoryMedium : @92, - UIContentSizeCategoryLarge : @96, - UIContentSizeCategoryExtraLarge : @100, - UIContentSizeCategoryExtraExtraLarge : @104, - UIContentSizeCategoryExtraExtraExtraLarge : @108, - UIContentSizeCategoryAccessibilityMedium : @108, - UIContentSizeCategoryAccessibilityLarge : @108, - UIContentSizeCategoryAccessibilityExtraLarge : @108, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @108, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @108 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleHeadline2 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @54, - UIContentSizeCategorySmall : @56, - UIContentSizeCategoryMedium : @58, - UIContentSizeCategoryLarge : @60, - UIContentSizeCategoryExtraLarge : @62, - UIContentSizeCategoryExtraExtraLarge : @64, - UIContentSizeCategoryExtraExtraExtraLarge : @66, - UIContentSizeCategoryAccessibilityMedium : @66, - UIContentSizeCategoryAccessibilityLarge : @66, - UIContentSizeCategoryAccessibilityExtraLarge : @66, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @66, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @66 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleHeadline3 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @42, - UIContentSizeCategorySmall : @44, - UIContentSizeCategoryMedium : @46, - UIContentSizeCategoryLarge : @48, - UIContentSizeCategoryExtraLarge : @50, - UIContentSizeCategoryExtraExtraLarge : @52, - UIContentSizeCategoryExtraExtraExtraLarge : @54, - UIContentSizeCategoryAccessibilityMedium : @54, - UIContentSizeCategoryAccessibilityLarge : @54, - UIContentSizeCategoryAccessibilityExtraLarge : @54, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @54, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @54 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleHeadline4 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @28, - UIContentSizeCategorySmall : @30, - UIContentSizeCategoryMedium : @32, - UIContentSizeCategoryLarge : @34, - UIContentSizeCategoryExtraLarge : @36, - UIContentSizeCategoryExtraExtraLarge : @38, - UIContentSizeCategoryExtraExtraExtraLarge : @40, - UIContentSizeCategoryAccessibilityMedium : @42, - UIContentSizeCategoryAccessibilityLarge : @42, - UIContentSizeCategoryAccessibilityExtraLarge : @42, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @42, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleHeadline5 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @21, - UIContentSizeCategorySmall : @22, - UIContentSizeCategoryMedium : @23, - UIContentSizeCategoryLarge : @24, - UIContentSizeCategoryExtraLarge : @26, - UIContentSizeCategoryExtraExtraLarge : @28, - UIContentSizeCategoryExtraExtraExtraLarge : @30, - UIContentSizeCategoryAccessibilityMedium : @32, - UIContentSizeCategoryAccessibilityLarge : @32, - UIContentSizeCategoryAccessibilityExtraLarge : @32, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @32, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @32 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleHeadline6 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @17, - UIContentSizeCategorySmall : @18, - UIContentSizeCategoryMedium : @19, - UIContentSizeCategoryLarge : @20, - UIContentSizeCategoryExtraLarge : @22, - UIContentSizeCategoryExtraExtraLarge : @24, - UIContentSizeCategoryExtraExtraExtraLarge : @26, - UIContentSizeCategoryAccessibilityMedium : @28, - UIContentSizeCategoryAccessibilityLarge : @28, - UIContentSizeCategoryAccessibilityExtraLarge : @28, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @28, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @28 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleSubtitle1 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @13, - UIContentSizeCategorySmall : @14, - UIContentSizeCategoryMedium : @15, - UIContentSizeCategoryLarge : @16, - UIContentSizeCategoryExtraLarge : @18, - UIContentSizeCategoryExtraExtraLarge : @20, - UIContentSizeCategoryExtraExtraExtraLarge : @22, - UIContentSizeCategoryAccessibilityMedium : @25, - UIContentSizeCategoryAccessibilityLarge : @30, - UIContentSizeCategoryAccessibilityExtraLarge : @37, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @44, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @52 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleSubtitle2 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @11, - UIContentSizeCategorySmall : @12, - UIContentSizeCategoryMedium : @13, - UIContentSizeCategoryLarge : @14, - UIContentSizeCategoryExtraLarge : @16, - UIContentSizeCategoryExtraExtraLarge : @18, - UIContentSizeCategoryExtraExtraExtraLarge : @20, - UIContentSizeCategoryAccessibilityMedium : @22, - UIContentSizeCategoryAccessibilityLarge : @25, - UIContentSizeCategoryAccessibilityExtraLarge : @30, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @36, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleBody2 isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @11, - UIContentSizeCategorySmall : @12, - UIContentSizeCategoryMedium : @13, - UIContentSizeCategoryLarge : @14, - UIContentSizeCategoryExtraLarge : @16, - UIContentSizeCategoryExtraExtraLarge : @18, - UIContentSizeCategoryExtraExtraExtraLarge : @20, - UIContentSizeCategoryAccessibilityMedium : @22, - UIContentSizeCategoryAccessibilityLarge : @25, - UIContentSizeCategoryAccessibilityExtraLarge : @30, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @36, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleButton isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @11, - UIContentSizeCategorySmall : @12, - UIContentSizeCategoryMedium : @13, - UIContentSizeCategoryLarge : @14, - UIContentSizeCategoryExtraLarge : @16, - UIContentSizeCategoryExtraExtraLarge : @18, - UIContentSizeCategoryExtraExtraExtraLarge : @20, - UIContentSizeCategoryAccessibilityMedium : @22, - UIContentSizeCategoryAccessibilityLarge : @24, - UIContentSizeCategoryAccessibilityExtraLarge : @26, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @28, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @30 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleCaption isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @11, - UIContentSizeCategorySmall : @11, - UIContentSizeCategoryMedium : @11, - UIContentSizeCategoryLarge : @12, - UIContentSizeCategoryExtraLarge : @14, - UIContentSizeCategoryExtraExtraLarge : @16, - UIContentSizeCategoryExtraExtraExtraLarge : @18, - UIContentSizeCategoryAccessibilityMedium : @20, - UIContentSizeCategoryAccessibilityLarge : @22, - UIContentSizeCategoryAccessibilityExtraLarge : @24, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @26, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @28 - }; - _scalingCurve = scalingCurve; - } else if ([MDCTextStyleOverline isEqualToString:textStyle]) { - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @8, - UIContentSizeCategorySmall : @8, - UIContentSizeCategoryMedium : @9, - UIContentSizeCategoryLarge : @10, - UIContentSizeCategoryExtraLarge : @12, - UIContentSizeCategoryExtraExtraLarge : @14, - UIContentSizeCategoryExtraExtraExtraLarge : @16, - UIContentSizeCategoryAccessibilityMedium : @18, - UIContentSizeCategoryAccessibilityLarge : @20, - UIContentSizeCategoryAccessibilityExtraLarge : @22, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @24, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @26 - }; - _scalingCurve = scalingCurve; - } else { - // If nothing matches, return the metrics for MDCTextStyleBody1 - _textStyle = [MDCTextStyleBody1 copy]; - NSDictionary *scalingCurve = @{ - UIContentSizeCategoryExtraSmall : @13, - UIContentSizeCategorySmall : @14, - UIContentSizeCategoryMedium : @15, - UIContentSizeCategoryLarge : @16, - UIContentSizeCategoryExtraLarge : @18, - UIContentSizeCategoryExtraExtraLarge : @20, - UIContentSizeCategoryExtraExtraExtraLarge : @22, - UIContentSizeCategoryAccessibilityMedium : @26, - UIContentSizeCategoryAccessibilityLarge : @30, - UIContentSizeCategoryAccessibilityExtraLarge : @34, - UIContentSizeCategoryAccessibilityExtraExtraLarge : @38, - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @42 - }; - _scalingCurve = scalingCurve; - } - } - - return self; -} - -- (UIFont *)scaledFontWithFont:(UIFont *)font { - // If it is available, query the preferredContentSizeCategory. - UIContentSizeCategory sizeCategory = GetCurrentSizeCategory(); - - // We create a new font to ensure we have a complete set of font traits. - // They we apply our new scaling curve before returning a scaled font. - UIFont *templateFont = [UIFont fontWithDescriptor:font.fontDescriptor size:0.0]; - templateFont.mdc_scalingCurve = _scalingCurve; - UIFont *scaledFont = [templateFont mdc_scaledFontForSizeCategory:sizeCategory]; - - return scaledFont; -} - -- (NSString *)description { - NSString *superDescription = [super description]; - NSString *styleDescription = @"No Attached Style"; - if (_textStyle) { - styleDescription = _textStyle; - } - - return [NSString stringWithFormat:@"%@ %@", superDescription, styleDescription]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h deleted file mode 100644 index 2cbe5a69..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCFontTextStyle.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - Material font text styles - - These styles are defined in: - https://material.io/go/design-typography - This enumeration is a set of semantic descriptions intended to describe the fonts returned by - + [UIFont mdc_preferredFontForMaterialTextStyle:] - + [UIFontDescriptor mdc_preferredFontDescriptorForMaterialTextStyle:] - */ -typedef NS_ENUM(NSInteger, MDCFontTextStyle) { - MDCFontTextStyleBody1, - MDCFontTextStyleBody2, - MDCFontTextStyleCaption, - MDCFontTextStyleHeadline, - MDCFontTextStyleSubheadline, - MDCFontTextStyleTitle, - MDCFontTextStyleDisplay1, - MDCFontTextStyleDisplay2, - MDCFontTextStyleDisplay3, - MDCFontTextStyleDisplay4, - MDCFontTextStyleButton, -}; diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h deleted file mode 100644 index 8ac239ac..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.h +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#pragma mark - Soon to be deprecated - -/** - MDCTypography uses this protocol to delegate responsibility of loading the custom fonts. - - The spec defines the Roboto font family and uses three fonts in the named styles. Use this - protocol to define your own fonts if there is a brand need. - - @warning This protocol will soon be deprecated. Consider using MDCTypographyScheme from the - schemes/Typography component instead. - - @see https://material.io/go/design-typography#typography-styles - */ -@protocol MDCTypographyFontLoading -@required - -/** Asks the receiver to return a font with a light weight. FontSize must be larger tha 0. */ -- (nullable UIFont *)lightFontOfSize:(CGFloat)fontSize; - -/** Asks the receiver to return a font with a normal weight. FontSize must be larger tha 0. */ -- (nonnull UIFont *)regularFontOfSize:(CGFloat)fontSize; - -/** Asks the receiver to return a font with a medium weight. FontSize must be larger tha 0. */ -- (nullable UIFont *)mediumFontOfSize:(CGFloat)fontSize; - -@optional - -/** Asks the receiver to return a font with a bold weight. FontSize must be larger tha 0. */ -- (nonnull UIFont *)boldFontOfSize:(CGFloat)fontSize; - -/** Asks the receiver to return an italic font. FontSize must be larger tha 0. */ -- (nonnull UIFont *)italicFontOfSize:(CGFloat)fontSize; - -/** Asks the receiver to return a font with an italic bold weight. FontSize must be larger tha 0. */ -- (nullable UIFont *)boldItalicFontOfSize:(CGFloat)fontSize; - -/** Returns a bold version of the specified font. */ -- (nonnull UIFont *)boldFontFromFont:(nonnull UIFont *)font; - -/** Returns an italic version of the specified font. */ -- (nonnull UIFont *)italicFontFromFont:(nonnull UIFont *)font; -/** - Asks the receiver to determine if a particular font would be considered "large" for the purposes of - calculating contrast ratios. - - Large fonts are defined as greater than 18pt normal or 14pt bold. - For more see: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html - - @param font The font to examine. - @return YES if the font is considered "large". - */ -- (BOOL)isLargeForContrastRatios:(nonnull UIFont *)font; - -@end - -/** - Typographic constants and helpers. - - To use these fonts, you must add MaterialTypography.bundle to your target. - - @warning This class will soon be deprecated. Consider using MDCTypographyScheme from the - schemes/Typography component instead. - - @see https://material.io/go/design-typography#typography-styles - */ -@interface MDCTypography : NSObject - -#pragma mark - Font loader access - -/** Set the font loader in order to use a non-system font. */ -+ (void)setFontLoader:(nonnull id)fontLoader; - -/** Get the current font loader. */ -+ (nonnull id)fontLoader; - -#pragma mark - Display fonts (extra large fonts) - -/** Returns the display 4 font. (largest of the display font sizes) */ -+ (nonnull UIFont *)display4Font; - -/** Returns the recommended opacity of black text for the display fonts 4. */ -+ (CGFloat)display4FontOpacity; - -/** Returns the display 3 font. (second largest of the display font sizes) */ -+ (nonnull UIFont *)display3Font; - -/** Returns the recommended opacity of black text for the display fonts 3. */ -+ (CGFloat)display3FontOpacity; - -/** Returns the display 2 font. (third largest of the display font sizes) */ -+ (nonnull UIFont *)display2Font; - -/** Returns the recommended opacity of black text for the display fonts 2. */ -+ (CGFloat)display2FontOpacity; - -/** Returns the display 1 font. (smallest of the display font sizes) */ -+ (nonnull UIFont *)display1Font; - -/** Returns the recommended opacity of black text for the display fonts 1. */ -+ (CGFloat)display1FontOpacity; - -#pragma mark - Common UI fonts - -/** Returns the headline font. */ -+ (nonnull UIFont *)headlineFont; - -/** Returns the recommended opacity of black text for the headline font. */ -+ (CGFloat)headlineFontOpacity; - -/** Returns the title font. */ -+ (nonnull UIFont *)titleFont; - -/** Returns the recommended opacity of black text for the title font. */ -+ (CGFloat)titleFontOpacity; - -/** Returns the subhead font. (subtitle) */ -+ (nonnull UIFont *)subheadFont; - -/** Returns the recommended opacity of black text for the subhead font. */ -+ (CGFloat)subheadFontOpacity; - -/** Returns the body 2 text font. (bold text) */ -+ (nonnull UIFont *)body2Font; - -/** Returns the recommended opacity of black text for the body 2 font. */ -+ (CGFloat)body2FontOpacity; - -/** Returns the body 1 text font. (normal text) */ -+ (nonnull UIFont *)body1Font; - -/** Returns the recommended opacity of black text for the body 1 font. */ -+ (CGFloat)body1FontOpacity; - -/** Returns the caption font. (a small font for image captions) */ -+ (nonnull UIFont *)captionFont; - -/** Returns the recommended opacity of black text for the caption font. */ -+ (CGFloat)captionFontOpacity; - -/** Returns a font for buttons. */ -+ (nonnull UIFont *)buttonFont; - -/** Returns the recommended opacity of black text for the button font. */ -+ (CGFloat)buttonFontOpacity; - -/** Returns a bold version of the specified font. */ -+ (nonnull UIFont *)boldFontFromFont:(nonnull UIFont *)font; - -/** Returns an italic version of the specified font. */ -+ (nonnull UIFont *)italicFontFromFont:(nonnull UIFont *)font; - -/** - Asks the receiver to determine if a particular font would be considered "large" for the purposes of - calculating contrast ratios. - - Large fonts are defined as greater than 18pt normal or 14pt bold. If the passed font is nil, then - this method returns NO. - For more see: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html - - @param font The font to examine. - @return YES if the font is non-nil and is considered "large". - */ -+ (BOOL)isLargeForContrastRatios:(nonnull UIFont *)font; - -@end - -/** - MDCSystemFontLoader allows you to use the system font for @c MDCTypography. - - @warning This class will soon be deprecated. Consider using MDCTypographyScheme from the - schemes/Typography component instead. - - #### Example - - ``` - [MDCTypography setFontLoader:[[MDCSystemFontLoader alloc] init]]; - ``` - */ -@interface MDCSystemFontLoader : NSObject -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m deleted file mode 100644 index 0052ecc6..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MDCTypography.m +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCTypography.h" - -#import "private/UIFont+MaterialTypographyPrivate.h" -#import - -static id gFontLoader = nil; -const CGFloat MDCTypographyStandardOpacity = (CGFloat)0.87; -const CGFloat MDCTypographySecondaryOpacity = (CGFloat)0.54; - -@implementation MDCTypography - -#pragma mark - Font loader access - -+ (void)setFontLoader:(id)fontLoader { - if (gFontLoader && fontLoader != gFontLoader) { - [[NSNotificationCenter defaultCenter] removeObserver:gFontLoader]; - } - gFontLoader = fontLoader; - NSAssert(gFontLoader, - @"Font loader can't be null. The font loader will be reset to the default font loader."); - if (!gFontLoader) { - gFontLoader = [self defaultFontLoader]; - } -} - -+ (id)fontLoader { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (!gFontLoader) { - gFontLoader = [self defaultFontLoader]; - } - }); - return gFontLoader; -} - -#pragma mark - Display fonts (extra large fonts) - -+ (UIFont *)display4Font { - return [[self fontLoader] lightFontOfSize:112]; -} - -+ (CGFloat)display4FontOpacity { - return MDCTypographySecondaryOpacity; -} - -+ (UIFont *)display3Font { - return [[self fontLoader] regularFontOfSize:56]; -} - -+ (CGFloat)display3FontOpacity { - return MDCTypographySecondaryOpacity; -} - -+ (UIFont *)display2Font { - return [[self fontLoader] regularFontOfSize:45]; -} - -+ (CGFloat)display2FontOpacity { - return MDCTypographySecondaryOpacity; -} - -+ (UIFont *)display1Font { - return [[self fontLoader] regularFontOfSize:34]; -} - -+ (CGFloat)display1FontOpacity { - return MDCTypographySecondaryOpacity; -} - -#pragma mark - Common UI fonts. - -+ (UIFont *)headlineFont { - return [[self fontLoader] regularFontOfSize:24]; -} - -+ (CGFloat)headlineFontOpacity { - return MDCTypographyStandardOpacity; -} - -+ (UIFont *)titleFont { - return [[self fontLoader] mediumFontOfSize:20]; -} - -+ (CGFloat)titleFontOpacity { - return MDCTypographyStandardOpacity; -} - -+ (UIFont *)subheadFont { - return [[self fontLoader] regularFontOfSize:16]; -} - -+ (CGFloat)subheadFontOpacity { - return MDCTypographyStandardOpacity; -} - -+ (UIFont *)body2Font { - return [[self fontLoader] mediumFontOfSize:14]; -} - -+ (CGFloat)body2FontOpacity { - return MDCTypographyStandardOpacity; -} - -+ (UIFont *)body1Font { - return [[self fontLoader] regularFontOfSize:14]; -} - -+ (CGFloat)body1FontOpacity { - return MDCTypographyStandardOpacity; -} - -+ (UIFont *)captionFont { - return [[self fontLoader] regularFontOfSize:12]; -} - -+ (CGFloat)captionFontOpacity { - return MDCTypographySecondaryOpacity; -} - -+ (UIFont *)buttonFont { - return [[self fontLoader] mediumFontOfSize:14]; -} - -+ (CGFloat)buttonFontOpacity { - return MDCTypographyStandardOpacity; -} - -+ (BOOL)isLargeForContrastRatios:(nonnull UIFont *)font { - id fontLoader = [self fontLoader]; - - if ([fontLoader respondsToSelector:@selector(isLargeForContrastRatios:)]) { - return [fontLoader isLargeForContrastRatios:font]; - } - - return [MDFTextAccessibility isLargeForContrastRatios:font]; -} - -+ (UIFont *)italicFontFromFont:(UIFont *)font { - SEL selector = @selector(italicFontFromFont:); - if ([self.fontLoader respondsToSelector:selector]) { - return [self.fontLoader italicFontFromFont:font]; - } - UIFontDescriptor *fontDescriptor = - [font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic]; - UIFont *fontFromDescriptor = [UIFont fontWithDescriptor:fontDescriptor size:0]; - return fontFromDescriptor ? fontFromDescriptor : [UIFont italicSystemFontOfSize:font.pointSize]; -} - -+ (UIFont *)boldFontFromFont:(UIFont *)font { - SEL selector = @selector(boldFontFromFont:); - if ([self.fontLoader respondsToSelector:selector]) { - return [self.fontLoader boldFontFromFont:font]; - } - UIFontDescriptorSymbolicTraits traits = UIFontDescriptorTraitBold; - if (font.mdc_slant != 0) { - traits = traits | UIFontDescriptorTraitItalic; - } - UIFontDescriptor *fontDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:traits]; - UIFont *fontFromDescriptor = [UIFont fontWithDescriptor:fontDescriptor size:0]; - return fontFromDescriptor ? fontFromDescriptor : [UIFont boldSystemFontOfSize:font.pointSize]; -} - -#pragma mark - Private - -+ (id)defaultFontLoader { - return [[MDCSystemFontLoader alloc] init]; -} - -@end - -@interface MDCSystemFontLoader () - -/* - In collectionView scrolling tests, manually caching UIFonts performs around 4.5 times better - (e.g. 230 ms vs. 1,080 ms in one test) than calling [UIFont systemFontForSize:weight:] every time. - */ -@property(nonatomic, strong) NSCache *fontCache; - -@end - -@implementation MDCSystemFontLoader - -- (instancetype)init { - self = [super init]; - if (self) { - _fontCache = [[NSCache alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(didChangeContentSizeCategory) - name:UIContentSizeCategoryDidChangeNotification - object:nil]; - } - return self; -} - -- (void)didChangeContentSizeCategory { - [_fontCache removeAllObjects]; -} - -- (nullable UIFont *)lightFontOfSize:(CGFloat)fontSize { - NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; - UIFont *font = [self.fontCache objectForKey:cacheKey]; - if (font) { - return font; - } - - font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightLight]; - if (font) { - [self.fontCache setObject:font forKey:cacheKey]; - } - return font; -} - -- (UIFont *)regularFontOfSize:(CGFloat)fontSize { - NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; - UIFont *font = [self.fontCache objectForKey:cacheKey]; - if (font) { - return font; - } - - font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightRegular]; - [self.fontCache setObject:font forKey:cacheKey]; - - return (UIFont *)font; -} - -- (nullable UIFont *)mediumFontOfSize:(CGFloat)fontSize { - NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; - UIFont *font = [self.fontCache objectForKey:cacheKey]; - if (font) { - return font; - } - - font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightMedium]; - if (font) { - [self.fontCache setObject:font forKey:cacheKey]; - } - return font; -} - -- (UIFont *)boldFontOfSize:(CGFloat)fontSize { - NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; - UIFont *font = [self.fontCache objectForKey:cacheKey]; - if (font) { - return font; - } - - font = [UIFont systemFontOfSize:fontSize weight:UIFontWeightSemibold]; - - [self.fontCache setObject:font forKey:cacheKey]; - - return font; -} - -- (UIFont *)italicFontOfSize:(CGFloat)fontSize { - NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; - UIFont *font = [self.fontCache objectForKey:cacheKey]; - if (font) { - return font; - } - - font = [UIFont italicSystemFontOfSize:fontSize]; - - [self.fontCache setObject:font forKey:cacheKey]; - - return font; -} - -- (nullable UIFont *)boldItalicFontOfSize:(CGFloat)fontSize { - NSString *cacheKey = [NSString stringWithFormat:@"%@-%06f", NSStringFromSelector(_cmd), fontSize]; - UIFont *font = [self.fontCache objectForKey:cacheKey]; - if (font) { - return font; - } - - UIFont *regular = [self regularFontOfSize:fontSize]; - UIFontDescriptor *_Nullable descriptor = [regular.fontDescriptor - fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic]; - if (!descriptor) { - return nil; - } - UIFontDescriptor *nonnullDescriptor = descriptor; - font = [UIFont fontWithDescriptor:nonnullDescriptor size:fontSize]; - - [self.fontCache setObject:font forKey:cacheKey]; - - return font; -} - -- (BOOL)isLargeForContrastRatios:(UIFont *)font { - if (font.pointSize >= 18) { - return YES; - } - if (font.pointSize < 14) { - return NO; - } - - UIFontDescriptor *fontDescriptor = font.fontDescriptor; - if ((fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) == UIFontDescriptorTraitBold) { - return YES; - } - - // We treat system font medium as large for accessibility when larger than 14. - if (font.mdc_weight >= UIFontWeightMedium) { - return YES; - } - - return NO; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h deleted file mode 100644 index 94fe5bb4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/MaterialTypography.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - The Typography component provides methods for getting sized fonts and opacities following Material - style guidelines. - - This header is the umbrella header for the component and should be imported by consumers of the - Typography component. Please do not directly import other headers. This will allow the componet to - expand or contract the header file space without consumer modifications. - */ - -#import "MDCFontScaler.h" -#import "MDCFontTextStyle.h" -#import "MDCTypography.h" -#import "UIFont+MaterialScalable.h" -#import "UIFont+MaterialSimpleEquality.h" -#import "UIFont+MaterialTypography.h" -#import "UIFontDescriptor+MaterialTypography.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h deleted file mode 100644 index f432bf18..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCFontTextStyle.h" - -/** - A representation of a mapping of UIContentSizeCategory keys to font size values. - - The values of this dictionary are CGFloat values represented as an NSNumber. Each value defines the - font size to be used for a given content size category. - */ -typedef NSDictionary *MDCScalingCurve; - -@interface UIFont (MaterialScalable) - -/** - A custom scaling curve to be used when scaling this font for Dynamic Type. - - The keys of a scaling curve MUST include the complete set of UIContentSizeCategory values, from - UIContentSizeCategoryExtraSmall to UIContentSizeCategoryExtraExtraExtraLarge AND all - UIContentSizeCategoryAccessibility categories. If any of these keys are missing then any scaling - behavior that reads from this property is undefined. - */ -@property(nonatomic, copy, nullable, setter=mdc_setScalingCurve:) MDCScalingCurve mdc_scalingCurve; - -/** - Returns a font with the same family, weight and traits, but whose point size is based on the given - size category and the corresponding value from @c mdc_scalingCurve. - - @param sizeCategory The size category for which the font should be scaled. - @return A font whose point size is extracted from @c mdc_scalingCurve for the given size category, - or self if @c mdc_scalingCurve is nil. - */ -- (nonnull UIFont *)mdc_scaledFontForSizeCategory:(nonnull UIContentSizeCategory)sizeCategory; - -/** - Returns a font with the same family, weight and traits, but whose point size is based on the given - trait environment's preferred content size category. - - @param traitEnvironment The trait environment whose trait collection should be queried. - @return A font whose point size is determined by @c mdc_scalingCurve for the given trait - environment's content size category, or self if @c mdc_scalingCurve is nil. - */ -- (nonnull UIFont *)mdc_scaledFontForTraitEnvironment: - (nonnull id)traitEnvironment; - -/** - Returns a font with the same family, weight and traits, but whose point size is based on the - default size category of UIContentSizeCategoryLarge and the corresponding value from - @c mdc_scalingCurve. - - This can be used to return a font for a text element that should *not* be scaled with Dynamic - Type. - - @return A font whose point size is extracted from @c mdc_scalingCurve for - UIContentSizeCategoryLarge, or self if @c mdc_scalingCurve is nil. - */ -- (nonnull UIFont *)mdc_scaledFontAtDefaultSize; - -/** - Returns a font with the same family, weight and traits, but whose point size is based on the - device's current content size category and the corresponding value from @c mdc_scalingCurve. - - @note Prefer @c -mdc_scaledFontForSizeCategory: because it encourages use of trait collections - instead. - - @return If @c mdc_scalingCurve is nil, returns self. On iOS 10 and above, returns a font whose - point size is extracted from @c mdc_scalingCurve for UIScreen.mainScreen's - preferredContentSizeCategory. On iOS 9, returns a font whose point size is extracted from - @c mdc_scalingCurve for UIApplication.sharedApplication's preferredContentSizeCategory, if a shared - application is available, otherwise uses UIContentSizeCategoryLarge instead. - */ -- (nonnull UIFont *)mdc_scaledFontForCurrentSizeCategory; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m deleted file mode 100644 index 54805308..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialScalable.m +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIFont+MaterialScalable.h" - -#import - -#import "MaterialApplication.h" - -#import "private/MDCTypographyUtilities.h" - -static char MDCFontScaleObjectKey; - -@implementation UIFont (MaterialScalable) - -- (UIFont *)mdc_scaledFontForSizeCategory:(UIContentSizeCategory)sizeCategory { - if (!self.mdc_scalingCurve) { - return self; - } - - NSNumber *fontSizeNumber; - if (sizeCategory) { - // Pick the correct font size from the pre-attached scaling curve that - // fits the specific size category. The scaling curve is attached based on - // the type of font, so a button font has a different scaling curve than - // a headline font, and the two will therefore see different font size numbers - // for the same size category. - fontSizeNumber = self.mdc_scalingCurve[sizeCategory]; - } - - // Guard against broken / incomplete scaling curves by returning self if fontSizeNumber is nil. - if (fontSizeNumber == nil) { - return self; - } - - CGFloat fontSize = (CGFloat)fontSizeNumber.doubleValue; - - // Guard against broken scaling curves encoded with 0.0 or negative values - if (fontSize <= 0.0) { - return self; - } - - UIFont *scaledFont = [UIFont fontWithDescriptor:self.fontDescriptor size:fontSize]; - scaledFont.mdc_scalingCurve = self.mdc_scalingCurve; - - return scaledFont; -} - -- (UIFont *)mdc_scaledFontForTraitEnvironment:(id)traitEnvironment { - UIContentSizeCategory sizeCategory = - traitEnvironment.traitCollection.preferredContentSizeCategory; - return [self mdc_scaledFontForSizeCategory:sizeCategory]; -} - -- (UIFont *)mdc_scaledFontForCurrentSizeCategory { - UIContentSizeCategory currentSizeCategory = GetCurrentSizeCategory(); - - return [self mdc_scaledFontForSizeCategory:currentSizeCategory]; -} - -- (nonnull UIFont *)mdc_scaledFontAtDefaultSize { - return [self mdc_scaledFontForSizeCategory:UIContentSizeCategoryLarge]; -} - -- (NSDictionary *)mdc_scalingCurve { - return (NSDictionary *)objc_getAssociatedObject( - self, &MDCFontScaleObjectKey); -} - -- (void)mdc_setScalingCurve:(NSDictionary *)scalingCurve { - objc_setAssociatedObject(self, &MDCFontScaleObjectKey, scalingCurve, - OBJC_ASSOCIATION_COPY_NONATOMIC); -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h deleted file mode 100644 index 1d4364df..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - This UIFont category provides a shallow equality check. http://www.openradar.appspot.com/28406766 - */ -@interface UIFont (MaterialSimpleEquality) - -/* - Checks simple characteristics: name, weight, pointsize, traits. - - While the actual implementation of UIFont's isEqual: is not known, it is believed that - isSimplyEqual: is more 'shallow' than isEqual:. - */ - -- (BOOL)mdc_isSimplyEqual:(UIFont*)font; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m deleted file mode 100644 index aa4bab0e..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialSimpleEquality.m +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIFont+MaterialSimpleEquality.h" - -#import "MaterialMath.h" - -@implementation UIFont (MaterialSimpleEquality) - -- (BOOL)mdc_isSimplyEqual:(UIFont *)font { - return [self.fontName isEqualToString:font.fontName] && - MDCCGFloatEqual(self.pointSize, font.pointSize) && - [[self.fontDescriptor objectForKey:UIFontDescriptorFaceAttribute] - isEqual:[font.fontDescriptor objectForKey:UIFontDescriptorFaceAttribute]]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h deleted file mode 100644 index 5f59a296..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCFontTextStyle.h" - -@interface UIFont (MaterialTypography) - -/** - Returns an instance of the font associated with the Material text style and scaled based on the - content size category. - - @param style The Material font text style for which to return a font. - @return The font associated with the specified style. - */ -+ (nonnull UIFont *)mdc_preferredFontForMaterialTextStyle:(MDCFontTextStyle)style; - -/** - Returns an instance of the font associated with the Material text style - This font is *not* scaled based on the content size category (Dynamic Type). - - @param style The Material font text style for which to return a font. - @return The font associated with the specified style. - */ -+ (nonnull UIFont *)mdc_standardFontForMaterialTextStyle:(MDCFontTextStyle)style; - -/** - Returns an new instance of the font sized according to the text-style and whether the content - size category (Dynamic Type) should be taken into account. - - @param style The Material font text style that will determine the fontSize of the new font - @param scaled Should the new font be scaled according to the content size category (Dynamic Type) - */ -- (nonnull UIFont *)mdc_fontSizedForMaterialTextStyle:(MDCFontTextStyle)style - scaledForDynamicType:(BOOL)scaled; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m deleted file mode 100644 index 6b4ae45e..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFont+MaterialTypography.m +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIFont+MaterialTypography.h" - -#import "MDCTypography.h" -#import "UIFontDescriptor+MaterialTypography.h" - -@implementation UIFont (MaterialTypography) - -+ (UIFont *)mdc_preferredFontForMaterialTextStyle:(MDCFontTextStyle)style { - // Due to the way iOS handles missing glyphs in fonts, we do not support using - // our font loader with Dynamic Type. - id fontLoader = [MDCTypography fontLoader]; - if (![fontLoader isKindOfClass:[MDCSystemFontLoader class]]) { - NSLog(@"MaterialTypography : Custom font loaders are not compatible with Dynamic Type."); - } - - UIFontDescriptor *fontDescriptor = - [UIFontDescriptor mdc_preferredFontDescriptorForMaterialTextStyle:style]; - - // Size is included in the fontDescriptor, so we pass in 0.0 in the parameter. - UIFont *font = [UIFont fontWithDescriptor:fontDescriptor size:0.0]; - - return font; -} - -+ (nonnull UIFont *)mdc_standardFontForMaterialTextStyle:(MDCFontTextStyle)style { - // Caches a font for a specific MDCFontTextStyle value - static NSCache *fontCache; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // NOTE: We assume the Font Loader will never change, so the cached fonts are never invalidated. - fontCache = [[NSCache alloc] init]; - }); - - // Due to the way iOS handles missing glyphs in fonts, we do not support using our - // font loader with standardFont. - id fontLoader = [MDCTypography fontLoader]; - if (![fontLoader isKindOfClass:[MDCSystemFontLoader class]]) { - NSLog(@"MaterialTypography : Custom font loaders are not compatible with Dynamic Type."); - } - - UIFont *font = [fontCache objectForKey:@(style)]; - if (!font) { - UIFontDescriptor *fontDescriptor = - [UIFontDescriptor mdc_standardFontDescriptorForMaterialTextStyle:style]; - - // Size is included in the fontDescriptor, so we pass in 0.0 in the parameter. - font = [UIFont fontWithDescriptor:fontDescriptor size:0.0]; - [fontCache setObject:font forKey:@(style)]; - } - - return font; -} - -- (nonnull UIFont *)mdc_fontSizedForMaterialTextStyle:(MDCFontTextStyle)style - scaledForDynamicType:(BOOL)scaled { - UIFontDescriptor *fontDescriptor; - if (scaled) { - fontDescriptor = [UIFontDescriptor mdc_preferredFontDescriptorForMaterialTextStyle:style]; - } else { - fontDescriptor = [UIFontDescriptor mdc_standardFontDescriptorForMaterialTextStyle:style]; - } - - return [self fontWithSize:fontDescriptor.pointSize]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h deleted file mode 100644 index a438f38f..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCFontTextStyle.h" - -@interface UIFontDescriptor (MaterialTypography) - -/** - Returns an instance of the font descriptor associated with the Material text style and scaled - based on the content size category. - - @param style The Material font text style for which to return a font descriptor. - @return The font descriptor associated with the specified style. - */ -+ (nonnull UIFontDescriptor *)mdc_preferredFontDescriptorForMaterialTextStyle: - (MDCFontTextStyle)style; - -/** - Returns an instance of the font descriptor associated with the Material text style. - This font descriptor is *not* scaled based on the content size category (Dynamic Type). - - @param style The Material font text style for which to return a font descriptor. - @return The font descriptor associated with the specified style. - */ -+ (nonnull UIFontDescriptor *)mdc_standardFontDescriptorForMaterialTextStyle: - (MDCFontTextStyle)style; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m deleted file mode 100644 index 7c86c4b8..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/UIFontDescriptor+MaterialTypography.m +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIFontDescriptor+MaterialTypography.h" - -#import "MaterialApplication.h" - -#import "private/MDCFontTraits.h" - -@implementation UIFontDescriptor (MaterialTypography) - -+ (nonnull UIFontDescriptor *)mdc_fontDescriptorForMaterialTextStyle:(MDCFontTextStyle)style - sizeCategory:(NSString *)sizeCategory { - // TODO(#1179): We should include our leading and tracking metrics when creating this descriptor. - MDCFontTraits *materialTraits = [MDCFontTraits traitsForTextStyle:style - sizeCategory:sizeCategory]; - - // Store the system font family name to ensure that we load the system font. - // If we do not explicitly include this UIFontDescriptorFamilyAttribute in the - // FontDescriptor the OS will default to Helvetica. On iOS 9+, the Font Family - // changes from San Francisco to San Francisco Display at point size 20. - static NSString *smallSystemFontFamilyName; - static NSString *largeSystemFontFamilyName; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - UIFont *smallSystemFont; - UIFont *largeSystemFont; - smallSystemFont = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular]; - largeSystemFont = [UIFont systemFontOfSize:20 weight:UIFontWeightRegular]; - smallSystemFontFamilyName = [smallSystemFont.familyName copy]; - largeSystemFontFamilyName = [largeSystemFont.familyName copy]; - }); - - NSDictionary *traits = @{UIFontWeightTrait : @(materialTraits.weight)}; - NSString *fontFamily = - materialTraits.pointSize < 19.5 ? smallSystemFontFamilyName : largeSystemFontFamilyName; - NSDictionary *attributes = @{ - UIFontDescriptorSizeAttribute : @(materialTraits.pointSize), - UIFontDescriptorTraitsAttribute : traits, - UIFontDescriptorFamilyAttribute : fontFamily - }; - - UIFontDescriptor *fontDescriptor = [[UIFontDescriptor alloc] initWithFontAttributes:attributes]; - - return fontDescriptor; -} - -+ (nonnull UIFontDescriptor *)mdc_preferredFontDescriptorForMaterialTextStyle: - (MDCFontTextStyle)style { - // iOS' default UIContentSizeCategory is Large. - NSString *sizeCategory = UIContentSizeCategoryLarge; - - // If we are within an application, query the preferredContentSizeCategory. - if ([UIApplication mdc_safeSharedApplication]) { - sizeCategory = [UIApplication mdc_safeSharedApplication].preferredContentSizeCategory; - } else { - sizeCategory = UIScreen.mainScreen.traitCollection.preferredContentSizeCategory; - } - - return [UIFontDescriptor mdc_fontDescriptorForMaterialTextStyle:style sizeCategory:sizeCategory]; -} - -+ (nonnull UIFontDescriptor *)mdc_standardFontDescriptorForMaterialTextStyle: - (MDCFontTextStyle)style { - // iOS' default UIContentSizeCategory is Large. - // Since we don't want to scale with Dynamic Type create the font descriptor based on that. - NSString *sizeCategory = UIContentSizeCategoryLarge; - - return [UIFontDescriptor mdc_fontDescriptorForMaterialTextStyle:style sizeCategory:sizeCategory]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h deleted file mode 100644 index 5ac7c75a..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MDCFontTextStyle.h" - -/** - Provides a means of storing defining font metrics based on size categories. - - This class is based off Apple recommendation in WWDC 2016 - 803 - Typography and Fonts @ 17:33. - */ -@interface MDCFontTraits : NSObject - -/** - The size to which the font is scaled. - - This value, in points, must be greater than 0.0. - */ -@property(nonatomic, readonly) CGFloat pointSize; - -/** - The weight of the font, specified as a font weight constant. - - For a list of possible values, see "Font Weights” in UIFontDescriptor. Avoid passing an arbitrary - floating-point number for weight, because a font might not include a variant for every weight. - */ -@property(nonatomic, readonly) CGFloat weight; - -/** - The leading value represents additional space between lines of text and is measured in points. - */ -@property(nonatomic, readonly) CGFloat leading; - -/** - The tracking value represents additional horizontal space between glyphs and is measured in points. - */ -@property(nonatomic, readonly) CGFloat tracking; - -/** - @param style MDCFontStyle of font traits being requested. - @param sizeCategory UIContentSizeCategory of the font traits being requested. - - @return Font traits that can be used to initialize a UIFont or UIFontDescriptor. - */ -+ (nonnull MDCFontTraits *)traitsForTextStyle:(MDCFontTextStyle)style - sizeCategory:(nonnull NSString *)sizeCategory; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m deleted file mode 100644 index 0598681c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCFontTraits.m +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCFontTraits.h" - -static NSDictionary *_body1Traits; -static NSDictionary *_body2Traits; -static NSDictionary *_buttonTraits; -static NSDictionary *_captionTraits; -static NSDictionary *_display1Traits; -static NSDictionary *_display2Traits; -static NSDictionary *_display3Traits; -static NSDictionary *_display4Traits; -static NSDictionary *_headlineTraits; -static NSDictionary *_subheadlineTraits; -static NSDictionary *_titleTraits; - -static NSDictionary *_styleTable; - -@interface MDCFontTraits (MaterialTypographyPrivate) - -+ (instancetype)traitsWithPointSize:(CGFloat)pointSize - weight:(CGFloat)weight - leading:(CGFloat)leading - tracking:(CGFloat)tracking; - -- (instancetype)initWithPointSize:(CGFloat)pointSize - weight:(CGFloat)weight - leading:(CGFloat)leading - tracking:(CGFloat)tracking; - -@end - -@implementation MDCFontTraits - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" -+ (void)initialize { - _body1Traits = @{ - UIContentSizeCategoryExtraSmall : [MDCFontTraits traitsWithPointSize:11 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [MDCFontTraits traitsWithPointSize:12 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [MDCFontTraits traitsWithPointSize:13 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [MDCFontTraits traitsWithPointSize:14 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [MDCFontTraits traitsWithPointSize:16 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : [MDCFontTraits traitsWithPointSize:18 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [MDCFontTraits traitsWithPointSize:20 weight:UIFontWeightRegular leading:0.0 tracking:0.0], - UIContentSizeCategoryAccessibilityMedium : - [MDCFontTraits traitsWithPointSize:25 weight:UIFontWeightRegular leading:0.0 tracking:0.0], - UIContentSizeCategoryAccessibilityLarge : [MDCFontTraits traitsWithPointSize:30 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryAccessibilityExtraLarge : - [MDCFontTraits traitsWithPointSize:37 weight:UIFontWeightRegular leading:0.0 tracking:0.0], - UIContentSizeCategoryAccessibilityExtraExtraLarge : - [MDCFontTraits traitsWithPointSize:44 weight:UIFontWeightRegular leading:0.0 tracking:0.0], - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : - [MDCFontTraits traitsWithPointSize:52 weight:UIFontWeightRegular leading:0.0 tracking:0.0], - }; - - _body2Traits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:11 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:12 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:13 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:14 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:16 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:18 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:20 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryAccessibilityMedium : - [[MDCFontTraits alloc] initWithPointSize:25 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryAccessibilityLarge : - [[MDCFontTraits alloc] initWithPointSize:30 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryAccessibilityExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:37 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryAccessibilityExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:44 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:52 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - }; - - _buttonTraits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:11 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:12 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:13 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:14 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:16 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:18 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:20 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - }; - - _captionTraits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:11 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:11 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:11 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:12 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:14 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:16 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:18 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - }; - - _display1Traits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:28 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:30 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:32 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:34 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:36 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:38 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:40 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - }; - - _display2Traits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:39 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:41 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:43 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:45 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:47 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:49 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:51 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - }; - - _display3Traits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:50 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:52 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:54 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:56 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:58 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:60 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:62 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - }; - - _display4Traits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:100 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:104 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:108 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:112 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:116 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:120 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:124 - weight:UIFontWeightLight - leading:0.0 - tracking:0.0], - }; - - _headlineTraits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:21 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:22 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:23 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:24 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:26 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:28 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:30 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - }; - - _subheadlineTraits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:13 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:14 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:15 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:16 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:18 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:20 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:22 - weight:UIFontWeightRegular - leading:0.0 - tracking:0.0], - }; - - _titleTraits = @{ - UIContentSizeCategoryExtraSmall : [[MDCFontTraits alloc] initWithPointSize:17 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategorySmall : [[MDCFontTraits alloc] initWithPointSize:18 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryMedium : [[MDCFontTraits alloc] initWithPointSize:19 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryLarge : [[MDCFontTraits alloc] initWithPointSize:20 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraLarge : [[MDCFontTraits alloc] initWithPointSize:22 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:24 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - UIContentSizeCategoryExtraExtraExtraLarge : - [[MDCFontTraits alloc] initWithPointSize:26 - weight:UIFontWeightMedium - leading:0.0 - tracking:0.0], - }; - - _styleTable = @{ - @(MDCFontTextStyleBody1) : _body1Traits, - @(MDCFontTextStyleBody2) : _body2Traits, - @(MDCFontTextStyleButton) : _buttonTraits, - @(MDCFontTextStyleCaption) : _captionTraits, - @(MDCFontTextStyleDisplay1) : _display1Traits, - @(MDCFontTextStyleDisplay2) : _display2Traits, - @(MDCFontTextStyleDisplay3) : _display3Traits, - @(MDCFontTextStyleDisplay4) : _display4Traits, - @(MDCFontTextStyleHeadline) : _headlineTraits, - @(MDCFontTextStyleSubheadline) : _subheadlineTraits, - @(MDCFontTextStyleTitle) : _titleTraits - }; -} -#pragma clang diagnostic pop - -+ (instancetype)traitsWithPointSize:(CGFloat)pointSize - weight:(CGFloat)weight - leading:(CGFloat)leading - tracking:(CGFloat)tracking { - return [[MDCFontTraits alloc] initWithPointSize:pointSize - weight:weight - leading:leading - tracking:tracking]; -} - -- (instancetype)initWithPointSize:(CGFloat)pointSize - weight:(CGFloat)weight - leading:(CGFloat)leading - tracking:(CGFloat)tracking { - self = [super init]; - if (self) { - _pointSize = pointSize; - _weight = weight; - _leading = leading; - _tracking = tracking; - } - - return self; -} - -+ (MDCFontTraits *)traitsForTextStyle:(MDCFontTextStyle)style - sizeCategory:(NSString *)sizeCategory { - NSDictionary *traitsTable = _styleTable[@(style)]; - NSCAssert(traitsTable, @"traitsTable cannot be nil. Is style valid?"); - - MDCFontTraits *traits; - if (traitsTable) { - if (sizeCategory) { - traits = traitsTable[sizeCategory]; - } - - // If you have queried the table for a sizeCategory that doesn't exist, we will return the - // traits for XXXL. This handles the case where the values are requested for one of the - // accessibility size categories beyond XXXL such as - // UIContentSizeCategoryAccessibilityExtraLarge. Accessbility size categories are only - // defined for the Body Font Style. - if (traits == nil) { - traits = traitsTable[UIContentSizeCategoryExtraExtraExtraLarge]; - } - } - - return traits; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h deleted file mode 100644 index 6fa1d139..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -UIContentSizeCategory GetCurrentSizeCategory(void); diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m deleted file mode 100644 index 4210d676..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/MDCTypographyUtilities.m +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCTypographyUtilities.h" - -#import "MaterialApplication.h" - -/** - @return Device's current UIContentSizeCategory or UIContentSizeCategoryLarge - if we are unable to query the device due to being in an extension. - */ -UIContentSizeCategory GetCurrentSizeCategory(void) { - return UIScreen.mainScreen.traitCollection.preferredContentSizeCategory; -} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h deleted file mode 100644 index 7d3c1860..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@interface UIFont (MaterialTypographyPrivate) - -/** - Returns the weight of the font. - - @return A value between -1.0 (very thin) to 1.0 (very thick). - - Regular weight is 0.0. - */ -- (CGFloat)mdc_weight; - -/** - Returns the slant of the font. - - @return more than 0 when italic and 0 when not italic. - - Regular slant is 0.0. - */ -- (CGFloat)mdc_slant; - -/** - Returns an extended description of the font including name, pointsize and weight. - */ -- (nonnull NSString *)mdc_extendedDescription; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m deleted file mode 100644 index f29c7517..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/Typography/src/private/UIFont+MaterialTypographyPrivate.m +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIFont+MaterialTypographyPrivate.h" - -#import "UIFont+MaterialScalable.h" - -@implementation UIFont (MaterialTypographyPrivate) - -/* - Returns a string indicating the weight of the font. These weights were added in iOS 8.2. - */ -+ (NSString *)mdc_fontWeightDescription:(CGFloat)weight { - // The UIFontWeight enumeration was added in iOS 8.2 - NSString *description = [NSString stringWithFormat:@"(%.3f)", weight]; -#if defined(__IPHONE_8_2) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" -#pragma clang diagnostic ignored "-Wtautological-pointer-compare" - if (&UIFontWeightMedium != NULL) { - if (weight == UIFontWeightUltraLight) { - return @"UltraLight"; - } else if (weight == UIFontWeightThin) { - return @"Thin"; - } else if (weight == UIFontWeightLight) { - return @"Light"; - } else if (weight == UIFontWeightRegular) { - return @"Regular"; - } else if (weight == UIFontWeightMedium) { - return @"Medium"; - } else if (weight == UIFontWeightSemibold) { - return @"Semibold"; - } else if (weight == UIFontWeightBold) { - return @"Bold"; - } else if (weight == UIFontWeightHeavy) { - return @"Heavy"; - } else if (weight == UIFontWeightBlack) { - return @"Black"; - } else { - return description; - } - } else { - return description; - } -#pragma clang diagnostic pop -#else - return description; -#endif -} - -- (CGFloat)mdc_weight { - // The default font weight is UIFontWeightRegular, which is 0.0. - CGFloat weight = 0.0; - - NSDictionary *fontTraits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; - if (fontTraits) { - NSNumber *weightNumber = fontTraits[UIFontWeightTrait]; - if (weightNumber != nil) { - weight = [weightNumber floatValue]; - } - } - - return weight; -} - -- (CGFloat)mdc_slant { - CGFloat slant = 0.0; - - NSDictionary *fontTraits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; - if (fontTraits) { - NSNumber *slantNumber = fontTraits[UIFontSlantTrait]; - if (slantNumber != nil) { - slant = [slantNumber floatValue]; - } - } - - return slant; -} - -- (NSString *)mdc_weightString { - CGFloat weight = [self mdc_weight]; - NSString *weightString = [UIFont mdc_fontWeightDescription:weight]; - - return weightString; -} - -- (NSString *)mdc_extendedDescription { - NSMutableString *extendedDescription = [[super description] mutableCopy]; - [extendedDescription appendFormat:@"%@ : ", self.fontName]; - [extendedDescription appendFormat:@"%@ : ", self.familyName]; - [extendedDescription appendFormat:@"%.1f pt : ", self.pointSize]; - [extendedDescription appendFormat:@"%@ : ", [self mdc_weightString]]; - if (self.mdc_scalingCurve) { - [extendedDescription appendString:@"+ScalingCurve"]; - } else { - [extendedDescription appendString:@"NoScalingCurve"]; - } - - return extendedDescription; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h deleted file mode 100644 index 346a5d70..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/MaterialApplication.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#import "UIApplication+MDCAppExtensions.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h deleted file mode 100644 index 75ddca1a..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - UIApplication extension for working with sharedApplication inside of app extensions. - */ -@interface UIApplication (MDCAppExtensions) - -/** - Returns sharedApplication if it is available otherwise returns nil. - - This is a wrapper around sharedApplication which is safe to compile and use in app extensions. - */ -+ (UIApplication *)mdc_safeSharedApplication; - -/** - Returns YES if called inside an application extension otherwise returns NO. - */ -+ (BOOL)mdc_isAppExtension; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m deleted file mode 100644 index a6482278..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Application/src/UIApplication+MDCAppExtensions.m +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIApplication+MDCAppExtensions.h" - -@implementation UIApplication (MDCAppExtensions) - -+ (UIApplication *)mdc_safeSharedApplication { - static UIApplication *application; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (![self mdc_isAppExtension]) { - // We can't call sharedApplication directly or else this won't build for app extensions. - application = [[UIApplication class] performSelector:@selector(sharedApplication)]; - } - }); - return application; -} - -+ (BOOL)mdc_isAppExtension { - static BOOL isAppExtension; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - isAppExtension = - [[[NSBundle mainBundle] executablePath] rangeOfString:@".appex/"].location != NSNotFound; - }); - return isAppExtension; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h deleted file mode 100644 index eb5514a4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/MaterialColor.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIColor+MaterialBlending.h" -#import "UIColor+MaterialDynamic.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h deleted file mode 100644 index 4c458665..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@interface UIColor (MaterialBlending) - -/** - Blending a color over a background color using Alpha compositing technique. - More info about Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing - - @param color UIColor value that sits on top. - @param backgroundColor UIColor on the background. - */ -+ (nonnull UIColor *)mdc_blendColor:(nonnull UIColor *)color - withBackgroundColor:(nonnull UIColor *)backgroundColor; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m deleted file mode 100644 index 1cfa186c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialBlending.m +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIColor+MaterialBlending.h" - -/** - Helper method to blend a color channel with a background color channel using alpha composition. - More info about Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing - - @params value is the value of color channel - @params bValue is the value of background color channel - @params alpha is the alpha of color channel - @params bAlpha is the alpha of background color channel - */ - -static CGFloat blendColorChannel(CGFloat value, CGFloat bValue, CGFloat alpha, CGFloat bAlpha) { - return ((1 - alpha) * bValue * bAlpha + alpha * value) / (alpha + bAlpha * (1 - alpha)); -} - -@implementation UIColor (MaterialBlending) - -+ (UIColor *)mdc_blendColor:(UIColor *)color withBackgroundColor:(UIColor *)backgroundColor { - CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; - [color getRed:&red green:&green blue:&blue alpha:&alpha]; - CGFloat bRed = 0.0, bGreen = 0.0, bBlue = 0.0, bAlpha = 0.0; - [backgroundColor getRed:&bRed green:&bGreen blue:&bBlue alpha:&bAlpha]; - - return [UIColor colorWithRed:blendColorChannel(red, bRed, alpha, bAlpha) - green:blendColorChannel(green, bGreen, alpha, bAlpha) - blue:blendColorChannel(blue, bBlue, alpha, bAlpha) - alpha:alpha + bAlpha * (1 - alpha)]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h deleted file mode 100644 index 54c39184..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@interface UIColor (MaterialDynamic) - -/// Returns a color object that picks its value from given color objects dynamically -/// based on currently active traits. For pre iOS 13, this method returns the default -/// color object. -/// -/// @param darkColor A color object returned when @c userInterfaceStyle is @c -/// UIUserInterfaceStyleDark based on currently active traits. -/// @param defaultColor A default color object. -+ (nonnull UIColor *)colorWithUserInterfaceStyleDarkColor:(nonnull UIColor *)darkColor - defaultColor:(nonnull UIColor *)defaultColor; - -/** - Returns a dynamic color object that resolves to high contrast color when current - @c accessibilityContrast is @c UIAccessibilityContrastHigh, and to normal contrast color - when @c accessibilityContrast is @c UIAccessibilityContrastNormal. - - The high contrast color and normal contrast color can be dynamic color objects. This method can - be used together with @c colorWithUserInterfaceStyleDarkColor:defaultColor:. - - For pre iOS 13, this method always returns the normal contrast color. - - @param highContrastColor A color object for high contrast color. - @param normalContrastColor A default color object for normal contrast color. - */ -+ (nonnull UIColor *)colorWithAccessibilityContrastHigh:(nonnull UIColor *)highContrastColor - normal:(nonnull UIColor *)normalContrastColor; - -/** - Returns the version of the current color that takes the specified traits into account. - - @note On pre-iOS 13 the orginal color is returned. - - @param traitCollection The traits to use when resolving the color information. - @return The version of the color to display for the specified traits. - */ -- (nonnull UIColor *)mdc_resolvedColorWithTraitCollection: - (nonnull UITraitCollection *)traitCollection; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m deleted file mode 100644 index d3d71190..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Color/src/UIColor+MaterialDynamic.m +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "UIColor+MaterialDynamic.h" - -#import "MaterialAvailability.h" - -@implementation UIColor (MaterialDynamic) - -+ (UIColor *)colorWithUserInterfaceStyleDarkColor:(UIColor *)darkColor - defaultColor:(UIColor *)defaultColor { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - return [UIColor - colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return darkColor; - } else { - return defaultColor; - } - }]; - } else { - return defaultColor; - } -#else - return defaultColor; -#endif // MDC_AVAILABLE_SDK_IOS(13_0) -} - -+ (UIColor *)colorWithAccessibilityContrastHigh:(UIColor *)highContrastColor - normal:(UIColor *)normalContrastColor { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - return [UIColor - colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.accessibilityContrast == UIAccessibilityContrastHigh) { - return highContrastColor; - } else { - return normalContrastColor; - } - }]; - } else { - return normalContrastColor; - } -#else - return normalContrastColor; -#endif // MDC_AVAILABLE_SDK_IOS(13_0) -} - -- (UIColor *)mdc_resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection { -#if MDC_AVAILABLE_SDK_IOS(13_0) - if (@available(iOS 13.0, *)) { - return [self resolvedColorWithTraitCollection:traitCollection]; - } else { - return self; - } -#else - return self; -#endif // MDC_AVAILABLE_SDK_IOS(13_0) -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h deleted file mode 100644 index 818207ba..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -#import "MaterialIcons.h" - -// This file was automatically generated by running ./scripts/sync_icons.sh -// Do not modify directly. - -@interface MDCIcons (ic_check_circle) - -/* - Returns the image for the ic_check_circle image contained in - MaterialIcons_ic_check_circle.bundle. - */ -+ (nullable UIImage *)imageFor_ic_check_circle; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m deleted file mode 100644 index 77176bc9..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file was automatically generated by running ./scripts/sync_icons.sh -// Do not modify directly. - -#import "MaterialIcons+ic_check_circle.h" - -static NSString *const kBundleName = @"MaterialIcons_ic_check_circle"; -static NSString *const kIconName = @"ic_check_circle"; - -// Export a nonsense symbol to suppress a libtool warning when this is linked alone in a static lib. -__attribute__((visibility("default"))) char MDCIconsExportToSuppressLibToolWarning_ic_check_circle = - 0; - -@implementation MDCIcons (ic_check_circle) - -+ (nullable UIImage *)imageFor_ic_check_circle { - NSBundle *bundle = [self bundleNamed:kBundleName]; - return [UIImage imageNamed:kIconName inBundle:bundle compatibleWithTraitCollection:nil]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json deleted file mode 100644 index 2d92bd53..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json deleted file mode 100644 index 1143a92d..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "ic_check_circle.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ic_check_circle@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ic_check_circle@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle.png deleted file mode 100644 index a2caa18db2b2f00f890d119183e78093314e7a64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmV+f0sH=mP)ayb&5+IG8{X5M=Wfq|%hM?=*;Bc- zRdy^W5Hsb1x}BNelK4Qu)+BmN4-)z_vHXh=V+;zHQ2*qG72-s;V0l;PKM->Z_K$VM z)`DtpLtPv#xFN)A6MjNnT>D@JP5Sn93+$35U#>0=7VOX<@pQ4VV1WjSr;C{dh4Ucs z^hyR}f*x!{$NH}RP7f(gcq8F5=Hz@I5kqFqmLu1;0~=;!p3EOL#1#p?D_&0k0000< KMNUMnLSTXkp==BQ diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@2x.png deleted file mode 100644 index 86bf38e98fd6cd4917a9a05f1627b4cd00bc95d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 493 zcmVM}5-5a(K1E3IIb76@&*2A@3Ka?2H4rk8_6a0`E`;ve`T*h!RCH0s zX}8X7{@uyIb^g5{ZiJlQMmOg$&b_i1ybw{R$1^iha&l5;JkjNvh+y7w%MvNsf*UUI z<_(YJh(0o6PVu0{AH;GinmBRD7sPQfRV;bMH^gIOYFKcn>-B`qQ+!4O_J9s8ByjKa zgZbA>`kmqCbHWLwJbLP11O3hS`|?|xd+RAA)||7I+aP`M8HSh;v2@n|*s2%iR4}Lq z>x;X>ptnQ4(81t&hk9Xz!3?3ek4zN(k*XJ_7^FyH?|~yGo_Zm{pm+y90GJ7Dy^!gF zK9nSWYI;48B6`9>FC_X)HWP~8_9Z_dYQj#h9MdkMCS3GF8-r^^O}OZVOAI0wdcr~f ziwc;eA(g)CGAuU6=+00000NkvXXu0mjf+)>#> diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets/ic_check_circle.imageset/ic_check_circle@3x.png deleted file mode 100644 index 1c2ddcd5f3474304cb73cf39be7d6f5deff012b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 709 zcmV;$0y_PPP)E9`6SZ1GRcK*-cVL!j)!@?JaXJ)yvnwpx@j4@A+V@j0q@hMZ{m>lzr z(oTXh%uv8b>a4;6)7-(iA-1_jELUZVEY1vbh%~SQgQ)A_3ld}Nbff4QS4hlVGJ%R# zJ|Ie4CPhr0wa@A~C3<;|1ojb2!%lJlULb*Gv2y4r2Vfy^vX6x!hdBU?fzttuww>m{ zQs8_OgACUR<_J|h>Y8z#Q?n`RDkPjE>`_M=Kj9qVH*JI(bD0ym5kk$Ae2y?js2mcq zD`z>vDxr=;e)5us{B)Ef91*I7P`d>H9&qj?M<^1ijMVHE|MYS0AV;_$RPCi-.h header and calling - [MDCIcons pathFor_] to get the icon's file system path or calling - [MDCIcons imageFor_] to get a cached image. - */ -@interface MDCIcons (BundleLoader) - -/** Returns a disk path for an icon contained within a bundle of a given name. */ -+ (nonnull NSString *)pathForIconName:(nonnull NSString *)iconName - withBundleName:(nonnull NSString *)bundleName; - -+ (nullable NSBundle *)bundleNamed:(nonnull NSString *)bundleName; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h deleted file mode 100644 index c2e9da62..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -/** - The MDCIcons class is designed to be extended by adding individual icon extensions to a project. - - Individual icons can be accessed by importing their MaterialIcons+.h header and calling - [MDCIcons pathFor_] to get the icon's file system path or calling - [MDCIcons imageFor_] to get a cached image. - */ -@interface MDCIcons : NSObject - -// This object is not intended to be instantiated. -- (instancetype)init NS_UNAVAILABLE; - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m deleted file mode 100644 index aa8179d4..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MDCIcons.m +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCIcons.h" - -#import "MDCIcons+BundleLoader.h" - -@implementation MDCIcons - -+ (nullable NSBundle *)bundleNamed:(nonnull NSString *)bundleName { - static NSCache *bundleCache; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - bundleCache = [[NSCache alloc] init]; - }); - - NSBundle *bundle = [bundleCache objectForKey:bundleName]; - if (!bundle) { - NSBundle *baseBundle = [NSBundle bundleForClass:[MDCIcons class]]; - NSString *bundlePath = [baseBundle pathForResource:bundleName ofType:@"bundle"]; - bundle = [NSBundle bundleWithPath:bundlePath]; - if (bundle) { - [bundleCache setObject:bundle forKey:bundleName]; - } - } - return bundle; -} - -+ (nonnull NSString *)pathForIconName:(nonnull NSString *)iconName - withBundleName:(nonnull NSString *)bundleName { - NSBundle *bundle = [self bundleNamed:bundleName]; - NSAssert(bundle, @"Missing bundle %@ containing icon %@.", bundleName, iconName); - return [bundle pathForResource:iconName ofType:@"png"]; -} - -@end diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h deleted file mode 100644 index f527b7b0..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Icons/src/MaterialIcons.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCIcons+BundleLoader.h" -#import "MDCIcons.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h deleted file mode 100644 index b12f0c65..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MDCMath.h +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import -#import -#import - -__deprecated_msg("Use sin instead.") static inline CGFloat MDCSin(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return sin(value); -#else - return sinf(value); -#endif -} - -__deprecated_msg("Use cos instead.") static inline CGFloat MDCCos(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return cos(value); -#else - return cosf(value); -#endif -} - -__deprecated_msg("Use atan2 instead.") static inline CGFloat MDCAtan2(CGFloat y, CGFloat x) { -#if CGFLOAT_IS_DOUBLE - return atan2(y, x); -#else - return atan2f(y, x); -#endif -} - -__deprecated_msg("Use ceil instead.") static inline CGFloat MDCCeil(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return ceil(value); -#else - return ceilf(value); -#endif -} - -__deprecated_msg("Use fabs instead.") static inline CGFloat MDCFabs(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return fabs(value); -#else - return fabsf(value); -#endif -} - -static inline CGFloat MDCDegreesToRadians(CGFloat degrees) { -#if CGFLOAT_IS_DOUBLE - return degrees * (CGFloat)M_PI / 180.0; -#else - return degrees * (CGFloat)M_PI / 180; -#endif -} - -static inline BOOL MDCCGFloatEqual(CGFloat a, CGFloat b) { - const CGFloat constantK = 3; -#if CGFLOAT_IS_DOUBLE - const CGFloat epsilon = DBL_EPSILON; - const CGFloat min = DBL_MIN; -#else - const CGFloat epsilon = FLT_EPSILON; - const CGFloat min = FLT_MIN; -#endif - return (fabs(a - b) < constantK * epsilon * fabs(a + b) || fabs(a - b) < min); -} - -__deprecated_msg("Use floor instead.") static inline CGFloat MDCFloor(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return floor(value); -#else - return floorf(value); -#endif -} - -__deprecated_msg("Use hypot instead.") static inline CGFloat MDCHypot(CGFloat x, CGFloat y) { -#if CGFLOAT_IS_DOUBLE - return hypot(x, y); -#else - return hypotf(x, y); -#endif -} - -// Checks whether the provided floating point number is exactly zero. -static inline BOOL MDCCGFloatIsExactlyZero(CGFloat value) { - return (value == 0); -} - -__deprecated_msg("Use pow instead.") static inline CGFloat MDCPow(CGFloat value, CGFloat power) { -#if CGFLOAT_IS_DOUBLE - return pow(value, power); -#else - return powf(value, power); -#endif -} - -__deprecated_msg("Use rint instead.") static inline CGFloat MDCRint(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return rint(value); -#else - return rintf(value); -#endif -} - -__deprecated_msg("Use round instead.") static inline CGFloat MDCRound(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return round(value); -#else - return roundf(value); -#endif -} - -__deprecated_msg("Use sqrt instead.") static inline CGFloat MDCSqrt(CGFloat value) { -#if CGFLOAT_IS_DOUBLE - return sqrt(value); -#else - return sqrtf(value); -#endif -} - -/** - Round the given value to ceiling with provided scale factor. - If @c scale is zero, then the rounded value will be zero. - - @param value The value to round - @param scale The scale factor - @return The ceiling value calculated using the provided scale factor - */ -static inline CGFloat MDCCeilScaled(CGFloat value, CGFloat scale) { - if (MDCCGFloatEqual(scale, 0)) { - return 0; - } - - return ceil(value * scale) / scale; -} - -/** - Round the given value to floor with provided scale factor. - If @c scale is zero, then the rounded value will be zero. - - @param value The value to round - @param scale The scale factor - @return The floor value calculated using the provided scale factor - */ -static inline CGFloat MDCFloorScaled(CGFloat value, CGFloat scale) { - if (MDCCGFloatEqual(scale, 0)) { - return 0; - } - - return floor(value * scale) / scale; -} - -/** - Expand `rect' to the smallest standardized rect containing it with pixel-aligned origin and size. - If @c scale is zero, then a scale of 1 will be used instead. - - @param rect the rectangle to align. - @param scale the scale factor to use for pixel alignment. - - @return the input rectangle aligned to the nearest pixels using the provided scale factor. - - @see CGRectIntegral - */ -static inline CGRect MDCRectAlignToScale(CGRect rect, CGFloat scale) { - if (CGRectIsNull(rect)) { - return CGRectNull; - } - if (MDCCGFloatEqual(scale, 0)) { - scale = 1; - } - - if (MDCCGFloatEqual(scale, 1)) { - return CGRectIntegral(rect); - } - - CGPoint originalMinimumPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)); - CGPoint newOrigin = CGPointMake(floor(originalMinimumPoint.x * scale) / scale, - floor(originalMinimumPoint.y * scale) / scale); - CGSize adjustWidthHeight = - CGSizeMake(originalMinimumPoint.x - newOrigin.x, originalMinimumPoint.y - newOrigin.y); - return CGRectMake(newOrigin.x, newOrigin.y, - ceil((CGRectGetWidth(rect) + adjustWidthHeight.width) * scale) / scale, - ceil((CGRectGetHeight(rect) + adjustWidthHeight.height) * scale) / scale); -} - -static inline CGPoint MDCPointRoundWithScale(CGPoint point, CGFloat scale) { - if (MDCCGFloatEqual(scale, 0)) { - return CGPointZero; - } - - return CGPointMake(round(point.x * scale) / scale, round(point.y * scale) / scale); -} - -/** - Expand `size' to the closest larger pixel-aligned value. - If @c scale is zero, then a CGSizeZero will be returned. - - @param size the size to align. - @param scale the scale factor to use for pixel alignment. - - @return the size aligned to the closest larger pixel-aligned value using the provided scale factor. - */ -static inline CGSize MDCSizeCeilWithScale(CGSize size, CGFloat scale) { - if (MDCCGFloatEqual(scale, 0)) { - return CGSizeZero; - } - - return CGSizeMake(ceil(size.width * scale) / scale, ceil(size.height * scale) / scale); -} - -/** - Align the centerPoint of a view so that its origin is pixel-aligned to the nearest pixel. - Returns @c CGRectZero if @c scale is zero or @c bounds is @c CGRectNull. - - @param center the unaligned center of the view. - @param bounds the bounds of the view. - @param scale the native scaling factor for pixel alignment. - - @return the center point of the view such that its origin will be pixel-aligned. - */ -static inline CGPoint MDCRoundCenterWithBoundsAndScale(CGPoint center, - CGRect bounds, - CGFloat scale) { - if (MDCCGFloatEqual(scale, 0) || CGRectIsNull(bounds)) { - return CGPointZero; - } - - CGFloat halfWidth = CGRectGetWidth(bounds) / 2; - CGFloat halfHeight = CGRectGetHeight(bounds) / 2; - CGPoint origin = CGPointMake(center.x - halfWidth, center.y - halfHeight); - origin = MDCPointRoundWithScale(origin, scale); - return CGPointMake(origin.x + halfWidth, origin.y + halfHeight); -} - -/// Compare two edge insets using MDCCGFloatEqual. -/// @param insets1 An edge inset to compare with insets2 -/// @param insets2 An edge inset to compare with insets1 -static inline BOOL MDCEdgeInsetsEqualToEdgeInsets(UIEdgeInsets insets1, UIEdgeInsets insets2) { - BOOL topEqual = MDCCGFloatEqual(insets1.top, insets2.top); - BOOL leftEqual = MDCCGFloatEqual(insets1.left, insets2.left); - BOOL bottomEqual = MDCCGFloatEqual(insets1.bottom, insets2.bottom); - BOOL rightEqual = MDCCGFloatEqual(insets1.right, insets2.right); - return topEqual && leftEqual && bottomEqual && rightEqual; -} diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h deleted file mode 100644 index 94b26d64..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMath.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "MDCMath.h" diff --git a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m b/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m deleted file mode 100644 index 75cef75c..00000000 --- a/test/fixtures/cocoapods/Pods/MaterialComponents/components/private/Math/src/MaterialMathDummy.m +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-present the Material Components for iOS authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - This file exists to keep pod lib lint passing - */ diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index 3d8bd023..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,3465 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXBuildFile section */ - 002C20BF2F798D2C64290D641171C7F3 /* MaterialShapeLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 90A761C8F692CCA4FDA5BFC5DEBACE74 /* MaterialShapeLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 004D58D25F2EB03B40ADB43039641BC0 /* AnimationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708C0285547318992A3989A6415E5D6B /* AnimationContext.swift */; }; - 023569941EF2F377A6F87A87062B479A /* MDFColorCalculations.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CCE660C00123EB989E5679091A134A /* MDFColorCalculations.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 02621C4B82398D0657F474E21493A3A2 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = C27430E91F40A49F302EA2C974453696 /* HTTPMethod.swift */; }; - 02DB462B121245593CE653B9B377F970 /* Protected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D70DCDE236059947C0BF795B512C84 /* Protected.swift */; }; - 02E41CCFF8E90ACE9B3F5267F6FE3DB7 /* MaterialCards.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DCF5983D0F4C2FB0E19A0D320B789B /* MaterialCards.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 03219B99BEEC9AB447188784DFDF4E19 /* UIImage+MaterialRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE016D373F57DCED6E20186B20A6596 /* UIImage+MaterialRTL.m */; }; - 048E7F185D045CEAB317FF30D739E39C /* MDCInkLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 92BF3EBBBF215FDB97014A1C7F674282 /* MDCInkLayer.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 04FBE0F7CABE3A8D6913CC677B5557DB /* MDCLegacyInkLayer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 16900192B6F1F880A2BEEE4A15228EE3 /* MDCLegacyInkLayer+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 0505586ED69AFD180618E639E5526C63 /* AnimationViewInitializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6966E76E2E05FEC74DB6972B4F38FCC /* AnimationViewInitializers.swift */; }; - 059F26929B3EB05770116463CC9DE9BD /* PathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC6506BF9A73061A58868C0D592B9B1 /* PathNode.swift */; }; - 06894D1A68123F284D70772314BBC171 /* FillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A07731940B92711A5CA2D9CF2C7DA38F /* FillRenderer.swift */; }; - 0711A9391B98D049C553FEF13D38C1AD /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6557B599662265675CB317D7A3AA35 /* Animation.swift */; }; - 0716AAD8964E0F60518EF92178614471 /* TextAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75A55C9E6101E233016AB66DD634B926 /* TextAnimator.swift */; }; - 0828664E6967E49C11D8F03D40827F40 /* MDCShadowElevations.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BA153CC755CD4895140457FB4274997 /* MDCShadowElevations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 092666672EA5B706BA5DAEAE9B49444F /* NodeProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55C5A1F93EFADC2096EE6DC9303B38A8 /* NodeProperty.swift */; }; - 0A4178C93B7F1DF931BF92BC4D971841 /* Vectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = A422591030AC5B802703CAAFA3536ED3 /* Vectors.swift */; }; - 0B399DCF32F8FE4F09B03B6E7B65E0D1 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1742EDF4DDCBB582B88771AEB1201E1A /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0B6124DD33313BE9E4A71126F8F3DB75 /* UIFont+MaterialScalable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE15CF0E1981C1CA722C1B5121C088C /* UIFont+MaterialScalable.m */; }; - 0C7FEE5AB0CD94782BD549DCF9974205 /* MDFImageCalculations.m in Sources */ = {isa = PBXBuildFile; fileRef = 408FEF5380BBEF2A9A4EF9878452691D /* MDFImageCalculations.m */; }; - 0D9E44B0B2AC6D75ACB5D1D1B8EB6090 /* MDCFloatingButton+Animation.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F1688277322547EA5D6CCDF2C94C1F /* MDCFloatingButton+Animation.m */; }; - 0E5AF92BC5AD820D750CDDE0DDC887D2 /* Pods-ios-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 100C94E08EE07B09E19F115713E5B184 /* Pods-ios-dummy.m */; }; - 0E97277DFF7EA15585CCD7FD4F881799 /* KeyedDecodingContainerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111FAD1AC980B2D67B27016C087B61ED /* KeyedDecodingContainerExtensions.swift */; }; - 0EDBF34C06554E9537C4DFCBAF24433E /* MDCRectangleShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 909F969A28FE09D5039EF1A274202B6D /* MDCRectangleShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0F1B88139EBAE43C4D04C4D238AE8F51 /* MDCLegacyInkLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 600ED7A50816D1A3222193C97DEA9BC3 /* MDCLegacyInkLayer.m */; }; - 0F7ADF590B5BBABFEE77D9E562058102 /* MDCRippleTouchController.h in Headers */ = {isa = PBXBuildFile; fileRef = 62A2F94AFB07146E8A9A5DC4985F19BA /* MDCRippleTouchController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0FF000CEEA87B583977819DE456F949B /* UIView+MDCTimingFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = E69B2A401E928B1CBB9CD4B02BEE853D /* UIView+MDCTimingFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1062EDD2C5C3B8D7A91592F639600D74 /* MDFInternationalization.h in Headers */ = {isa = PBXBuildFile; fileRef = B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */; }; - 10F1A59D76699A85A3D3F9CE14D68340 /* MDCTriangleEdgeTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = AC80A65D7F1D3E258FEC205047C3EB0A /* MDCTriangleEdgeTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 114CD9D207171C75415C5B60E0609675 /* Pods-iosTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CEC781B13B51E108C5658001DCCCD2A /* Pods-iosTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 11E325AD2BE51DA7C48EB54B7737B4BF /* UIColor+MaterialBlending.m in Sources */ = {isa = PBXBuildFile; fileRef = 1495FD5410C5BA9748915AE95726D978 /* UIColor+MaterialBlending.m */; }; - 12017489E0E497EC8A12E9418643AA6D /* UIFont+MaterialTypographyPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = FE68F0EF6188796115A2408E20255E7C /* UIFont+MaterialTypographyPrivate.m */; }; - 124EEAE22B8077371D55FDB1AC4F4D30 /* NSString+MaterialBidi.m in Sources */ = {isa = PBXBuildFile; fileRef = BC918096888E65550CADD29286F70E6B /* NSString+MaterialBidi.m */; }; - 12AC4439EB9B96B847691B34BB75656D /* AnimationTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5DF95E38B16446E73BCFAC6F53DA2E /* AnimationTime.swift */; }; - 1378724980581463BB5A2AD8A3B39BC3 /* MDCButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F90CBF2D1339BB6CDDAFF78CC3B59761 /* MDCButton.m */; }; - 138A6746D98577BC9AEFBA7E0DBCE9A7 /* MDCRectangleShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FB83587411063DC53155C1A2C082DD3 /* MDCRectangleShapeGenerator.m */; }; - 13E62623092B680C6A5C349D48B8A4FD /* CachedResponseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748CA1EDB7765D88F678941AA746AA9E /* CachedResponseHandler.swift */; }; - 1773084DECF68CADD45567FBEC56036D /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DF304359AEBCFADD6BBD420523B223F /* Alamofire-dummy.m */; }; - 17CEA056B7594689546EAB74E595D060 /* MDCTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = 5264280FEB93B40117162EB11A934890 /* MDCTypography.m */; }; - 185E3D77C15AC7CE8A4870A909237FCA /* AnimationCacheProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6E10A41E382E72F54FC92EF85F00963 /* AnimationCacheProvider.swift */; }; - 1899682FBADFB9FAE8EBCF416FD2C577 /* PointValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D52B7A10D74C906F9C84CC976B1B2D /* PointValueProvider.swift */; }; - 192B25A167B62AA5C7B878CE91F3582E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - 198CE4B8F3B6704CB4AD916821DC22A1 /* ReplyFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F40483DF444914DE771891BCED1F593 /* ReplyFeedbackGenerator.swift */; }; - 1A3B96F4D5840C14E50F8320D2E81542 /* MDCRippleViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A71810DBABD1DE3268F0A777230A85A2 /* MDCRippleViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1A7D0E6F5105EC32427DBB1983EC27BE /* BezierPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEB21337FEDA85FD4CE4B5534D436F9 /* BezierPath.swift */; }; - 1BA653A4F6BE2F4443363A09C3E99628 /* MDCInkTouchController.m in Sources */ = {isa = PBXBuildFile; fileRef = 02F95682318FB0084CD0FAE9E698A337 /* MDCInkTouchController.m */; }; - 1C3510C6893F019ABE94367A4EE9D07E /* MDCShadow.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B3D295F42CEAEA35B48C43AD477C61 /* MDCShadow.m */; }; - 1C63F278B31BE01E9A399C213588B1F5 /* MDCTypographyUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D638A90314C4C723DF58CBA2D02DCF3 /* MDCTypographyUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 1C994289D94896DEAE034CD8A0B4CDF0 /* KeyframeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABFF571A3232481B34A73FDDE0F2B55 /* KeyframeExtensions.swift */; }; - 1D17B83410DC98911D539F2BD5254C05 /* RequestTaskMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6090940C42D19C30699D9DFCD46FEA1 /* RequestTaskMap.swift */; }; - 1D313F4071D041D30364BC379612A7C8 /* MDCEdgeTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E786DB3BAA72871D457F5DC195A08AD /* MDCEdgeTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1DCF9A800AE274F89C29078A6E212B7D /* MDFInternationalization-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B120981A19A9C956A2941EB064D1FD1D /* MDFInternationalization-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1F3D77EAFD74D2CFE5C43D46FD93C6D8 /* VectorsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD410EF9494BB5D4662CBBE59C2C06C /* VectorsExtensions.swift */; }; - 20F80E96D387808DB7427A1249E8EADF /* TextAnimatorNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF0A7BE4E58D6697CCF7D0FA0CBBDFB /* TextAnimatorNode.swift */; }; - 2155754362430305C54CBCC65CC1A1A1 /* MDCPathGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 8246440519C85238FCFCF8F58392A86F /* MDCPathGenerator.m */; }; - 22AA981BFEFAFBA8C6B511348D60D284 /* MaterialComponents-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6741DCC6A7B256467254EDFBBDF05B3F /* MaterialComponents-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 22FFC6C3AAFCD260E0DAB32352B704A9 /* PreCompLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82AB7D6FD755ADF708F94AF789430203 /* PreCompLayerModel.swift */; }; - 24B503B9E52E65303577FE6910BF3D15 /* MDCShadowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 561E46EAC325F966EA9DBA55F0689607 /* MDCShadowLayer.m */; }; - 252655EF3425EC8EF3154477B9B4C3F1 /* ChatItemPresenterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06460E2CD793BBF2C535219D13A50C05 /* ChatItemPresenterFactory.swift */; }; - 2550F0D474DE846FEC5C76CBE85F927E /* OperationQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30734D2F4660366E1C28FA6E2E4345B1 /* OperationQueue+Alamofire.swift */; }; - 25ABCCB506CD51393434BE44F446AD5F /* MDFInternationalization-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F1F807B149E883C56B85C5C055D651D /* MDFInternationalization-dummy.m */; }; - 25C84FA79FF9DCAA88669C3380168F44 /* AnimationTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BEC31BE7157E03DF220C390111D13 /* AnimationTextProvider.swift */; }; - 267ED94E779B638FF06A3796A5DBA7E5 /* ChatCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F96BF2DFC0E11E6A55539B9225C94 /* ChatCollectionViewLayout.swift */; }; - 27004AABBE0496F9D5C7B3D8450F657B /* MaterialButtons.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DA90325405F33A5808A47E8BFFAEE6F /* MaterialButtons.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 274C601104AC3151F7A65A4F15A82F3D /* RenderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E173EE03A996655ED2311253DA58EB9 /* RenderNode.swift */; }; - 2866EB6C78D5E0A3256F868DA33E06DA /* MaterialInk.h in Headers */ = {isa = PBXBuildFile; fileRef = A8AB1E1A488B0F97C034BFD4ECB9C11B /* MaterialInk.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2A94738462743EA303248E706CF448BB /* TextLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7639C506BC1DBC0CA831E0E2A106D658 /* TextLayerModel.swift */; }; - 2ADBB4BE80F61C7FF2AC39558549DDC1 /* AnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F4B5DB7011E5176A063CF3110CD09E /* AnimationView.swift */; }; - 2CE76602B51D001D59FCC5CA12BEC37B /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 713A971DE11ED11949FB41259A01995C /* Merge.swift */; }; - 2DC8E9D2FBC9CB113C32F1F2DD4DCA22 /* MaterialComponents-MaterialIcons_ic_check_circle in Resources */ = {isa = PBXBuildFile; fileRef = 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */; }; - 2E8A20C40A0F48D1F8C0806CE740853D /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17C76564686F46F80432DCE2D053940 /* LottieView.swift */; }; - 3064376167F8EFF6F63AEC652D55E9A0 /* MDCPillShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 2102D1FE11630B5091FE865DDD43F05A /* MDCPillShapeGenerator.m */; }; - 30A331CD9286145E92DB11D671664C63 /* MultipartUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7872898EA812A796E27F3664AA901902 /* MultipartUpload.swift */; }; - 31B827F3FA1AB67F3139AA92AA3C9C88 /* BaseChatViewController+CellPanGestureHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F58CC76B154EE1FFCEBFD29458765F /* BaseChatViewController+CellPanGestureHandler.swift */; }; - 32DA6FCE9DD284D13AC2A4CE1259320D /* MDCFloatingButtonModeAnimatorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B239243820382C1C6E7038645E10958 /* MDCFloatingButtonModeAnimatorDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 32E691399B269968E2DD2CAD9F66C4F9 /* NSArray+MDFUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ADBAC831F15371103E6075A7099776A /* NSArray+MDFUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 335CCDCEBD9EB7CB33697888F3E0D6F8 /* AnimationKeypath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4259483CFD0AC1C7F1295B7B354781A1 /* AnimationKeypath.swift */; }; - 336037502C5A263DA6CC3831EF8EFD9F /* MDCPillShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2882BFF487CA5879D2F68C6B944595F5 /* MDCPillShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 33F86FAB918B148A63A1575667F9B570 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 870DC3835055FF27A861C151FF69B97C /* CFNetwork.framework */; }; - 34426DF353C060CB74307404D7788A7F /* MDCInkGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = A67A50DB4E82C2861D15BCA11383D919 /* MDCInkGestureRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 34F8E92AE1B47864EB2828243BEA8378 /* MDCInkGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9906975C7ABA6E0912B4362EFF02B32C /* MDCInkGestureRecognizer.m */; }; - 35A265AD5CA62B93ED1BE1780D22B92B /* AnyValueContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE99EB6CC9C88A5D592843A9171EC23B /* AnyValueContainer.swift */; }; - 370A0DE2184C02814935D61032C2E189 /* MDCRippleTouchControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = D6E67B621B39259F3CED1DE2F43E57AE /* MDCRippleTouchControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3730BD7F19450F90CC632D633517DCD8 /* KeypathSearchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FC5A268C4EB4A715BD6C5613A37B5A9 /* KeypathSearchable.swift */; }; - 378706EAE3C525A5A25FD652CF35ED3A /* MDCShadowsCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = C73D5045364BD3202565B4FDB54AE37A /* MDCShadowsCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 37E3D167D051B6E64ACCC71D8111CBE1 /* AnimationKeypathExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F0F84872DE9FA147E94B5562B8FE68 /* AnimationKeypathExtension.swift */; }; - 388951286A46DDEBC11081395CE19424 /* GroupInterpolator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5739897ED45AAC422F0EECBA1D75F58A /* GroupInterpolator.swift */; }; - 388CB67650DEC60578CD193C099F93C7 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E035C3FBB06190E0CE480284B8CF014 /* Group.swift */; }; - 39874777CA1F53BB8BF51F9C0DF90173 /* MDCCurvedRectShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = FEC9B0599DF18776D54CF802CB9FFFD9 /* MDCCurvedRectShapeGenerator.m */; }; - 3A8BFA61B606E1EA5527EF2D18D49D4D /* ShapeTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DBE44442938F2CBE3624C972232C24E /* ShapeTransform.swift */; }; - 3B214DA1F9E0A2F6BAA4DF248BE271AE /* MDCCardCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A035D2874E49F84C6A8FF8FBF6503C8 /* MDCCardCollectionCell.m */; }; - 3B8C4CEB676840C42DE451C65C4D79E9 /* MDCStatefulRippleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BD15A4D6488FC7DCA7F0F18E0CAC1B9 /* MDCStatefulRippleView.m */; }; - 3C044F4DA0AAF294B5B40D8D61F76067 /* LRUAnimationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94E88E183E7AC90A64BD46E39E2C85E /* LRUAnimationCache.swift */; }; - 3C1AF825830E083E899D53A5C2F7E3EB /* MDCIcons.h in Headers */ = {isa = PBXBuildFile; fileRef = C364DE9E9FEDDD730DBF09F77CB0B143 /* MDCIcons.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3C1C2FE0D3AD4E5B2EEDF80C83AD822F /* ChatItemProtocolDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A488EF6B3932789A199EFF0E48669A8F /* ChatItemProtocolDefinitions.swift */; }; - 3C51EC6664C4867B564D0B6604C7738E /* MDFColorCalculations.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BEEEC76296F936D8FA70CA0604BB74F /* MDFColorCalculations.m */; }; - 3CE30BDD410305F4D3C8C13D12507A4A /* NSLocale+MaterialRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */; }; - 3CE69A673E4CCBBC2BF363C34B3E4702 /* MDCTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DE63B387DA24E48BA2EDE84C68DC8DE /* MDCTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3DCCF761B72AB77C921C6347AD39D34E /* NSLocale+MaterialRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */; }; - 3ED06C6A277226FD2D9410BAC8E6649D /* Glyph.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C869DD8FBC246E0FE2199DCEB50D3D /* Glyph.swift */; }; - 3F016AAA33E691084D294F7A12AC56AD /* MDCRaisedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FF5E5B45041E7444D6447C17872668F /* MDCRaisedButton.m */; }; - 40F06CD846C60BCA8E500808BFC5D8AB /* StarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD727B37100536B0582E4ACA83844 /* StarNode.swift */; }; - 40FA888B44A7961B13F0E7B332102CFC /* AnimatedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F84C77FED35072FBA03ADBD28FF9F3 /* AnimatedButton.swift */; }; - 415CF97552CF15ABEB0FBF16BB7CBC0C /* BaseChatViewController+Changes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA378A26B99BBD94560AE1C8C02B11F6 /* BaseChatViewController+Changes.swift */; }; - 41A27CED3C6DB6554EBDD9BA47356969 /* MaterialIcons.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D0323B0058AFC25122470F8D9558B7D /* MaterialIcons.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 41C21FEBC88B3A3F97B18DBDF929C751 /* MDCTriangleEdgeTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B5993FADA59638CD437C1C18E3DDA1D /* MDCTriangleEdgeTreatment.m */; }; - 42DD01B84A5FF2063B4C6ECA671FCE3E /* ItemsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61814AFE969DF03C54E6B9E503F28302 /* ItemsExtension.swift */; }; - 437EF01383D730AE242D947B9B58AA18 /* MDCInkLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3414E16D6767D8F8FBAB002ECBAA6CD /* MDCInkLayer.m */; }; - 442D9429E34A98231CFCDE3F82B30DE5 /* Marker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22A0783A99782B1368A7C295F05B893 /* Marker.swift */; }; - 45E483574827876CEA2E3D2DE49257BF /* LayerImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35BD0FEEB2EF9ADE87455CB6949E56DC /* LayerImageProvider.swift */; }; - 461BEAD29EE3D6E33BCDD8951CA80FD3 /* GradientStroke.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6ACC937F5C195801B15F2AB3B92101F /* GradientStroke.swift */; }; - 4634BA717BFCE522E5B42304C6A78B5D /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5CBEF7738EFE577EEAE11F00FF329E /* ParameterEncoding.swift */; }; - 471611F482CDC15BF464E3BA9CB83968 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE7DFA57EB0C03A4757CAB9256086346 /* Notifications.swift */; }; - 47A9B68E4429C21FB25951BF68989FA8 /* NSArray+MDFUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C83FB5770E9E4004C80C5217E3CD69 /* NSArray+MDFUtils.m */; }; - 482827C9A937BDC9B86EC162974D8F3E /* MDCShadowsCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D714F498A6A5E7FC8FB92A3819A449A /* MDCShadowsCollection.m */; }; - 489308FC142DC204B40A895C3A6F3A1B /* GroupNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63431295907542E5A9DD8630DC9338BA /* GroupNode.swift */; }; - 48A55C7FBBE4B4F30AEA67E3A50006EE /* UICollectionViewController+MDCCardReordering.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A370E644FBCA6F70723C4C1D6720158 /* UICollectionViewController+MDCCardReordering.m */; }; - 490142072F8EE916442517A5884282E0 /* UIColor+MaterialElevation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C64B5F902ABF8446821C23B3779E31D /* UIColor+MaterialElevation.m */; }; - 496713FA3DBA4082C7948ECF7349A992 /* Pods-iosTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F5922665F1DC85A6D8A92E2E648D19BE /* Pods-iosTests-dummy.m */; }; - 49A404FC51F1F7CDCDF7FD0112F00A5F /* SerialTaskQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536C19C18245423A6AFB8166C8F93F35 /* SerialTaskQueue.swift */; }; - 49D5F0C6A4922BCF3D0B41DFDCE905CA /* ShapeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBCF7000A4C9DC9C24B02BFE17451F85 /* ShapeNode.swift */; }; - 49E32757F5D405E67A46B71E862DE633 /* Interpolatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91855A9940FB42F19FD2875C45E2CB01 /* Interpolatable.swift */; }; - 49F6DAAA65DA40549E2D693A7E1B9E85 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - 4AF0F0425EE13873D1A3D72C87FD0CFE /* Star.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE689E19EA5C8EF636167214A787615 /* Star.swift */; }; - 4B4B75F4B215FD3E854871B3E1F31CD3 /* MaterialShadowElevations.h in Headers */ = {isa = PBXBuildFile; fileRef = 0056C47896966D18A6A1A5446B66D105 /* MaterialShadowElevations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4B9A1C30219659C0AA8D1AE79832347B /* MaterialColor.h in Headers */ = {isa = PBXBuildFile; fileRef = A575DBC967BD1B9824384DE5EAC71824 /* MaterialColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4CD67A5130FE795F570FA71E4A0F0817 /* MDCFontScaler.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D37C9BDBB809F1F5B2D0212FF9CFE99 /* MDCFontScaler.m */; }; - 4D06CA57EA8BFCEC35A0E97BEA2DF034 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEFD189E2BBDDBFEFE3D285ECEA7330D /* Bundle.swift */; }; - 4F63D1C9A3C378378BEBA8171E0EB4B3 /* MDCFlatButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6828FC052E6421784F770E0D6078DDB6 /* MDCFlatButton.m */; }; - 4FAB52F6DBCC8A48A6FC5B865CF4984D /* ShapeLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF96553AC1A4981ED8F74D3571CB86C /* ShapeLayerModel.swift */; }; - 4FC0BE22D1389E78DCDC8515F3C1F124 /* ValueContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80EB9AAB47357453596394BFD2FBD6D /* ValueContainer.swift */; }; - 509492048834DCECFB5FAC619B80CDBA /* ImageLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEB0A58B0BC617DECB8D3427A1405634 /* ImageLayerModel.swift */; }; - 512FAFBD71830F126224C033B6C45F4E /* RetryPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3619A8910E5127A2A7EBD2417924A3D /* RetryPolicy.swift */; }; - 51BE4D64CB57A36659FD73D354F810F0 /* RectNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20AA7DE19CA8DB5FD1FE6636C4C6E89 /* RectNode.swift */; }; - 5269279BA5DAB06E59635312FF2FCC76 /* MDCInkView.h in Headers */ = {isa = PBXBuildFile; fileRef = 79EA03EF06AA77426C2C84981497D6F8 /* MDCInkView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 52BE6F747C26DF2A24532458E55DC10F /* HTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FFD3AD6BFF43E494BD4BD3E560F4A9 /* HTTPHeaders.swift */; }; - 535D24555149DB4F16003AEB62B2F4FE /* lottie-ios-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 631CC31B1866E988A56C286024B8A805 /* lottie-ios-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 546594038A39C1C5480919C5C51FFF46 /* NSString+MaterialBidi.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */; }; - 569D15C26BA16947069372B7D6269C51 /* SizeValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C78482FC811B81F0499146DB6F8592 /* SizeValueProvider.swift */; }; - 58287E8A2D3CC19A61F6EC3D5138CED3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB57E704596E48FF3325A1F46B6E783C /* QuartzCore.framework */; }; - 593F9E60F9CA978FD7DEF9A6C0EB917D /* MaterialMathDummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D3C80D0A870376D3DAB74C8D106BFF8C /* MaterialMathDummy.m */; }; - 597AB8E13D0B44321059C630A47807D7 /* MaterialMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 98EB1455F30B28A8F4F59C33E8CB38D2 /* MaterialMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 59CA53BBA8B52B7565A5B31E205B5DBE /* MDCIcons+BundleLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FCF28462DDA6182768A18DEE727FA6C /* MDCIcons+BundleLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 59DB1972DC0FB82B837041D7ADA10E9B /* MDFImageCalculations.h in Headers */ = {isa = PBXBuildFile; fileRef = 7053BE770B19CADCC67259D2058FBFE5 /* MDFImageCalculations.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 59E047AE8E4BE5C88696952252BF2AF4 /* PathOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 940160C49FE72F93E569C21314AB30EE /* PathOutputNode.swift */; }; - 5CDEA5D93507BB56778DFE0FF9D755B6 /* MDCFloatingButtonModeAnimator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4148219036DBEF09983EE603792119B1 /* MDCFloatingButtonModeAnimator.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5DEC3A3BBE20B44348CE23832360AB86 /* ShapeItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86684B080F0E3D4C1D38F093601783B /* ShapeItem.swift */; }; - 5E479B4E6701DA0CEA6D0A57C50DD9DC /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E145BBB7A0266AF3CEF37E16F141F03 /* ColorExtension.swift */; }; - 5E5703B9B2DCBD225054E143EDE69080 /* GradientStrokeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0A65CE9CFA027DECC15DDE7C84261A /* GradientStrokeRenderer.swift */; }; - 5E594FA3290D3D70F500572D0AC100DB /* URLConvertible+URLRequestConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C4846B514755F166442D8D10CEA565 /* URLConvertible+URLRequestConvertible.swift */; }; - 61FC00C7FCA912D5B60ACFCD7B93CB32 /* MDCInkView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA395024B9E67C9EE8187839618E96FB /* MDCInkView.m */; }; - 626F1E2369BBC5E226FCB50B0B3D74A2 /* Asset.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7B1A690B1D232B90A70E4E318BB297 /* Asset.swift */; }; - 629777492DD9FCEC0E6BADDED7A06292 /* SolidCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C5DEABB0B24A9A6A4FB076F829A033 /* SolidCompositionLayer.swift */; }; - 64317603918517CB58BC50355BBD14F8 /* MDFInternationalization.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */; }; - 64A1B6BAE055F3BEAD2244B06B758B47 /* UIFont+MaterialScalable.h in Headers */ = {isa = PBXBuildFile; fileRef = B867765B8E02C5AE1B3C6A2B771DE65C /* UIFont+MaterialScalable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 654D4B8FB3BA49FB52CF733DF0F28CDF /* AnimationFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD480CF250BF63F248837A06A6F55B90 /* AnimationFontProvider.swift */; }; - 688337B18659C4BF722F87AFC4FEEF81 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7B54C5101F5D61940E8C3AE0858E0A6 /* SessionDelegate.swift */; }; - 68C81A493C4CD3F1D8425C3C12B6F1E3 /* MDFRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */; }; - 69200AC3BAAF8F7878D72847AB8DDB8C /* MDCCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B2BD90586E1B7C4656D777C09F8F99D /* MDCCornerTreatment.m */; }; - 6A6B387C7487CCA8B700C94CAE4297C3 /* StrokeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C11F8849A9F9FA96193BDF6D47B305 /* StrokeNode.swift */; }; - 6A7604C990C5DFF551C625E6EBC1E93A /* MDCCurvedCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = CBBE66515626EA5EE7282EF95C8275B3 /* MDCCurvedCornerTreatment.m */; }; - 6BC837979F6FD58ABE2029174745CF8F /* FillNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B32DE56F884A29871DDC06DEFDD299 /* FillNode.swift */; }; - 6BFC3EBA0293805C586DED141F57D440 /* MDCInkTouchController.h in Headers */ = {isa = PBXBuildFile; fileRef = AB54FAD6E9F36E88C0A18E94EB070CA3 /* MDCInkTouchController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6C945C98391E1347EE968F36DABA4B3D /* SolidLayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187B43272E4EAEF4B35EA65BBA8B1334 /* SolidLayerModel.swift */; }; - 6CEDCE10C0318529B41437117724D2A3 /* GradientFillNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19302162D052125FE5F7992C054A240 /* GradientFillNode.swift */; }; - 6DADCA7F70AA8718696BB357CD2D6140 /* UIApplication+MDCAppExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = AC2834BC1BEF7C025DE72333BE447006 /* UIApplication+MDCAppExtensions.m */; }; - 6FF5AC95881C5994A17183FCD68D9760 /* MDCShapeMediator.h in Headers */ = {isa = PBXBuildFile; fileRef = A287971E3E70E72ECCDFC5485A8B6D35 /* MDCShapeMediator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7004F49ED8B75EF71E86E1BB0BF7F31B /* StrokeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0EBEFA02E716358C707E8E92A56CC2 /* StrokeRenderer.swift */; }; - 70BC4C88A01730C10637E04170D18842 /* MDCShapedView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4157CBEADF18154FD243E39C50468258 /* MDCShapedView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7154A1C49053E5493963962660A03513 /* CAMediaTimingFunction+MDCAnimationTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = A4D99A5054C55BC32C98A5EF91CB5BDD /* CAMediaTimingFunction+MDCAnimationTiming.m */; }; - 7187BA8BB4975F949B9A3E5413EF29E6 /* MDCFontTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = 275AD0BC20557E9DC939C53DFF5BB2F3 /* MDCFontTraits.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 71EEB4CB0D6FF002E43D958FAE52AF63 /* UIView+MDCTimingFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D1A663AAA238F5A6580850607B58FB9 /* UIView+MDCTimingFunction.m */; }; - 728FBE5D5AFDA125AB4D0FF6F66DDDA0 /* KeyframeInterpolator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174A76F8A707305878153E63E7AF69F3 /* KeyframeInterpolator.swift */; }; - 731FB6C1A89663C6273A5ED2A93838A2 /* UIColor+MaterialBlending.h in Headers */ = {isa = PBXBuildFile; fileRef = 73500719BEBC67E98C2432BA726CF2BD /* UIColor+MaterialBlending.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 74129ED9A3E24DFAFFC726872335B23E /* MDCFloatingButtonModeAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1732F4D5612B18CF4BCB5A0D5F9A8C /* MDCFloatingButtonModeAnimator.m */; }; - 7429801C83C663E2AC03DF593EB09F26 /* MDCCurvedCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A09CD4BB399191B8387A7D72E4B2909 /* MDCCurvedCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 74804BD8118FAA4C1A0F62E7A9E8F8B7 /* UIFont+MaterialTypographyPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = F49594B80BEB495C857FE4A0C034A387 /* UIFont+MaterialTypographyPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 74B1E17BF464786B34DEB58A508B48B4 /* MDCRoundedCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 95DF7386D55C9F9642DC5077C341E35E /* MDCRoundedCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 74C2C803859AE1F9EDBB8BC6D027ACE9 /* InputPositionControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA603856ED143A5A0536C7F9F8785FFF /* InputPositionControlling.swift */; }; - 7514339E0BB67279B4151F7E7C9DF20D /* UIApplication+MDCAppExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = B27333CDD645D2D5CF612BB67776A72B /* UIApplication+MDCAppExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 75A51A634B4414C4BC6CE7DA089CAEC8 /* MDCEdgeTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 19E09DB898B5D3E34265E0EE3D2FB559 /* MDCEdgeTreatment.m */; }; - 77F503B82A75DA59A61413C1D5371422 /* MDCTypographyUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 49FB87A2C6705951A697BFD94E3FFB81 /* MDCTypographyUtilities.m */; }; - 785E547BC62FE180EDB4F80976888437 /* MaterialIcons+ic_check_circle.h in Headers */ = {isa = PBXBuildFile; fileRef = DF79A6BB5BC283B6F1FFF7394D1ABD51 /* MaterialIcons+ic_check_circle.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7873F0E585A1DD314E9A681B5E4C050C /* MDCRippleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 65D2F1380362B7EE62D2678F79650A3A /* MDCRippleView.m */; }; - 78A557FFD62CE14E4D6B85C238D6D367 /* BaseChatItemPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6102438DC85CE0EBEE3E8E37512F0869 /* BaseChatItemPresenter.swift */; }; - 7A42366DBD41A248734C3A9973EC9DE9 /* MDCPathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D1132D92154CA46250E791A60FC2C43 /* MDCPathGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7B71F5D8CEF38A4E44B101984BF32A22 /* MDCCutCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 43693CCEC265B75D660F71A9422AC97E /* MDCCutCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7B90B8B1E94AA7BCB6F8195B232C0504 /* NodePropertyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF2CED0DB3829D4D4BE54A78C1072DB /* NodePropertyMap.swift */; }; - 7C49833872601FCD2511E3717A726467 /* ColorValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0B52C8F35A67626A42F0430F36E070 /* ColorValueProvider.swift */; }; - 7D3E0B70A1031AF6F52BB2F2704C5D7A /* Chatto-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B93D411155688717E99B55AE5D57CF10 /* Chatto-dummy.m */; }; - 7D6751B1F74E12C7B85C8A86EF398113 /* AnimatedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6EB232EC11EB49D9DA52EDFB9660747 /* AnimatedControl.swift */; }; - 7D990BA3B83998A2A1ABD69E0F3D44CE /* Pods-ios-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D720E1DADE8B1BB169230ADFA11012E3 /* Pods-ios-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7DB6497C9CC06CA922AC1152CE38CDAC /* MDFRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */; }; - 7E4FBF289DDBE5FE3D144CF1347D4092 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3117010738ED3E7B5080810EBF99FBBD /* CoreGraphics.framework */; }; - 7E636D3BE68B055932C1DBF52E9E5F8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - 7EF5AA59D08C107D83A5E3FE51633211 /* UIView+MaterialElevationResponding.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA556AD9FDEF109652EAA16FF14D299 /* UIView+MaterialElevationResponding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7F1DEF5C995E93F3EEDEE6562BD3FBF4 /* ChatLayoutConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD0995EC979D7375ABF14FB9A0A9EB0 /* ChatLayoutConfiguration.swift */; }; - 8143C58AA2F1E117E2A7BF20E9582EF3 /* TextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BBD822E6408AD8D9D1D46DBCBA9381 /* TextLayer.swift */; }; - 822EE6C99BED7B9C6A92C87D7717EECC /* UIFont+MaterialSimpleEquality.m in Sources */ = {isa = PBXBuildFile; fileRef = 039BA34B5A5B9FE2B7971FE6E64557BB /* UIFont+MaterialSimpleEquality.m */; }; - 82558E399C6304091C91304BC4E9FAD1 /* UIFont+MaterialTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E28A208954B54643898470FABAC2905 /* UIFont+MaterialTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 847D96F05D6EF0D2AC3E54036A72843F /* ChatDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA843D119FBBE1873DB868B2C42F47A /* ChatDataSourceProtocol.swift */; }; - 84C8B10EB6AFC1DB7574650206F73019 /* GroupOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 058498847739D73FC959AC6FAC7A0D9F /* GroupOutputNode.swift */; }; - 84DA7DB4EF4E0E8B628B429882B14FBB /* TextCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C8E244ED61A9B57F738C5DB46FCC12 /* TextCompositionLayer.swift */; }; - 84F43C1B8B0B179BBFF86DE99BBF4422 /* MDFTextAccessibility-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = FBE0655303C1520FF0EF50C57907E661 /* MDFTextAccessibility-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 856565C8AF270085E80E72C5D81FA186 /* BundleImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAEF5AB5FA15E20986DA16EBD0840CA2 /* BundleImageProvider.swift */; }; - 85AF5816CC3948E8200711491BA938E5 /* MaterialApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CE46363B5ADC47CE9B57A3335A26016 /* MaterialApplication.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 86DB9FBEAE3DE9E248D74A04EEE10875 /* TrimPathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0CB865665244183E4B9659E94101F9B /* TrimPathNode.swift */; }; - 874F69B4F2476462C2E8952A231DEF39 /* LayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525767FD21D481C033ED537000C2A5B1 /* LayerModel.swift */; }; - 87882C5A446E492E7BC82763056E299E /* MDCShapeGenerating.h in Headers */ = {isa = PBXBuildFile; fileRef = 46AE7E00528585497EA59CD94F669E3A /* MDCShapeGenerating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8917A3295F44CCF46EEB66EC69B6DC31 /* GradientStrokeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FAB2482AA7F33F1F62927A25599EF4 /* GradientStrokeNode.swift */; }; - 8AC8E7FC1C4A48B36484927306556F31 /* CellPanGestureHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C197D9AE91D805F5FFDD745A3332768 /* CellPanGestureHandler.swift */; }; - 8AEE5FA821A6A42749CFDF8DD6CDF413 /* ShapeRenderLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6DB58D8654DC8B5F88E4D12FD74409 /* ShapeRenderLayer.swift */; }; - 8B8F97130DA7C5ACCF2ADBD9A8B8C138 /* MDCRippleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = E81ECAF8BBE1DB54276ED819382CA9FA /* MDCRippleLayer.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 8B9CDBE3FFD712120CD66DD8B06C44E4 /* ParameterEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA343ABB695B730239978538A94F38D6 /* ParameterEncoder.swift */; }; - 8BBBA0F8098CEDCDE429C22E32810395 /* MDCLegacyInkLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = B15025A80B5750A7F05A489683577910 /* MDCLegacyInkLayer.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 8C050C985EF60CCC3B9E25050BE20F3E /* NSLocale+MaterialRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A85C401C2117402508517940B47B11D /* NSLocale+MaterialRTL.m */; }; - 8C59CE48E9D049159ECD769482024C7A /* AssetLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D7840BB6B99AC092EAB4942FDD3F53 /* AssetLibrary.swift */; }; - 8C802F4EE8500BDBC0D000E200B162F2 /* DummyChatItemPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A9EA755014DD5DD4C27D7ECD93783FD /* DummyChatItemPresenter.swift */; }; - 8E9D0B3DD987E4503F62401A7F412FCA /* MDCRippleTouchController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DC202126AC7DEA521D46EACE0A8CB33 /* MDCRippleTouchController.m */; }; - 8EFABEE2E3BAE5BD9B93C1B58A69A178 /* UIView+MaterialRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */; }; - 8F9E1EEF2FE52E3231A769722D5C4148 /* ServerTrustEvaluation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8B9BF018FD470AE5EBE4F9FF3808CD /* ServerTrustEvaluation.swift */; }; - 9165FD4860C36068CF81600ED9992F02 /* MDCRoundedCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A16E9C361081323779110A2E0EE4F83 /* MDCRoundedCornerTreatment.m */; }; - 92D5715E90A1540B39AFE10F3D2A5443 /* CompatibleAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6F69E4E368E5F576A3D6AF24022D2D /* CompatibleAnimationView.swift */; }; - 92DA5E1124FFD63F1FD2CB5B3A106B3F /* MDFTextAccessibility-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AA82D677A35A05B022A324E49F50C /* MDFTextAccessibility-dummy.m */; }; - 941822CDF68EB8F4D49F150457A82616 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B411BF2903D79884DFBA1F089404388E /* ResponseSerialization.swift */; }; - 9476C7CB756F02F62FF85F3AC6A74FDE /* AnyValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A162FEC0123631BFA766AC1CD2E593 /* AnyValueProvider.swift */; }; - 94C1D0EC92D3122730BAC6014910D19F /* MDCButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 97BE17BC31FAF7F8B04D6EFFBBE78C80 /* MDCButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9535D614A8A0FD3B56CA7E8BACA11451 /* MDCCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D46DD556D1F791A1853E2A6E2506FF6 /* MDCCard.m */; }; - 960892840B75C67C133BAA7E39C35D55 /* MDCSlantedRectShapeGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EEB4FCAEBF9555B90A5EC48FE3D6B4F /* MDCSlantedRectShapeGenerator.m */; }; - 96A6F504929F868038D46B5B6A03038B /* MaterialShapes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A7B2BF1390CAC2A1A79D280BA2AA739 /* MaterialShapes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 97030F2617BD73C118C8334624A84458 /* MDCFloatingButton+Animation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4838FCADE32426C4BC81299CE2906DE1 /* MDCFloatingButton+Animation.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 97584BC08D2B494417BDEE268CFF38C9 /* Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93520D487909F1B889485508132F1A96 /* Combine.swift */; }; - 984167BC563C943B94B43A01DECBEAB0 /* UIView+MaterialRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = A2783799D5308869A35311BBDF845364 /* UIView+MaterialRTL.m */; }; - 98E4EFD77D2015E422923ED1EAD4AF7F /* BaseChatViewControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4F064EEF86F1829D1B928D11963739 /* BaseChatViewControllerView.swift */; }; - 99D85B4B92B509E63CB8C3056CF601C5 /* SingleValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF8701D965CC5D013B9CC49B8B228E8 /* SingleValueProvider.swift */; }; - 9C0BE8FA0030B2BC1DF7C159FA059389 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - 9CFDA7C92E0EEA31F709663B0E727ABA /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6A0C29D359F8F037F7DF2A21B78FC4 /* EventMonitor.swift */; }; - 9D3A2386D2F126134C1A61111BAF2B8C /* Stroke.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCD73AF1D7111668851A56E369B30AF /* Stroke.swift */; }; - 9D880C5CFBE97A142FC516777217C492 /* MaterialShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = ABED2A4D6F3787803F5376C66CB9544C /* MaterialShadow.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9DAA6E25378FE244F7D4451A5D35FB51 /* MDCCornerTreatment+CornerTypeInitalizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC4A25E6CBE6CD94D65C7642079986 /* MDCCornerTreatment+CornerTypeInitalizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9E361C56F23EEFCEBF62E79BF5165AD6 /* MaterialElevation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DD0AD806399CC6AE3AA2B4F75C77C23 /* MaterialElevation.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9F64E84B54A721D525FB5FEB6B04AD92 /* NSString+MaterialBidi.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */; }; - 9FA8E4B6DCC77FC54C0E72979D87FDDA /* MDCFontTextStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 87107EE4D974CAE959A375B662F4204F /* MDCFontTextStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A093909FE7269A84364560DDC9375F31 /* MDCCornerTreatment.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E28ED602A75DB92340D7444DB1995A1 /* MDCCornerTreatment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A0E0BC90F446AAFA84B16933CE45D172 /* CAMediaTimingFunction+MDCAnimationTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 37F289E6F2E521F6C7C3C1203689674C /* CAMediaTimingFunction+MDCAnimationTiming.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A3153333FC136836B0028E6AB2A56BEE /* AlamofireExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 701CF46760379C28084078F7D4BAFF32 /* AlamofireExtended.swift */; }; - A32194DA20A525E2A0DD2C1DC780FF99 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45EFEC8F1A0F1093631AF60C90BB060 /* Font.swift */; }; - A3D5AADA1B068C272F4C41E9771EE195 /* TextDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDC53D7CBF3A251E463BB224DD12DCE /* TextDocument.swift */; }; - A4D811A62B4CD50FEF2943AB7A090055 /* MDCRippleLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6DE9329322864658D938C211EAFA09 /* MDCRippleLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - A4F1202CE5BBE79F3BBCAE3D2B16BC03 /* Result+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246B8D62D146A318E129AB0C450D33BA /* Result+Alamofire.swift */; }; - A6487D3314DFDA70D3EF7D709AFD1A17 /* UIFont+MaterialSimpleEquality.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AB8B4FEB07692832F02CED08212015A /* UIFont+MaterialSimpleEquality.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A664924D6CCE2922A3F81EC932F4D476 /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BDB7E8223919BD683DE47719CEA1A69 /* AFError.swift */; }; - A6735D2299B4CAEA6BE631073D8BA734 /* AnimatorNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E1DDE875714E724C5D0109D3735095 /* AnimatorNode.swift */; }; - A6F57488F348E0036579E5B9DD98CAA2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - A76309F8030847107CE541B3B173FC24 /* EllipseNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67AF078C9EE64DED1961778EFF012A0 /* EllipseNode.swift */; }; - A76F7B04181F8D8A0F931424F54C4C07 /* MDCCutCornerTreatment.m in Sources */ = {isa = PBXBuildFile; fileRef = EA9BAA45A6BA523F01C4735963DDDADC /* MDCCutCornerTreatment.m */; }; - A94F1967A266EA1EB3E4910073BE41F4 /* BaseChatViewController+Scrolling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0314F72C05B7FF63F27BE4646BD8305E /* BaseChatViewController+Scrolling.swift */; }; - A957485EF720B71593D3C8160C44B9DF /* MDCLegacyInkLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DCA91058EFB02847D0DA47F4F3195AC /* MDCLegacyInkLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - A9613D5848A69B5554E49963050E189E /* InterpolatableExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805B67A1EC691E2F297D0CB0A125C31B /* InterpolatableExtensions.swift */; }; - A9778B7C3AC696E47DF7D19ABEBA80C9 /* DashPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = C290807FDFEFCB92E5FABE8FE851443C /* DashPattern.swift */; }; - A9B10FC18D020818728E285314FDEDAD /* FloatValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 686DE3CEFB348F67B7F2E0CCDBED00A7 /* FloatValueProvider.swift */; }; - AA41EBE1B990DE355E95E626BCDE0D20 /* MDCShadowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ED311731D2D73DBC56836B34F3D634A /* MDCShadowLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AA578A39DA8FF95C6E3B387DE3C8654A /* AnimatedSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD14A3955F01AD7D390CC5763E43CEFE /* AnimatedSwitch.swift */; }; - AABFEA684B7B708D497FCACE7A03FFF7 /* CGFloatExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FE3D47542C17E69E982939D7C4342C /* CGFloatExtensions.swift */; }; - AB0E5CD170B9656EA06269823DD89CB1 /* UIColor+MaterialDynamic.m in Sources */ = {isa = PBXBuildFile; fileRef = 1555D5D35385492B8D027E811BE666CB /* UIColor+MaterialDynamic.m */; }; - ABFFB41EC17E1CC5B8A7B0266718DAE1 /* MDCFloatingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C7C60015DC526524C5DC274704897564 /* MDCFloatingButton.m */; }; - AD9CE391F7F42CA08809086C88DA94F8 /* MDCSlantedRectShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 34EBCCA2E4424A06FB1C11FFF86AE0E2 /* MDCSlantedRectShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AEDBDAA38F82EC2FA51759F782D4D5AC /* MDCButton+Subclassing.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C628C67659ABBADF02BA056396F87E1 /* MDCButton+Subclassing.h */; settings = {ATTRIBUTES = (Project, ); }; }; - AFE7E0A95861830B7BE0CFCF493F662E /* Keyframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984A2B5195B698FF523B7D82CD694F0 /* Keyframe.swift */; }; - B061007C0C097FF93364AC9411CAFCDD /* KeyframeGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F19458534DF74ABBBC1D9171D2FA1A /* KeyframeGroup.swift */; }; - B1F8084190567650796949754C0D25E7 /* ShapeCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10CAC47530EA0936ECF4F5B18F41899 /* ShapeCompositionLayer.swift */; }; - B2CE1396D0B81AF08A5F6C886FF6E61C /* MaterialIcons_ic_check_circle.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 427AFF770A87FFC8BA489ECCD299D58F /* MaterialIcons_ic_check_circle.xcassets */; }; - B3901084F60BD621D593F30B15E9D5C0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - B3CB521DFC1B2FC4C400466B2636C9DA /* MDCLegacyInkLayerRippleDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F5C3BCCB2FBBD3DC6701A99A1EC16E2 /* MDCLegacyInkLayerRippleDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B4B385550E3C5121D46697EB10E83C02 /* MDCRippleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2931B1BB799911DD00AA57C202BA2A6F /* MDCRippleLayer.m */; }; - B50CAE951FE6B2B57ADDE94632B53B82 /* ImageAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D884B74C43A162D8B9756985E116E84 /* ImageAsset.swift */; }; - B59FA52B2FA220A61E7B371F6902BF61 /* MDCShapedShadowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E0B142ACFD6D223F820B0A902B17ADB /* MDCShapedShadowLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B5C83F05C6D42CD1F1AA5322952A475F /* FillI.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2502619916EAC0AB2E21B1A54DB347D /* FillI.swift */; }; - B5EB74E6B21E71690D59818B99FD9F4B /* GradientFill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B6A73993CD9CF59863BB4C36386EEAE /* GradientFill.swift */; }; - B6473B8E8353317F75D6800D4F7054CB /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D25173317D99ECFE9E0ECC69FCEC95FA /* NetworkReachabilityManager.swift */; }; - B713DE1857A1BE1020811FB842D67B1F /* MDCFloatingButton.h in Headers */ = {isa = PBXBuildFile; fileRef = F873EC876A8574DA2C7425CA4C3BC418 /* MDCFloatingButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B7964B8982C863F7AA9A9A0051387976 /* CompositionLayersInitializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31592E117E0046F2678B3066EB06AC3 /* CompositionLayersInitializer.swift */; }; - B89D1C69742F61878115334A1D2DFFE7 /* URLRequest+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B995834698359995E0262F3F70C896A9 /* URLRequest+Alamofire.swift */; }; - BB9A0A0E2A295ECBE4DF5DD2BCC49A6E /* MDCRaisedButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF5C7C1F8F18927BF9A4EBDE9886621 /* MDCRaisedButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC95E3D42356C72892097F3C727F8ED4 /* MDCElevationOverriding.h in Headers */ = {isa = PBXBuildFile; fileRef = BF7CDBB13E9F0AC3175BDF383C468787 /* MDCElevationOverriding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BD275ABF1DF797F0313866144001AE2D /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB9978D69E4ABC8F7AE8425D9E1F048 /* StringExtensions.swift */; }; - BECE55736F8FE0964E095D93B134E4E0 /* ImageCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A0FCAED698C32DB774702E78F6A9BB /* ImageCompositionLayer.swift */; }; - BF61BD1B780F84EB855CDC8D07C70CF6 /* AnimationPublic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B613FA13F2264BCF72F6DE1BD3588C52 /* AnimationPublic.swift */; }; - BF7F9976FA80C821BC13E58E30E94B5B /* MDFRTL.m in Sources */ = {isa = PBXBuildFile; fileRef = FC7593B690F872386EAC6DA2F85BD36D /* MDFRTL.m */; }; - C02D5ACD4E1A80E8AE4FFC958652C0AA /* Rectangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E21A4FEF3A460EFF2C8B38BBA65C25 /* Rectangle.swift */; }; - C033F3E94355E90147C197D165E7DEE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - C16A047C4E8D856309A486182A490993 /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F557E15F0C636BE0C4547A358DA8C93 /* MultipartFormData.swift */; }; - C16B4056D4AF47CF2BDF2B30E9ED27B1 /* Ellipse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA25E16755D429C6CC771AD6044CEED /* Ellipse.swift */; }; - C1C2719D78A6372AF387756B6D2FB07B /* UIColor+MaterialElevation.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F86DF24DDC31E73E6DC1AD1E8E22E6 /* UIColor+MaterialElevation.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C32D94C3DBA969C96E803632C0F15D5E /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AF02B3727FEC99B62286905D9F172 /* Color.swift */; }; - C4DA75B4F7DD07F64D5B2FB7B83421D5 /* ChatItemCompanion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 777B7794311A16E26C00B24639C365CB /* ChatItemCompanion.swift */; }; - C5248490BD1AB45374D2AFA6BC3C5C32 /* ChatItemCompanionCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FB631B982623359A1D4E17E34F6369B /* ChatItemCompanionCollection.swift */; }; - C52D6111A5A724CED4C0451F2D8D7F76 /* MaterialShadowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = B3F1C077ED9B83ED31FAF400299DC965 /* MaterialShadowLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C58D6FE775EF73EB56A0DB0F81E9F505 /* FilepathImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64882FB458891751B0C4E20C1C23D5F1 /* FilepathImageProvider.swift */; }; - C6464DD1F08A953F238EAD6328090708 /* MaskContainerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5241F42DE93198A9743B520528DEBE0E /* MaskContainerLayer.swift */; }; - C6A87EEB51FF906E9FF947949211D34E /* Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1BFB72AD30F0DE0585E804791B833D6 /* Transform.swift */; }; - C78971D6EF0C7FFBCF847261C2015F62 /* MDCCard.h in Headers */ = {isa = PBXBuildFile; fileRef = 11A09793C107A7840B6234B0400A787E /* MDCCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C7F66519CE6148F21D7DB11423F1D34D /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1962BFA68BF11D74BDD363384439BAC5 /* Response.swift */; }; - C872E018E08E0FD0B30B386614AB0E43 /* UIImage+MaterialRTL.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */; }; - C97B9E8FA09F8CDA9458EC1387BBB6A2 /* PassThroughOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25B52A26AE822B9B2F58EA6B9E7E240 /* PassThroughOutputNode.swift */; }; - CA13097C8726C13E7D19984C391E156B /* MDCInkTouchControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 229265038E32F623E46E6B51D41CC33C /* MDCInkTouchControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CA24AD420264A0A9E0481C2F2E062AB0 /* MaterialComponents-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F3ED95F3D3F150E9EF3F7B365B3BE9C /* MaterialComponents-dummy.m */; }; - CA9F252FDC0DCF869BDE8A27E5B03BA0 /* MathKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919EA980AF20A4FC75CE16B54373D398 /* MathKit.swift */; }; - CBC6305777E98D5C59FC5ACAB00A17D1 /* MDCIcons.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A7AEC8A27ED687F3745E5FF8E0FEB75 /* MDCIcons.m */; }; - CD82340C0739E932322EB77FBCFA6D29 /* PolygonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80B62ED9C592FA542C65C50680BD2D3 /* PolygonNode.swift */; }; - CE150C3D6D3F9D4DA4F25E6B2831A385 /* LayerTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D4358B34E866143ECD08151FA7D559 /* LayerTextProvider.swift */; }; - CEDB69EFF8FEDC065680C80B49BDC5EE /* PreCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EE66424995BD90C997E2CEA9946F2AA /* PreCompositionLayer.swift */; }; - CFD442A95C590C28AA710A2A228F9F4B /* PrecompAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81EFFCF10BFC1B08CD7DBED57D297675 /* PrecompAsset.swift */; }; - D04A750433B71EFFE369931A5990825E /* MDCFontScaler.h in Headers */ = {isa = PBXBuildFile; fileRef = 42A318810EC6F37DE9379EF21DE434C3 /* MDCFontScaler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D0C349CE9DB7214843BCDE9B8A4E4AF9 /* UIFontDescriptor+MaterialTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = EEBE2EE981029A2CA291F17EA8723B03 /* UIFontDescriptor+MaterialTypography.m */; }; - D0CC26A37DD0CD758EA556DD00D6503F /* MDCFontTraits.m in Sources */ = {isa = PBXBuildFile; fileRef = 789CAA7B6C1E469B4554820ADC2147D3 /* MDCFontTraits.m */; }; - D15FEA31AA9625BBF041FB91E48A9995 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C971CF5443E97989CD0DF44D9C18846 /* Validation.swift */; }; - D1662BB63BC57C2FEAA5E16F73DFB9C9 /* CompatibleAnimationKeypath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95BFC36D1019120C1DEC0F6758A0ED /* CompatibleAnimationKeypath.swift */; }; - D28B26A308824EEAE6D48615A1E0D14C /* GradientValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553199DA713332B2A7CE2BC1B053344C /* GradientValueProvider.swift */; }; - D2EB1D5874469F96E26EF47829867231 /* ShapeContainerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B96F646D146FE43BCB3C820D9573D99 /* ShapeContainerLayer.swift */; }; - D323E03646FFB2D4FA1C0633BE363EC0 /* MDFTextAccessibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 14889A17A94EB87B76300716DD2A9F36 /* MDFTextAccessibility.m */; }; - D3B9D2340450E1A03FC812FED6DE209C /* MDCCardCollectionCell.h in Headers */ = {isa = PBXBuildFile; fileRef = B518C0072DFC3DBA1FA3C49569B1F3DA /* MDCCardCollectionCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D4939BB9A0101F96A5D863184AE21A7F /* MaterialAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = E58F9B821896E6DE3629364AB9B21F35 /* MaterialAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D620FDB5BDD15F3DF283F6989DAEF5C7 /* MDCRippleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 31F194A86821D5801BEACFA2DADF397D /* MDCRippleView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D6245FA6A979B1028595B60789B07701 /* CollectionChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA5A232C8B4D4671F587B33DCEF5FB4 /* CollectionChanges.swift */; }; - D66484BFD46F95EE237049C7F9150C76 /* Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CD9DD5A9448FED236DB4E8AAF6C8A4 /* Trim.swift */; }; - D681FB9211B3EB99507A7659A926AD3B /* CurveVertex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74405B4230DF6726303FA767434E6A14 /* CurveVertex.swift */; }; - D9D325BD0837A5B3F2C4368B4AF42D8D /* AnimationSubview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAC390311C727CE7C129F84BD9B09B9 /* AnimationSubview.swift */; }; - D9F13F6D976BDA43867D98A5F1CF6BF1 /* MDCCornerTreatment+CornerTypeInitalizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 24866442BD723570188362AFBC61ECA3 /* MDCCornerTreatment+CornerTypeInitalizer.m */; }; - DA24453A3C4C0EFDC25A253712BCEFB6 /* BaseChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C3EF2AB0724BFD75D6FE28F5429702 /* BaseChatViewController.swift */; }; - DAF43358D7A8ED2F222994AA7F1EDED6 /* MDCShapedShadowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = FDEEADA23D6F4C3DFE04A4117338D768 /* MDCShapedShadowLayer.m */; }; - DB44F781C343FE7260506125A2B217D5 /* Repeater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F59122698AC6FE35BC3BC2998203C14 /* Repeater.swift */; }; - DB65627B75E519F4B1B65221BA00176A /* CompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EAC9DA9D8A3173E14F6714F6DD597E /* CompositionLayer.swift */; }; - DC21E754F631A9780BEF04CB9EC24C23 /* AnimationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970D404B267F9ADF520FA00EC3700F65 /* AnimationContainer.swift */; }; - DC6BF07D4BE73BA3084613035CD88F56 /* Mask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77EF08C7D4C2345726A07A8B9A265452 /* Mask.swift */; }; - DCD0C33A2B50811D53CF68F021284B47 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931593E088B694A7D6B0D1FC4FE6C080 /* DispatchQueue+Alamofire.swift */; }; - DD58A00EACBEE274C381B491519C6B8C /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1E20D6B957EF263B8B500DA4F13C39 /* RedirectHandler.swift */; }; - DEBE3EC1FC8DCA47B2FFC71153B0636B /* LayerFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D7B9CC58E107433D11681261211BE1 /* LayerFontProvider.swift */; }; - DF4526CA0220910B45398C91C207AB2A /* MDCElevatable.h in Headers */ = {isa = PBXBuildFile; fileRef = 26833FC1284ED4F21560352EB2FA83B1 /* MDCElevatable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E06D3EE4B89C14DF0F1EACA6DB9DCF87 /* UICollectionViewController+MDCCardReordering.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FF269172DE0E3442E369BAF00EAB3CD /* UICollectionViewController+MDCCardReordering.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E0C65E16219718869CD2AFCA2C5465CB /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7800E126D44EA6D8FC842E5214971EF6 /* Request.swift */; }; - E1149D3565B395487CF0EB4487448957 /* MDCShapedView.m in Sources */ = {isa = PBXBuildFile; fileRef = BBF566D92B960CE6B69285672FDE16A8 /* MDCShapedView.m */; }; - E1769C267E82B0C24FE0FFBF949F0A6E /* StringEncoding+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF3276002056A8603CF35E5D0FDBB3B /* StringEncoding+Alamofire.swift */; }; - E1B1195009B9F9BD2E5EFE2113CFE20A /* MaterialRipple.h in Headers */ = {isa = PBXBuildFile; fileRef = 33FE8470F10646E30DF3366F83774F58 /* MaterialRipple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E2BFA4A6A408809ECF183418A7368E1C /* PathElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096CBFAE6AD4730265460085F9186006 /* PathElement.swift */; }; - E43BAEAC9F260161234AFCA6EE7CA2B9 /* MaterialShadowElevationsDummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D0F908B38DD2B1F35BBC0B8F214C6DC0 /* MaterialShadowElevationsDummy.m */; }; - E46C272C4B95CBC92CDA9A2519D37833 /* MDFTextAccessibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C2F6B0AED78566F963846D8D10A8CC9 /* MDFTextAccessibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E47B9E036187569C65F248B6D527020E /* MDCInkViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 42C1DD5860EC1699A757C9BAB12A66D7 /* MDCInkViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E56C430EEEEAF6E39FF0942CCD78A061 /* UIView+MaterialRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */; }; - E5F04CB5F20F4E13DCFBC88AB7506E55 /* UIFont+MaterialTypography.m in Sources */ = {isa = PBXBuildFile; fileRef = D390D3DEF1579285A22574E5E6EA97D0 /* UIFont+MaterialTypography.m */; }; - E70A755D57CC8683634F611358142A89 /* MDCMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9A7C0A475863CB257C9866B1EB05E /* MDCMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E80D58FB9E5F025ED34C4817E1D03D20 /* MDCCurvedRectShapeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = DB1E998F48C65751461A4CB1E13E45C5 /* MDCCurvedRectShapeGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E857ADCAD7B647883D5B2AEC3F16D1D5 /* URLSessionConfiguration+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6587D8532A9E9E13BA4B37E713566C3B /* URLSessionConfiguration+Alamofire.swift */; }; - E990970757CDEBE53FA26AA40A17BD7E /* MaterialIcons+ic_check_circle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AF20B708D8368FA42456BB1F0DF92F7 /* MaterialIcons+ic_check_circle.m */; }; - E9CD4CB2CB433D9920922378F7886DAF /* MDCAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 3647DF7F6475E74ED3EB0CD6926B63DB /* MDCAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EAAFAB74BE099CA0217F13D56892CC50 /* MDFTextAccessibility-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 97A0DF460C679110DBBD0ED8B315B64D /* MDFTextAccessibility-Bridging-Header.h */; settings = {ATTRIBUTES = (Project, ); }; }; - EB0960E12A2CB2EDF98C11A39F506A15 /* InvertedMatteLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1B1AAE95B01FE76CC5E0D131135D20 /* InvertedMatteLayer.swift */; }; - EB1C650699E9DA20023D24CCB95944D4 /* CompoundBezierPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE0D77753F90766C6F5C64AAADA005 /* CompoundBezierPath.swift */; }; - EB4388F68E8E658447BDAC97AE5805C2 /* NullCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E0BB0AD007791BD42D66DF1F4DB387 /* NullCompositionLayer.swift */; }; - EC11B17DA78F7EEBEBC3EFAF68C6DF9F /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6684560C13B020634CFCE92295A15B72 /* Session.swift */; }; - EC2275BC165A2F54AC3498E0FDB085C9 /* AnimationImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F711AC4E23408CDD020388C5E5D71009 /* AnimationImageProvider.swift */; }; - EC9E770804940B15285BD22E29A6FCBA /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D5C4656A9953B89898D46D2942F6E89 /* UIColorExtension.swift */; }; - ED10CCA16008FE6450460EADE24C758B /* KeyboardTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97374F6103D866145B36386C14D1D8E5 /* KeyboardTracker.swift */; }; - EDC23A30E84A45747FC576F67901CCE3 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9DE4BB5450E42050B34C1355AB7E72 /* Utils.swift */; }; - EED90CBEBBCB86F3FE4215070F7737E0 /* MDCShapeMediator.m in Sources */ = {isa = PBXBuildFile; fileRef = F6BB438AED2F3B6F61B65BEBABF41B1F /* MDCShapeMediator.m */; }; - F03C958A2DC83E362F4E76C1758F2E99 /* UIColor+MaterialDynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = B829E8E11D02D1970F8C8FF989A82B64 /* UIColor+MaterialDynamic.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F0501EA15D82306F07F094CA2558130F /* BaseChatViewController+Presenters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0594042CC23BC8F1D7592815BDBFC2C6 /* BaseChatViewController+Presenters.swift */; }; - F129D1B53CBB7D4F4F19F7C651C9EF04 /* MDCShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = 9625E67A414049AFFBCF73EDB3F00B1B /* MDCShadow.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F1665937EE88175C13BD066950658EDB /* LayerDebugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C30F85DF0D50528EF9BEE8A2BA482034 /* LayerDebugging.swift */; }; - F337417E09334F51565A55905275A05E /* MDCStatefulRippleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6AA23B87CFACBF3F6DA8E1FFE48A3A /* MDCStatefulRippleView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F363D238E6734C6503D7E8AACFB6F01A /* UIView+MaterialElevationResponding.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EA8C38C23FD8FDD5FE62F2CFD7B55B9 /* UIView+MaterialElevationResponding.m */; }; - F36D96A4346C90A2D11CB3B6A2ECF4CF /* AuthenticationInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB094FC5A0B58CDEC708A4D9A8189F89 /* AuthenticationInterceptor.swift */; }; - F457424CFB7849798D80D62A5EF98BB0 /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3D75AB0F4CDA59E7BB16BD81745B956 /* Shape.swift */; }; - F45BE4AD9E22109487817B3E369E8774 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 617F2D7F52382550906F7F058A9681F9 /* UIKit.framework */; }; - F59346B37473C393BC5B9573CA75D311 /* MDCFlatButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 985D00F7F6511C37B9DECF5DBEB3B451 /* MDCFlatButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F5D2A31C7EB1DE010771140B6E7ABAD8 /* URLEncodedFormEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D28FDB1911F3BD71EEBE077FA899488 /* URLEncodedFormEncoder.swift */; }; - F63BE0585331CAA3482EF736803F8243 /* RequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097A230A81C9305A1D7CAF969BD3B009 /* RequestInterceptor.swift */; }; - F659E0EBE31959B1758B3628AB4720B4 /* AnimatorNodeDebugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6DBEEB82DA69272704408A3AEBA6EC /* AnimatorNodeDebugging.swift */; }; - F79954C7D48B356B32ACA8BDDFDD183E /* LayerTransformNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B246A845EEBC2196E07F54E35ACA25A /* LayerTransformNode.swift */; }; - F8460445640FB3C461E58C1DFA2E0F16 /* AnyNodeProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678552B49FF59718A815C8CB72951E75 /* AnyNodeProperty.swift */; }; - F95134B340BDE8537CEDC91162AE776B /* UIImage+MaterialRTL.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */; }; - FAF429A0D030A7708AC3E1C890CDFBBC /* MDCInkLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43E371B84D82B4BE12084C4D22A945 /* MDCInkLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - FBBCCDCCAFD735DA076AEC214B31DEBE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */; }; - FC622CB054985417281FE0FB6755C6DD /* MaterialAnimationTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = B443745DA64B8F614899680B793F7108 /* MaterialAnimationTiming.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FCA5FAFF46860FFD052B5A96A4253915 /* UIFontDescriptor+MaterialTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 4040341BD83B0FAE1A20FF1D71955BB5 /* UIFontDescriptor+MaterialTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FDE49FD4B92B698131BA846A877E4F5C /* MaterialTypography.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FA60992213EE87D4775A402EF9DE39B /* MaterialTypography.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FEDBAD32E2EDA85AD6E362B82892A74A /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854600D850F2C3A98DF50071F6F04C09 /* Alamofire.swift */; }; - FF11F9BF6D01BF9AFD49B1831C74E77F /* lottie-ios-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E5379887AF991A23AF0071E5BBA66A3 /* lottie-ios-dummy.m */; }; - FF82E104EC48B26651A60B944BFFA297 /* GradientFillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619B96C839D9C1480A0A40E988C63AFD /* GradientFillRenderer.swift */; }; - FFFCB73998E59CCA79007CF915D56E22 /* Chatto-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DE1B915229A73A86A29689D51EBCEE8E /* Chatto-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 09ADAB9C1EBB34AFF27286BC540ECBF9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 7F4022CD4249985E761F745058CAB372; - remoteInfo = "MaterialComponents-MaterialIcons_ic_check_circle"; - }; - 1E07159C86DCD357DDD6438B3A657BB7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = CB5AA7A4FA94F4765AE579D6F4491956; - remoteInfo = "Pods-ios"; - }; - 37B8EC58BCB778F91D6C37656EDA3111 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2508BC3B908322C9E85E61AE743C9842; - remoteInfo = Chatto; - }; - 547A50F181286054E7D1AD178F2C9D36 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0B967D7F8561D42493EE289EC8D450D1; - remoteInfo = "lottie-ios"; - }; - 6ED33D3BA173DAE7C34C14C6974F4E02 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = EAAA1AD3A8A1B59AB91319EE40752C6D; - remoteInfo = Alamofire; - }; - 7678DE8865A337D774511B701E4FB23B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 55A01F8E987A6E2F7F0ED141A1ECA406; - remoteInfo = MDFInternationalization; - }; - 86D033B1EAA6DB4A9250A038CF62AF07 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = B4723B5744013DF08BE3A5FEAA286944; - remoteInfo = MaterialComponents; - }; - 8AAE22C70F1836AC9C87C67A00D249D2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 55A01F8E987A6E2F7F0ED141A1ECA406; - remoteInfo = MDFInternationalization; - }; - CCFB263D6803CCB26550A6888F18657A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = A8E4C927B2E899954BC2FD3314BCA9DA; - remoteInfo = MDFTextAccessibility; - }; - D397BCB4986AFF763539DD0013C5417D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = A8E4C927B2E899954BC2FD3314BCA9DA; - remoteInfo = MDFTextAccessibility; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 85B3B64C90CFC972449F93DC4A812BBD /* Copy . Public Headers */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/."; - dstSubfolderSpec = 16; - files = ( - 64317603918517CB58BC50355BBD14F8 /* MDFInternationalization.h in Copy . Public Headers */, - 7DB6497C9CC06CA922AC1152CE38CDAC /* MDFRTL.h in Copy . Public Headers */, - 3DCCF761B72AB77C921C6347AD39D34E /* NSLocale+MaterialRTL.h in Copy . Public Headers */, - 546594038A39C1C5480919C5C51FFF46 /* NSString+MaterialBidi.h in Copy . Public Headers */, - C872E018E08E0FD0B30B386614AB0E43 /* UIImage+MaterialRTL.h in Copy . Public Headers */, - 8EFABEE2E3BAE5BD9B93C1B58A69A178 /* UIView+MaterialRTL.h in Copy . Public Headers */, - ); - name = "Copy . Public Headers"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0056C47896966D18A6A1A5446B66D105 /* MaterialShadowElevations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShadowElevations.h; path = components/ShadowElevations/src/MaterialShadowElevations.h; sourceTree = ""; }; - 02F95682318FB0084CD0FAE9E698A337 /* MDCInkTouchController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkTouchController.m; path = components/Ink/src/MDCInkTouchController.m; sourceTree = ""; }; - 0314F72C05B7FF63F27BE4646BD8305E /* BaseChatViewController+Scrolling.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+Scrolling.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+Scrolling.swift"; sourceTree = ""; }; - 039BA34B5A5B9FE2B7971FE6E64557BB /* UIFont+MaterialSimpleEquality.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialSimpleEquality.m"; path = "components/Typography/src/UIFont+MaterialSimpleEquality.m"; sourceTree = ""; }; - 058498847739D73FC959AC6FAC7A0D9F /* GroupOutputNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupOutputNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift; sourceTree = ""; }; - 0594042CC23BC8F1D7592815BDBFC2C6 /* BaseChatViewController+Presenters.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+Presenters.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+Presenters.swift"; sourceTree = ""; }; - 06460E2CD793BBF2C535219D13A50C05 /* ChatItemPresenterFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemPresenterFactory.swift; path = Chatto/Source/ChatController/Collaborators/ChatItemPresenterFactory.swift; sourceTree = ""; }; - 096CBFAE6AD4730265460085F9186006 /* PathElement.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathElement.swift; path = Sources/Private/Utility/Primitives/PathElement.swift; sourceTree = ""; }; - 097A230A81C9305A1D7CAF969BD3B009 /* RequestInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestInterceptor.swift; path = Source/RequestInterceptor.swift; sourceTree = ""; }; - 0A5DF95E38B16446E73BCFAC6F53DA2E /* AnimationTime.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationTime.swift; path = Sources/Public/Primitives/AnimationTime.swift; sourceTree = ""; }; - 0C07D8193799950468B6CF3885E1193B /* Pods-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ios.debug.xcconfig"; sourceTree = ""; }; - 0D37C9BDBB809F1F5B2D0212FF9CFE99 /* MDCFontScaler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFontScaler.m; path = components/Typography/src/MDCFontScaler.m; sourceTree = ""; }; - 0D46DD556D1F791A1853E2A6E2506FF6 /* MDCCard.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCard.m; path = components/Cards/src/MDCCard.m; sourceTree = ""; }; - 0EA7FAE72F27B4A66C2CAEEC25C31634 /* MDFTextAccessibility.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFTextAccessibility.release.xcconfig; sourceTree = ""; }; - 0F40483DF444914DE771891BCED1F593 /* ReplyFeedbackGenerator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReplyFeedbackGenerator.swift; path = Chatto/Source/ChatController/Collaborators/ReplyFeedbackGenerator.swift; sourceTree = ""; }; - 0F59122698AC6FE35BC3BC2998203C14 /* Repeater.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Repeater.swift; path = Sources/Private/Model/ShapeItems/Repeater.swift; sourceTree = ""; }; - 0FC5A268C4EB4A715BD6C5613A37B5A9 /* KeypathSearchable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeypathSearchable.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift; sourceTree = ""; }; - 100C94E08EE07B09E19F115713E5B184 /* Pods-ios-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-ios-dummy.m"; sourceTree = ""; }; - 111FAD1AC980B2D67B27016C087B61ED /* KeyedDecodingContainerExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyedDecodingContainerExtensions.swift; path = Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift; sourceTree = ""; }; - 11A09793C107A7840B6234B0400A787E /* MDCCard.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCard.h; path = components/Cards/src/MDCCard.h; sourceTree = ""; }; - 14889A17A94EB87B76300716DD2A9F36 /* MDFTextAccessibility.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFTextAccessibility.m; path = src/MDFTextAccessibility.m; sourceTree = ""; }; - 1495FD5410C5BA9748915AE95726D978 /* UIColor+MaterialBlending.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MaterialBlending.m"; path = "components/private/Color/src/UIColor+MaterialBlending.m"; sourceTree = ""; }; - 1555D5D35385492B8D027E811BE666CB /* UIColor+MaterialDynamic.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MaterialDynamic.m"; path = "components/private/Color/src/UIColor+MaterialDynamic.m"; sourceTree = ""; }; - 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MaterialRTL.h"; path = "Sources/UIView+MaterialRTL.h"; sourceTree = ""; }; - 16900192B6F1F880A2BEEE4A15228EE3 /* MDCLegacyInkLayer+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCLegacyInkLayer+Private.h"; path = "components/Ink/src/private/MDCLegacyInkLayer+Private.h"; sourceTree = ""; }; - 1742EDF4DDCBB582B88771AEB1201E1A /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; - 174A76F8A707305878153E63E7AF69F3 /* KeyframeInterpolator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyframeInterpolator.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift; sourceTree = ""; }; - 187B43272E4EAEF4B35EA65BBA8B1334 /* SolidLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SolidLayerModel.swift; path = Sources/Private/Model/Layers/SolidLayerModel.swift; sourceTree = ""; }; - 18C8E244ED61A9B57F738C5DB46FCC12 /* TextCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift; sourceTree = ""; }; - 1962BFA68BF11D74BDD363384439BAC5 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = ""; }; - 19E09DB898B5D3E34265E0EE3D2FB559 /* MDCEdgeTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCEdgeTreatment.m; path = components/Shapes/src/MDCEdgeTreatment.m; sourceTree = ""; }; - 1A09CD4BB399191B8387A7D72E4B2909 /* MDCCurvedCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCurvedCornerTreatment.h; path = components/ShapeLibrary/src/MDCCurvedCornerTreatment.h; sourceTree = ""; }; - 1A85C401C2117402508517940B47B11D /* NSLocale+MaterialRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSLocale+MaterialRTL.m"; path = "Sources/NSLocale+MaterialRTL.m"; sourceTree = ""; }; - 1A8B9BF018FD470AE5EBE4F9FF3808CD /* ServerTrustEvaluation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustEvaluation.swift; path = Source/ServerTrustEvaluation.swift; sourceTree = ""; }; - 1A9AE3381193F742879F523CCE471749 /* lottie-ios.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "lottie-ios.modulemap"; sourceTree = ""; }; - 1B239243820382C1C6E7038645E10958 /* MDCFloatingButtonModeAnimatorDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFloatingButtonModeAnimatorDelegate.h; path = components/Buttons/src/private/MDCFloatingButtonModeAnimatorDelegate.h; sourceTree = ""; }; - 1BD15A4D6488FC7DCA7F0F18E0CAC1B9 /* MDCStatefulRippleView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCStatefulRippleView.m; path = components/Ripple/src/MDCStatefulRippleView.m; sourceTree = ""; }; - 1C0B52C8F35A67626A42F0430F36E070 /* ColorValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ColorValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift; sourceTree = ""; }; - 1CBA47FDA3E910C7EDFE324EFA897551 /* lottie-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "lottie-ios.release.xcconfig"; sourceTree = ""; }; - 1CEC781B13B51E108C5658001DCCCD2A /* Pods-iosTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iosTests-umbrella.h"; sourceTree = ""; }; - 1D76179FC8700321E982CBF63CBE974C /* MDFInternationalization-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MDFInternationalization-Info.plist"; sourceTree = ""; }; - 1E1B1AAE95B01FE76CC5E0D131135D20 /* InvertedMatteLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InvertedMatteLayer.swift; path = Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift; sourceTree = ""; }; - 1E5379887AF991A23AF0071E5BBA66A3 /* lottie-ios-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "lottie-ios-dummy.m"; sourceTree = ""; }; - 1E786DB3BAA72871D457F5DC195A08AD /* MDCEdgeTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCEdgeTreatment.h; path = components/Shapes/src/MDCEdgeTreatment.h; sourceTree = ""; }; - 1E9DE4BB5450E42050B34C1355AB7E72 /* Utils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = Chatto/Source/Utils.swift; sourceTree = ""; }; - 2102D1FE11630B5091FE865DDD43F05A /* MDCPillShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCPillShapeGenerator.m; path = components/ShapeLibrary/src/MDCPillShapeGenerator.m; sourceTree = ""; }; - 21ABC4AABBA66B5F87EF5A5C789FDC8F /* lottie-ios-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "lottie-ios-Info.plist"; sourceTree = ""; }; - 21FFD3AD6BFF43E494BD4BD3E560F4A9 /* HTTPHeaders.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HTTPHeaders.swift; path = Source/HTTPHeaders.swift; sourceTree = ""; }; - 229265038E32F623E46E6B51D41CC33C /* MDCInkTouchControllerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkTouchControllerDelegate.h; path = components/Ink/src/MDCInkTouchControllerDelegate.h; sourceTree = ""; }; - 22BBD822E6408AD8D9D1D46DBCBA9381 /* TextLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextLayer.swift; path = Sources/Private/LayerContainers/Utility/TextLayer.swift; sourceTree = ""; }; - 246B8D62D146A318E129AB0C450D33BA /* Result+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Result+Alamofire.swift"; path = "Source/Result+Alamofire.swift"; sourceTree = ""; }; - 24866442BD723570188362AFBC61ECA3 /* MDCCornerTreatment+CornerTypeInitalizer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MDCCornerTreatment+CornerTypeInitalizer.m"; path = "components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.m"; sourceTree = ""; }; - 25A7B53BBB25EA665175DB0281DBDE11 /* Pods-ios-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ios-frameworks.sh"; sourceTree = ""; }; - 26833FC1284ED4F21560352EB2FA83B1 /* MDCElevatable.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCElevatable.h; path = components/Elevation/src/MDCElevatable.h; sourceTree = ""; }; - 275AD0BC20557E9DC939C53DFF5BB2F3 /* MDCFontTraits.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFontTraits.h; path = components/Typography/src/private/MDCFontTraits.h; sourceTree = ""; }; - 27B3D295F42CEAEA35B48C43AD477C61 /* MDCShadow.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShadow.m; path = components/Shadow/src/MDCShadow.m; sourceTree = ""; }; - 2882BFF487CA5879D2F68C6B944595F5 /* MDCPillShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCPillShapeGenerator.h; path = components/ShapeLibrary/src/MDCPillShapeGenerator.h; sourceTree = ""; }; - 2931B1BB799911DD00AA57C202BA2A6F /* MDCRippleLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRippleLayer.m; path = components/Ripple/src/private/MDCRippleLayer.m; sourceTree = ""; }; - 2AB8B4FEB07692832F02CED08212015A /* UIFont+MaterialSimpleEquality.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialSimpleEquality.h"; path = "components/Typography/src/UIFont+MaterialSimpleEquality.h"; sourceTree = ""; }; - 2AD410EF9494BB5D4662CBBE59C2C06C /* VectorsExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = VectorsExtensions.swift; path = Sources/Private/Utility/Primitives/VectorsExtensions.swift; sourceTree = ""; }; - 2AF20B708D8368FA42456BB1F0DF92F7 /* MaterialIcons+ic_check_circle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MaterialIcons+ic_check_circle.m"; path = "components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.m"; sourceTree = ""; }; - 2B1E20D6B957EF263B8B500DA4F13C39 /* RedirectHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RedirectHandler.swift; path = Source/RedirectHandler.swift; sourceTree = ""; }; - 2B2BD90586E1B7C4656D777C09F8F99D /* MDCCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCornerTreatment.m; path = components/Shapes/src/MDCCornerTreatment.m; sourceTree = ""; }; - 2C197D9AE91D805F5FFDD745A3332768 /* CellPanGestureHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CellPanGestureHandler.swift; path = Chatto/Source/ChatController/Collaborators/CellPanGestureHandler.swift; sourceTree = ""; }; - 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+MaterialRTL.h"; path = "Sources/UIImage+MaterialRTL.h"; sourceTree = ""; }; - 2D6557B599662265675CB317D7A3AA35 /* Animation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Animation.swift; path = Sources/Private/Model/Animation.swift; sourceTree = ""; }; - 2EE66424995BD90C997E2CEA9946F2AA /* PreCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PreCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift; sourceTree = ""; }; - 2FA60992213EE87D4775A402EF9DE39B /* MaterialTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialTypography.h; path = components/Typography/src/MaterialTypography.h; sourceTree = ""; }; - 2FB631B982623359A1D4E17E34F6369B /* ChatItemCompanionCollection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemCompanionCollection.swift; path = Chatto/Source/ChatItemCompanionCollection.swift; sourceTree = ""; }; - 30734D2F4660366E1C28FA6E2E4345B1 /* OperationQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "OperationQueue+Alamofire.swift"; path = "Source/OperationQueue+Alamofire.swift"; sourceTree = ""; }; - 30C3EF2AB0724BFD75D6FE28F5429702 /* BaseChatViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseChatViewController.swift; path = Chatto/Source/ChatController/BaseChatViewController.swift; sourceTree = ""; }; - 3117010738ED3E7B5080810EBF99FBBD /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; - 3198F273FE8A5D0B39F6AA64EB844FAD /* MaterialComponents.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MaterialComponents.modulemap; sourceTree = ""; }; - 31D7840BB6B99AC092EAB4942FDD3F53 /* AssetLibrary.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssetLibrary.swift; path = Sources/Private/Model/Assets/AssetLibrary.swift; sourceTree = ""; }; - 31F194A86821D5801BEACFA2DADF397D /* MDCRippleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleView.h; path = components/Ripple/src/MDCRippleView.h; sourceTree = ""; }; - 32E84BC23BD4ED514C1BB73378A0EF16 /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = ""; }; - 33FE8470F10646E30DF3366F83774F58 /* MaterialRipple.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialRipple.h; path = components/Ripple/src/MaterialRipple.h; sourceTree = ""; }; - 341DED9C8A182716EE1504F58E3BBA27 /* Pods-iosTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosTests-acknowledgements.plist"; sourceTree = ""; }; - 34EBCCA2E4424A06FB1C11FFF86AE0E2 /* MDCSlantedRectShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCSlantedRectShapeGenerator.h; path = components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.h; sourceTree = ""; }; - 35A0FCAED698C32DB774702E78F6A9BB /* ImageCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift; sourceTree = ""; }; - 35BD0FEEB2EF9ADE87455CB6949E56DC /* LayerImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerImageProvider.swift; path = Sources/Private/LayerContainers/Utility/LayerImageProvider.swift; sourceTree = ""; }; - 3647DF7F6475E74ED3EB0CD6926B63DB /* MDCAvailability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCAvailability.h; path = components/Availability/src/MDCAvailability.h; sourceTree = ""; }; - 37F289E6F2E521F6C7C3C1203689674C /* CAMediaTimingFunction+MDCAnimationTiming.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "CAMediaTimingFunction+MDCAnimationTiming.h"; path = "components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.h"; sourceTree = ""; }; - 3984A2B5195B698FF523B7D82CD694F0 /* Keyframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Keyframe.swift; path = Sources/Private/Model/Keyframes/Keyframe.swift; sourceTree = ""; }; - 3A16E9C361081323779110A2E0EE4F83 /* MDCRoundedCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRoundedCornerTreatment.m; path = components/ShapeLibrary/src/MDCRoundedCornerTreatment.m; sourceTree = ""; }; - 3A7AEC8A27ED687F3745E5FF8E0FEB75 /* MDCIcons.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCIcons.m; path = components/private/Icons/src/MDCIcons.m; sourceTree = ""; }; - 3AEB21337FEDA85FD4CE4B5534D436F9 /* BezierPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BezierPath.swift; path = Sources/Private/Utility/Primitives/BezierPath.swift; sourceTree = ""; }; - 3B5993FADA59638CD437C1C18E3DDA1D /* MDCTriangleEdgeTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCTriangleEdgeTreatment.m; path = components/ShapeLibrary/src/MDCTriangleEdgeTreatment.m; sourceTree = ""; }; - 3BEEEC76296F936D8FA70CA0604BB74F /* MDFColorCalculations.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFColorCalculations.m; path = src/private/MDFColorCalculations.m; sourceTree = ""; }; - 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "MaterialComponents-MaterialIcons_ic_check_circle"; path = MaterialIcons_ic_check_circle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - 3CDCB1C8D529F8DE664D2E4C96BD3A7D /* Alamofire.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.release.xcconfig; sourceTree = ""; }; - 3D28FDB1911F3BD71EEBE077FA899488 /* URLEncodedFormEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLEncodedFormEncoder.swift; path = Source/URLEncodedFormEncoder.swift; sourceTree = ""; }; - 3D5C4656A9953B89898D46D2942F6E89 /* UIColorExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIColorExtension.swift; path = Sources/Public/iOS/UIColorExtension.swift; sourceTree = ""; }; - 3D6F69E4E368E5F576A3D6AF24022D2D /* CompatibleAnimationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompatibleAnimationView.swift; path = Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift; sourceTree = ""; }; - 3D884B74C43A162D8B9756985E116E84 /* ImageAsset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageAsset.swift; path = Sources/Private/Model/Assets/ImageAsset.swift; sourceTree = ""; }; - 3DCA91058EFB02847D0DA47F4F3195AC /* MDCLegacyInkLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCLegacyInkLayerDelegate.h; path = components/Ink/src/private/MDCLegacyInkLayerDelegate.h; sourceTree = ""; }; - 3E7BEEB423FD0BB8206D7833EAFB5A94 /* MaterialComponents-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MaterialComponents-prefix.pch"; sourceTree = ""; }; - 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MaterialComponents.debug.xcconfig; sourceTree = ""; }; - 3EEB4FCAEBF9555B90A5EC48FE3D6B4F /* MDCSlantedRectShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCSlantedRectShapeGenerator.m; path = components/ShapeLibrary/src/MDCSlantedRectShapeGenerator.m; sourceTree = ""; }; - 3F6AA23B87CFACBF3F6DA8E1FFE48A3A /* MDCStatefulRippleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCStatefulRippleView.h; path = components/Ripple/src/MDCStatefulRippleView.h; sourceTree = ""; }; - 3FCF28462DDA6182768A18DEE727FA6C /* MDCIcons+BundleLoader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCIcons+BundleLoader.h"; path = "components/private/Icons/src/MDCIcons+BundleLoader.h"; sourceTree = ""; }; - 4040341BD83B0FAE1A20FF1D71955BB5 /* UIFontDescriptor+MaterialTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFontDescriptor+MaterialTypography.h"; path = "components/Typography/src/UIFontDescriptor+MaterialTypography.h"; sourceTree = ""; }; - 408FEF5380BBEF2A9A4EF9878452691D /* MDFImageCalculations.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFImageCalculations.m; path = src/private/MDFImageCalculations.m; sourceTree = ""; }; - 40FC4A25E6CBE6CD94D65C7642079986 /* MDCCornerTreatment+CornerTypeInitalizer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCCornerTreatment+CornerTypeInitalizer.h"; path = "components/ShapeLibrary/src/MDCCornerTreatment+CornerTypeInitalizer.h"; sourceTree = ""; }; - 4148219036DBEF09983EE603792119B1 /* MDCFloatingButtonModeAnimator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFloatingButtonModeAnimator.h; path = components/Buttons/src/private/MDCFloatingButtonModeAnimator.h; sourceTree = ""; }; - 4157CBEADF18154FD243E39C50468258 /* MDCShapedView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapedView.h; path = components/Shapes/src/MDCShapedView.h; sourceTree = ""; }; - 4259483CFD0AC1C7F1295B7B354781A1 /* AnimationKeypath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationKeypath.swift; path = Sources/Public/DynamicProperties/AnimationKeypath.swift; sourceTree = ""; }; - 427AFF770A87FFC8BA489ECCD299D58F /* MaterialIcons_ic_check_circle.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = MaterialIcons_ic_check_circle.xcassets; path = components/private/Icons/icons/ic_check_circle/src/MaterialIcons_ic_check_circle.xcassets; sourceTree = ""; }; - 42A318810EC6F37DE9379EF21DE434C3 /* MDCFontScaler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFontScaler.h; path = components/Typography/src/MDCFontScaler.h; sourceTree = ""; }; - 42C1DD5860EC1699A757C9BAB12A66D7 /* MDCInkViewDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkViewDelegate.h; path = components/Ink/src/MDCInkViewDelegate.h; sourceTree = ""; }; - 43693CCEC265B75D660F71A9422AC97E /* MDCCutCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCutCornerTreatment.h; path = components/ShapeLibrary/src/MDCCutCornerTreatment.h; sourceTree = ""; }; - 43E52D204C785271C19FE614C61534A4 /* MDFInternationalization.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFInternationalization.release.xcconfig; sourceTree = ""; }; - 46AE7E00528585497EA59CD94F669E3A /* MDCShapeGenerating.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapeGenerating.h; path = components/Shapes/src/MDCShapeGenerating.h; sourceTree = ""; }; - 4838FCADE32426C4BC81299CE2906DE1 /* MDCFloatingButton+Animation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCFloatingButton+Animation.h"; path = "components/Buttons/src/MDCFloatingButton+Animation.h"; sourceTree = ""; }; - 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFRTL.h; path = Sources/MDFRTL.h; sourceTree = ""; }; - 49FB87A2C6705951A697BFD94E3FFB81 /* MDCTypographyUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCTypographyUtilities.m; path = components/Typography/src/private/MDCTypographyUtilities.m; sourceTree = ""; }; - 4ADBAC831F15371103E6075A7099776A /* NSArray+MDFUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+MDFUtils.h"; path = "src/private/NSArray+MDFUtils.h"; sourceTree = ""; }; - 4AE9A7C0A475863CB257C9866B1EB05E /* MDCMath.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCMath.h; path = components/private/Math/src/MDCMath.h; sourceTree = ""; }; - 4BF5C7C1F8F18927BF9A4EBDE9886621 /* MDCRaisedButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRaisedButton.h; path = components/Buttons/src/MDCRaisedButton.h; sourceTree = ""; }; - 4C628C67659ABBADF02BA056396F87E1 /* MDCButton+Subclassing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDCButton+Subclassing.h"; path = "components/Buttons/src/private/MDCButton+Subclassing.h"; sourceTree = ""; }; - 4DE016D373F57DCED6E20186B20A6596 /* UIImage+MaterialRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+MaterialRTL.m"; path = "Sources/UIImage+MaterialRTL.m"; sourceTree = ""; }; - 4E0B142ACFD6D223F820B0A902B17ADB /* MDCShapedShadowLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapedShadowLayer.h; path = components/Shapes/src/MDCShapedShadowLayer.h; sourceTree = ""; }; - 4E2AA82D677A35A05B022A324E49F50C /* MDFTextAccessibility-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MDFTextAccessibility-dummy.m"; sourceTree = ""; }; - 4FF269172DE0E3442E369BAF00EAB3CD /* UICollectionViewController+MDCCardReordering.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UICollectionViewController+MDCCardReordering.h"; path = "components/Cards/src/UICollectionViewController+MDCCardReordering.h"; sourceTree = ""; }; - 51BA97E8B5085EFFB47BC9C0B785CEA7 /* lottie-ios */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "lottie-ios"; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5241F42DE93198A9743B520528DEBE0E /* MaskContainerLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MaskContainerLayer.swift; path = Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift; sourceTree = ""; }; - 525767FD21D481C033ED537000C2A5B1 /* LayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerModel.swift; path = Sources/Private/Model/Layers/LayerModel.swift; sourceTree = ""; }; - 5264280FEB93B40117162EB11A934890 /* MDCTypography.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCTypography.m; path = components/Typography/src/MDCTypography.m; sourceTree = ""; }; - 5278EC208A2BA0E35B57E4AE2C020987 /* MDFInternationalization-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFInternationalization-prefix.pch"; sourceTree = ""; }; - 536C19C18245423A6AFB8166C8F93F35 /* SerialTaskQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SerialTaskQueue.swift; path = Chatto/Source/SerialTaskQueue.swift; sourceTree = ""; }; - 553199DA713332B2A7CE2BC1B053344C /* GradientValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift; sourceTree = ""; }; - 55C5A1F93EFADC2096EE6DC9303B38A8 /* NodeProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NodeProperty.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift; sourceTree = ""; }; - 561E46EAC325F966EA9DBA55F0689607 /* MDCShadowLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShadowLayer.m; path = components/ShadowLayer/src/MDCShadowLayer.m; sourceTree = ""; }; - 5739897ED45AAC422F0EECBA1D75F58A /* GroupInterpolator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupInterpolator.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift; sourceTree = ""; }; - 57C4846B514755F166442D8D10CEA565 /* URLConvertible+URLRequestConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLConvertible+URLRequestConvertible.swift"; path = "Source/URLConvertible+URLRequestConvertible.swift"; sourceTree = ""; }; - 5923674729AC49BE14806A1AEB0FB8DE /* Pods-iosTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosTests-Info.plist"; sourceTree = ""; }; - 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+MaterialBidi.h"; path = "Sources/NSString+MaterialBidi.h"; sourceTree = ""; }; - 5AF2CED0DB3829D4D4BE54A78C1072DB /* NodePropertyMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NodePropertyMap.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift; sourceTree = ""; }; - 5BA153CC755CD4895140457FB4274997 /* MDCShadowElevations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadowElevations.h; path = components/ShadowElevations/src/MDCShadowElevations.h; sourceTree = ""; }; - 5BAC390311C727CE7C129F84BD9B09B9 /* AnimationSubview.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationSubview.swift; path = Sources/Public/iOS/AnimationSubview.swift; sourceTree = ""; }; - 5D06BA269EAAEF5A59A416177B7571E8 /* MDFTextAccessibility-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFTextAccessibility-prefix.pch"; sourceTree = ""; }; - 5D714F498A6A5E7FC8FB92A3819A449A /* MDCShadowsCollection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShadowsCollection.m; path = components/Shadow/src/MDCShadowsCollection.m; sourceTree = ""; }; - 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5DC202126AC7DEA521D46EACE0A8CB33 /* MDCRippleTouchController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRippleTouchController.m; path = components/Ripple/src/MDCRippleTouchController.m; sourceTree = ""; }; - 5DDC53D7CBF3A251E463BB224DD12DCE /* TextDocument.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextDocument.swift; path = Sources/Private/Model/Text/TextDocument.swift; sourceTree = ""; }; - 5EA8C38C23FD8FDD5FE62F2CFD7B55B9 /* UIView+MaterialElevationResponding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MaterialElevationResponding.m"; path = "components/Elevation/src/UIView+MaterialElevationResponding.m"; sourceTree = ""; }; - 5FB83587411063DC53155C1A2C082DD3 /* MDCRectangleShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRectangleShapeGenerator.m; path = components/Shapes/src/MDCRectangleShapeGenerator.m; sourceTree = ""; }; - 5FD3683EBDED4FBCF7CF58DBB4864E15 /* MaterialComponents-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MaterialComponents-Info.plist"; sourceTree = ""; }; - 600ED7A50816D1A3222193C97DEA9BC3 /* MDCLegacyInkLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCLegacyInkLayer.m; path = components/Ink/src/private/MDCLegacyInkLayer.m; sourceTree = ""; }; - 6102438DC85CE0EBEE3E8E37512F0869 /* BaseChatItemPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseChatItemPresenter.swift; path = "Chatto/Source/Chat Items/BaseChatItemPresenter.swift"; sourceTree = ""; }; - 615BC7DD9F6466F845804084D0B12D66 /* lottie-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "lottie-ios.debug.xcconfig"; sourceTree = ""; }; - 617F2D7F52382550906F7F058A9681F9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; - 61814AFE969DF03C54E6B9E503F28302 /* ItemsExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ItemsExtension.swift; path = Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift; sourceTree = ""; }; - 619B96C839D9C1480A0A40E988C63AFD /* GradientFillRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientFillRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift; sourceTree = ""; }; - 62A2F94AFB07146E8A9A5DC4985F19BA /* MDCRippleTouchController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleTouchController.h; path = components/Ripple/src/MDCRippleTouchController.h; sourceTree = ""; }; - 62FE3D47542C17E69E982939D7C4342C /* CGFloatExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CGFloatExtensions.swift; path = Sources/Private/Utility/Extensions/CGFloatExtensions.swift; sourceTree = ""; }; - 631CC31B1866E988A56C286024B8A805 /* lottie-ios-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "lottie-ios-umbrella.h"; sourceTree = ""; }; - 63431295907542E5A9DD8630DC9338BA /* GroupNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift; sourceTree = ""; }; - 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MaterialComponents.release.xcconfig; sourceTree = ""; }; - 64882FB458891751B0C4E20C1C23D5F1 /* FilepathImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FilepathImageProvider.swift; path = Sources/Public/iOS/FilepathImageProvider.swift; sourceTree = ""; }; - 6587D8532A9E9E13BA4B37E713566C3B /* URLSessionConfiguration+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLSessionConfiguration+Alamofire.swift"; path = "Source/URLSessionConfiguration+Alamofire.swift"; sourceTree = ""; }; - 65D2F1380362B7EE62D2678F79650A3A /* MDCRippleView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRippleView.m; path = components/Ripple/src/MDCRippleView.m; sourceTree = ""; }; - 65EAC9DA9D8A3173E14F6714F6DD597E /* CompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift; sourceTree = ""; }; - 6684560C13B020634CFCE92295A15B72 /* Session.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Session.swift; path = Source/Session.swift; sourceTree = ""; }; - 6741DCC6A7B256467254EDFBBDF05B3F /* MaterialComponents-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MaterialComponents-umbrella.h"; sourceTree = ""; }; - 678552B49FF59718A815C8CB72951E75 /* AnyNodeProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyNodeProperty.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift; sourceTree = ""; }; - 6828FC052E6421784F770E0D6078DDB6 /* MDCFlatButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFlatButton.m; path = components/Buttons/src/MDCFlatButton.m; sourceTree = ""; }; - 686DE3CEFB348F67B7F2E0CCDBED00A7 /* FloatValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FloatValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift; sourceTree = ""; }; - 6A035D2874E49F84C6A8FF8FBF6503C8 /* MDCCardCollectionCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCardCollectionCell.m; path = components/Cards/src/MDCCardCollectionCell.m; sourceTree = ""; }; - 6A7B2BF1390CAC2A1A79D280BA2AA739 /* MaterialShapes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShapes.h; path = components/Shapes/src/MaterialShapes.h; sourceTree = ""; }; - 6A9EA755014DD5DD4C27D7ECD93783FD /* DummyChatItemPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DummyChatItemPresenter.swift; path = "Chatto/Source/Chat Items/DummyChatItemPresenter.swift"; sourceTree = ""; }; - 6BB9978D69E4ABC8F7AE8425D9E1F048 /* StringExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StringExtensions.swift; path = Sources/Private/Utility/Extensions/StringExtensions.swift; sourceTree = ""; }; - 6BDB7E8223919BD683DE47719CEA1A69 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = ""; }; - 6C4F064EEF86F1829D1B928D11963739 /* BaseChatViewControllerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseChatViewControllerView.swift; path = Chatto/Source/ChatController/Collaborators/BaseChatViewControllerView.swift; sourceTree = ""; }; - 6E035C3FBB06190E0CE480284B8CF014 /* Group.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Group.swift; path = Sources/Private/Model/ShapeItems/Group.swift; sourceTree = ""; }; - 6E28A208954B54643898470FABAC2905 /* UIFont+MaterialTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialTypography.h"; path = "components/Typography/src/UIFont+MaterialTypography.h"; sourceTree = ""; }; - 6ED311731D2D73DBC56836B34F3D634A /* MDCShadowLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadowLayer.h; path = components/ShadowLayer/src/MDCShadowLayer.h; sourceTree = ""; }; - 6F2AF02B3727FEC99B62286905D9F172 /* Color.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Color.swift; path = Sources/Public/Primitives/Color.swift; sourceTree = ""; }; - 6F5C3BCCB2FBBD3DC6701A99A1EC16E2 /* MDCLegacyInkLayerRippleDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCLegacyInkLayerRippleDelegate.h; path = components/Ink/src/private/MDCLegacyInkLayerRippleDelegate.h; sourceTree = ""; }; - 6F6DE9329322864658D938C211EAFA09 /* MDCRippleLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleLayerDelegate.h; path = components/Ripple/src/private/MDCRippleLayerDelegate.h; sourceTree = ""; }; - 701CF46760379C28084078F7D4BAFF32 /* AlamofireExtended.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AlamofireExtended.swift; path = Source/AlamofireExtended.swift; sourceTree = ""; }; - 7053BE770B19CADCC67259D2058FBFE5 /* MDFImageCalculations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFImageCalculations.h; path = src/private/MDFImageCalculations.h; sourceTree = ""; }; - 708C0285547318992A3989A6415E5D6B /* AnimationContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationContext.swift; path = Sources/Private/Utility/Helpers/AnimationContext.swift; sourceTree = ""; }; - 713A971DE11ED11949FB41259A01995C /* Merge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Merge.swift; path = Sources/Private/Model/ShapeItems/Merge.swift; sourceTree = ""; }; - 72E3FDB351A03387E5608B6C84700D31 /* Pods-iosTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosTests.debug.xcconfig"; sourceTree = ""; }; - 73500719BEBC67E98C2432BA726CF2BD /* UIColor+MaterialBlending.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MaterialBlending.h"; path = "components/private/Color/src/UIColor+MaterialBlending.h"; sourceTree = ""; }; - 73D70DCDE236059947C0BF795B512C84 /* Protected.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Protected.swift; path = Source/Protected.swift; sourceTree = ""; }; - 74405B4230DF6726303FA767434E6A14 /* CurveVertex.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CurveVertex.swift; path = Sources/Private/Utility/Primitives/CurveVertex.swift; sourceTree = ""; }; - 748CA1EDB7765D88F678941AA746AA9E /* CachedResponseHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CachedResponseHandler.swift; path = Source/CachedResponseHandler.swift; sourceTree = ""; }; - 75A55C9E6101E233016AB66DD634B926 /* TextAnimator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextAnimator.swift; path = Sources/Private/Model/Text/TextAnimator.swift; sourceTree = ""; }; - 7639C506BC1DBC0CA831E0E2A106D658 /* TextLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextLayerModel.swift; path = Sources/Private/Model/Layers/TextLayerModel.swift; sourceTree = ""; }; - 76D52B7A10D74C906F9C84CC976B1B2D /* PointValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PointValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift; sourceTree = ""; }; - 777B7794311A16E26C00B24639C365CB /* ChatItemCompanion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemCompanion.swift; path = "Chatto/Source/Chat Items/ChatItemCompanion.swift"; sourceTree = ""; }; - 77EF08C7D4C2345726A07A8B9A265452 /* Mask.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Mask.swift; path = Sources/Private/Model/Objects/Mask.swift; sourceTree = ""; }; - 7800E126D44EA6D8FC842E5214971EF6 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = ""; }; - 7821F9D3A25C026AA6AFACB2A720A953 /* lottie-ios-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "lottie-ios-prefix.pch"; sourceTree = ""; }; - 7872898EA812A796E27F3664AA901902 /* MultipartUpload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartUpload.swift; path = Source/MultipartUpload.swift; sourceTree = ""; }; - 789CAA7B6C1E469B4554820ADC2147D3 /* MDCFontTraits.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFontTraits.m; path = components/Typography/src/private/MDCFontTraits.m; sourceTree = ""; }; - 78E1DDE875714E724C5D0109D3735095 /* AnimatorNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatorNode.swift; path = Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift; sourceTree = ""; }; - 79EA03EF06AA77426C2C84981497D6F8 /* MDCInkView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkView.h; path = components/Ink/src/MDCInkView.h; sourceTree = ""; }; - 7B96F646D146FE43BCB3C820D9573D99 /* ShapeContainerLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeContainerLayer.swift; path = Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift; sourceTree = ""; }; - 7BC6506BF9A73061A58868C0D592B9B1 /* PathNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathNode.swift; path = Sources/Private/NodeRenderSystem/Protocols/PathNode.swift; sourceTree = ""; }; - 7BF3276002056A8603CF35E5D0FDBB3B /* StringEncoding+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "StringEncoding+Alamofire.swift"; path = "Source/StringEncoding+Alamofire.swift"; sourceTree = ""; }; - 7C6DBEEB82DA69272704408A3AEBA6EC /* AnimatorNodeDebugging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatorNodeDebugging.swift; path = Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift; sourceTree = ""; }; - 7CE46363B5ADC47CE9B57A3335A26016 /* MaterialApplication.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialApplication.h; path = components/private/Application/src/MaterialApplication.h; sourceTree = ""; }; - 7DF304359AEBCFADD6BBD420523B223F /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; - 7E145BBB7A0266AF3CEF37E16F141F03 /* ColorExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ColorExtension.swift; path = Sources/Private/Utility/Primitives/ColorExtension.swift; sourceTree = ""; }; - 7E28ED602A75DB92340D7444DB1995A1 /* MDCCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCornerTreatment.h; path = components/Shapes/src/MDCCornerTreatment.h; sourceTree = ""; }; - 7F557E15F0C636BE0C4547A358DA8C93 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = ""; }; - 7FA25E16755D429C6CC771AD6044CEED /* Ellipse.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Ellipse.swift; path = Sources/Private/Model/ShapeItems/Ellipse.swift; sourceTree = ""; }; - 805B67A1EC691E2F297D0CB0A125C31B /* InterpolatableExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InterpolatableExtensions.swift; path = Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift; sourceTree = ""; }; - 8186408B3030AB74BE1331A431FB83D4 /* Chatto.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Chatto.debug.xcconfig; sourceTree = ""; }; - 81EFFCF10BFC1B08CD7DBED57D297675 /* PrecompAsset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PrecompAsset.swift; path = Sources/Private/Model/Assets/PrecompAsset.swift; sourceTree = ""; }; - 8246440519C85238FCFCF8F58392A86F /* MDCPathGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCPathGenerator.m; path = components/Shapes/src/MDCPathGenerator.m; sourceTree = ""; }; - 82AB7D6FD755ADF708F94AF789430203 /* PreCompLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PreCompLayerModel.swift; path = Sources/Private/Model/Layers/PreCompLayerModel.swift; sourceTree = ""; }; - 84C11F8849A9F9FA96193BDF6D47B305 /* StrokeNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StrokeNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift; sourceTree = ""; }; - 854600D850F2C3A98DF50071F6F04C09 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; - 870DC3835055FF27A861C151FF69B97C /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; - 87107EE4D974CAE959A375B662F4204F /* MDCFontTextStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFontTextStyle.h; path = components/Typography/src/MDCFontTextStyle.h; sourceTree = ""; }; - 8A95BFC36D1019120C1DEC0F6758A0ED /* CompatibleAnimationKeypath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompatibleAnimationKeypath.swift; path = Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift; sourceTree = ""; }; - 8ABFF571A3232481B34A73FDDE0F2B55 /* KeyframeExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyframeExtensions.swift; path = Sources/Private/Utility/Interpolatable/KeyframeExtensions.swift; sourceTree = ""; }; - 8AF96553AC1A4981ED8F74D3571CB86C /* ShapeLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeLayerModel.swift; path = Sources/Private/Model/Layers/ShapeLayerModel.swift; sourceTree = ""; }; - 8C2F6B0AED78566F963846D8D10A8CC9 /* MDFTextAccessibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFTextAccessibility.h; path = src/MDFTextAccessibility.h; sourceTree = ""; }; - 8C64B5F902ABF8446821C23B3779E31D /* UIColor+MaterialElevation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MaterialElevation.m"; path = "components/Elevation/src/UIColor+MaterialElevation.m"; sourceTree = ""; }; - 8C971CF5443E97989CD0DF44D9C18846 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; - 8CE15CF0E1981C1CA722C1B5121C088C /* UIFont+MaterialScalable.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialScalable.m"; path = "components/Typography/src/UIFont+MaterialScalable.m"; sourceTree = ""; }; - 8D0323B0058AFC25122470F8D9558B7D /* MaterialIcons.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialIcons.h; path = components/private/Icons/src/MaterialIcons.h; sourceTree = ""; }; - 8D1A663AAA238F5A6580850607B58FB9 /* UIView+MDCTimingFunction.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MDCTimingFunction.m"; path = "components/AnimationTiming/src/UIView+MDCTimingFunction.m"; sourceTree = ""; }; - 8D638A90314C4C723DF58CBA2D02DCF3 /* MDCTypographyUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCTypographyUtilities.h; path = components/Typography/src/private/MDCTypographyUtilities.h; sourceTree = ""; }; - 8DA90325405F33A5808A47E8BFFAEE6F /* MaterialButtons.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialButtons.h; path = components/Buttons/src/MaterialButtons.h; sourceTree = ""; }; - 8DBE44442938F2CBE3624C972232C24E /* ShapeTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeTransform.swift; path = Sources/Private/Model/ShapeItems/ShapeTransform.swift; sourceTree = ""; }; - 8DD0AD806399CC6AE3AA2B4F75C77C23 /* MaterialElevation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialElevation.h; path = components/Elevation/src/MaterialElevation.h; sourceTree = ""; }; - 8EE689E19EA5C8EF636167214A787615 /* Star.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Star.swift; path = Sources/Private/Model/ShapeItems/Star.swift; sourceTree = ""; }; - 8F1F807B149E883C56B85C5C055D651D /* MDFInternationalization-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MDFInternationalization-dummy.m"; sourceTree = ""; }; - 8F3ED95F3D3F150E9EF3F7B365B3BE9C /* MaterialComponents-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MaterialComponents-dummy.m"; sourceTree = ""; }; - 909F969A28FE09D5039EF1A274202B6D /* MDCRectangleShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRectangleShapeGenerator.h; path = components/Shapes/src/MDCRectangleShapeGenerator.h; sourceTree = ""; }; - 90A761C8F692CCA4FDA5BFC5DEBACE74 /* MaterialShapeLibrary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShapeLibrary.h; path = components/ShapeLibrary/src/MaterialShapeLibrary.h; sourceTree = ""; }; - 91855A9940FB42F19FD2875C45E2CB01 /* Interpolatable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Interpolatable.swift; path = Sources/Private/Utility/Interpolatable/Interpolatable.swift; sourceTree = ""; }; - 919EA980AF20A4FC75CE16B54373D398 /* MathKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MathKit.swift; path = Sources/Private/Utility/Extensions/MathKit.swift; sourceTree = ""; }; - 924ED3C5CACD9B01ED8F6ACDCE0843EE /* Pods-iosTests */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iosTests"; path = Pods_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 92BF3EBBBF215FDB97014A1C7F674282 /* MDCInkLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkLayer.h; path = components/Ink/src/private/MDCInkLayer.h; sourceTree = ""; }; - 931593E088B694A7D6B0D1FC4FE6C080 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; - 93520D487909F1B889485508132F1A96 /* Combine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Combine.swift; path = Source/Combine.swift; sourceTree = ""; }; - 940160C49FE72F93E569C21314AB30EE /* PathOutputNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathOutputNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift; sourceTree = ""; }; - 952959F2E1983393A6EE1CAF37FB84E1 /* Pods-ios-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ios-Info.plist"; sourceTree = ""; }; - 95DF7386D55C9F9642DC5077C341E35E /* MDCRoundedCornerTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRoundedCornerTreatment.h; path = components/ShapeLibrary/src/MDCRoundedCornerTreatment.h; sourceTree = ""; }; - 9625E67A414049AFFBCF73EDB3F00B1B /* MDCShadow.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadow.h; path = components/Shadow/src/MDCShadow.h; sourceTree = ""; }; - 970D404B267F9ADF520FA00EC3700F65 /* AnimationContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationContainer.swift; path = Sources/Private/LayerContainers/AnimationContainer.swift; sourceTree = ""; }; - 97374F6103D866145B36386C14D1D8E5 /* KeyboardTracker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyboardTracker.swift; path = Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift; sourceTree = ""; }; - 97A0DF460C679110DBBD0ED8B315B64D /* MDFTextAccessibility-Bridging-Header.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MDFTextAccessibility-Bridging-Header.h"; path = "src/MDFTextAccessibility-Bridging-Header.h"; sourceTree = ""; }; - 97BE17BC31FAF7F8B04D6EFFBBE78C80 /* MDCButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCButton.h; path = components/Buttons/src/MDCButton.h; sourceTree = ""; }; - 985D00F7F6511C37B9DECF5DBEB3B451 /* MDCFlatButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFlatButton.h; path = components/Buttons/src/MDCFlatButton.h; sourceTree = ""; }; - 98EB1455F30B28A8F4F59C33E8CB38D2 /* MaterialMath.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialMath.h; path = components/private/Math/src/MaterialMath.h; sourceTree = ""; }; - 9906975C7ABA6E0912B4362EFF02B32C /* MDCInkGestureRecognizer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkGestureRecognizer.m; path = components/Ink/src/MDCInkGestureRecognizer.m; sourceTree = ""; }; - 9A1732F4D5612B18CF4BCB5A0D5F9A8C /* MDCFloatingButtonModeAnimator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFloatingButtonModeAnimator.m; path = components/Buttons/src/private/MDCFloatingButtonModeAnimator.m; sourceTree = ""; }; - 9A247D7873C2106F7E0006C6D3FA0DC9 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; - 9A2AC0B8BAC6EF061EBC28825653DF99 /* MDFInternationalization */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MDFInternationalization; path = MDFInternationalization.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9A370E644FBCA6F70723C4C1D6720158 /* UICollectionViewController+MDCCardReordering.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UICollectionViewController+MDCCardReordering.m"; path = "components/Cards/src/UICollectionViewController+MDCCardReordering.m"; sourceTree = ""; }; - 9B246A845EEBC2196E07F54E35ACA25A /* LayerTransformNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerTransformNode.swift; path = Sources/Private/LayerContainers/Utility/LayerTransformNode.swift; sourceTree = ""; }; - 9B6A73993CD9CF59863BB4C36386EEAE /* GradientFill.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientFill.swift; path = Sources/Private/Model/ShapeItems/GradientFill.swift; sourceTree = ""; }; - 9CF0A7BE4E58D6697CCF7D0FA0CBBDFB /* TextAnimatorNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextAnimatorNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift; sourceTree = ""; }; - 9D1132D92154CA46250E791A60FC2C43 /* MDCPathGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCPathGenerator.h; path = components/Shapes/src/MDCPathGenerator.h; sourceTree = ""; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 9DE63B387DA24E48BA2EDE84C68DC8DE /* MDCTypography.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCTypography.h; path = components/Typography/src/MDCTypography.h; sourceTree = ""; }; - 9E173EE03A996655ED2311253DA58EB9 /* RenderNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RenderNode.swift; path = Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift; sourceTree = ""; }; - 9ECD5C7C84C99618AF2620886CE9964E /* Pods-ios-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ios-acknowledgements.markdown"; sourceTree = ""; }; - 9FF5E5B45041E7444D6447C17872668F /* MDCRaisedButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCRaisedButton.m; path = components/Buttons/src/MDCRaisedButton.m; sourceTree = ""; }; - A07731940B92711A5CA2D9CF2C7DA38F /* FillRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift; sourceTree = ""; }; - A0D4358B34E866143ECD08151FA7D559 /* LayerTextProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerTextProvider.swift; path = Sources/Private/LayerContainers/Utility/LayerTextProvider.swift; sourceTree = ""; }; - A1BFB72AD30F0DE0585E804791B833D6 /* Transform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Transform.swift; path = Sources/Private/Model/Objects/Transform.swift; sourceTree = ""; }; - A2502619916EAC0AB2E21B1A54DB347D /* FillI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillI.swift; path = Sources/Private/Model/ShapeItems/FillI.swift; sourceTree = ""; }; - A2783799D5308869A35311BBDF845364 /* UIView+MaterialRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MaterialRTL.m"; path = "Sources/UIView+MaterialRTL.m"; sourceTree = ""; }; - A287971E3E70E72ECCDFC5485A8B6D35 /* MDCShapeMediator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShapeMediator.h; path = components/Shapes/src/MDCShapeMediator.h; sourceTree = ""; }; - A2C83FB5770E9E4004C80C5217E3CD69 /* NSArray+MDFUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+MDFUtils.m"; path = "src/private/NSArray+MDFUtils.m"; sourceTree = ""; }; - A34157B3708EDB7D17422B3D92F591D2 /* MaterialComponents */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MaterialComponents; path = MaterialComponents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A3619A8910E5127A2A7EBD2417924A3D /* RetryPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RetryPolicy.swift; path = Source/RetryPolicy.swift; sourceTree = ""; }; - A422591030AC5B802703CAAFA3536ED3 /* Vectors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Vectors.swift; path = Sources/Public/Primitives/Vectors.swift; sourceTree = ""; }; - A45EFEC8F1A0F1093631AF60C90BB060 /* Font.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Font.swift; path = Sources/Private/Model/Text/Font.swift; sourceTree = ""; }; - A488EF6B3932789A199EFF0E48669A8F /* ChatItemProtocolDefinitions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatItemProtocolDefinitions.swift; path = "Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift"; sourceTree = ""; }; - A4D99A5054C55BC32C98A5EF91CB5BDD /* CAMediaTimingFunction+MDCAnimationTiming.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "CAMediaTimingFunction+MDCAnimationTiming.m"; path = "components/AnimationTiming/src/CAMediaTimingFunction+MDCAnimationTiming.m"; sourceTree = ""; }; - A575DBC967BD1B9824384DE5EAC71824 /* MaterialColor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialColor.h; path = components/private/Color/src/MaterialColor.h; sourceTree = ""; }; - A5ED19C47A3B111AEEAE6C09A8E508BB /* Pods-ios.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-ios.modulemap"; sourceTree = ""; }; - A67A50DB4E82C2861D15BCA11383D919 /* MDCInkGestureRecognizer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkGestureRecognizer.h; path = components/Ink/src/MDCInkGestureRecognizer.h; sourceTree = ""; }; - A6DCF5983D0F4C2FB0E19A0D320B789B /* MaterialCards.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialCards.h; path = components/Cards/src/MaterialCards.h; sourceTree = ""; }; - A71810DBABD1DE3268F0A777230A85A2 /* MDCRippleViewDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleViewDelegate.h; path = components/Ripple/src/MDCRippleViewDelegate.h; sourceTree = ""; }; - A8AB1E1A488B0F97C034BFD4ECB9C11B /* MaterialInk.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialInk.h; path = components/Ink/src/MaterialInk.h; sourceTree = ""; }; - A9B79521A3553AD1005FA6A9670CEBF2 /* Pods-iosTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-iosTests.modulemap"; sourceTree = ""; }; - AB54FAD6E9F36E88C0A18E94EB070CA3 /* MDCInkTouchController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkTouchController.h; path = components/Ink/src/MDCInkTouchController.h; sourceTree = ""; }; - ABED2A4D6F3787803F5376C66CB9544C /* MaterialShadow.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShadow.h; path = components/Shadow/src/MaterialShadow.h; sourceTree = ""; }; - AC2834BC1BEF7C025DE72333BE447006 /* UIApplication+MDCAppExtensions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+MDCAppExtensions.m"; path = "components/private/Application/src/UIApplication+MDCAppExtensions.m"; sourceTree = ""; }; - AC80A65D7F1D3E258FEC205047C3EB0A /* MDCTriangleEdgeTreatment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCTriangleEdgeTreatment.h; path = components/ShapeLibrary/src/MDCTriangleEdgeTreatment.h; sourceTree = ""; }; - AD480CF250BF63F248837A06A6F55B90 /* AnimationFontProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationFontProvider.swift; path = Sources/Public/FontProvider/AnimationFontProvider.swift; sourceTree = ""; }; - AD54C4E77B772162BFA739B3159C945F /* MDFTextAccessibility.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MDFTextAccessibility.modulemap; sourceTree = ""; }; - AE7DFA57EB0C03A4757CAB9256086346 /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = ""; }; - AEFD189E2BBDDBFEFE3D285ECEA7330D /* Bundle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Bundle.swift; path = Sources/Private/Model/Extensions/Bundle.swift; sourceTree = ""; }; - AF43E371B84D82B4BE12084C4D22A945 /* MDCInkLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCInkLayerDelegate.h; path = components/Ink/src/private/MDCInkLayerDelegate.h; sourceTree = ""; }; - AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSLocale+MaterialRTL.h"; path = "Sources/NSLocale+MaterialRTL.h"; sourceTree = ""; }; - B120981A19A9C956A2941EB064D1FD1D /* MDFInternationalization-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFInternationalization-umbrella.h"; sourceTree = ""; }; - B15025A80B5750A7F05A489683577910 /* MDCLegacyInkLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCLegacyInkLayer.h; path = components/Ink/src/private/MDCLegacyInkLayer.h; sourceTree = ""; }; - B17C76564686F46F80432DCE2D053940 /* LottieView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LottieView.swift; path = Sources/Public/iOS/LottieView.swift; sourceTree = ""; }; - B19302162D052125FE5F7992C054A240 /* GradientFillNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientFillNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift; sourceTree = ""; }; - B1F119B189824C232697C892AF030E98 /* Pods-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ios.release.xcconfig"; sourceTree = ""; }; - B1F29114317DB38E4711B41B1C6376C9 /* Chatto-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Chatto-prefix.pch"; sourceTree = ""; }; - B25B52A26AE822B9B2F58EA6B9E7E240 /* PassThroughOutputNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PassThroughOutputNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift; sourceTree = ""; }; - B27333CDD645D2D5CF612BB67776A72B /* UIApplication+MDCAppExtensions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIApplication+MDCAppExtensions.h"; path = "components/private/Application/src/UIApplication+MDCAppExtensions.h"; sourceTree = ""; }; - B2EE0D77753F90766C6F5C64AAADA005 /* CompoundBezierPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompoundBezierPath.swift; path = Sources/Private/Utility/Primitives/CompoundBezierPath.swift; sourceTree = ""; }; - B3F0F84872DE9FA147E94B5562B8FE68 /* AnimationKeypathExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationKeypathExtension.swift; path = Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift; sourceTree = ""; }; - B3F1C077ED9B83ED31FAF400299DC965 /* MaterialShadowLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialShadowLayer.h; path = components/ShadowLayer/src/MaterialShadowLayer.h; sourceTree = ""; }; - B411BF2903D79884DFBA1F089404388E /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = ""; }; - B443745DA64B8F614899680B793F7108 /* MaterialAnimationTiming.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialAnimationTiming.h; path = components/AnimationTiming/src/MaterialAnimationTiming.h; sourceTree = ""; }; - B4C869DD8FBC246E0FE2199DCEB50D3D /* Glyph.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Glyph.swift; path = Sources/Private/Model/Text/Glyph.swift; sourceTree = ""; }; - B50C69F9BC8FB745BE744D03394900F8 /* MDFTextAccessibility-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MDFTextAccessibility-Info.plist"; sourceTree = ""; }; - B518C0072DFC3DBA1FA3C49569B1F3DA /* MDCCardCollectionCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCardCollectionCell.h; path = components/Cards/src/MDCCardCollectionCell.h; sourceTree = ""; }; - B613FA13F2264BCF72F6DE1BD3588C52 /* AnimationPublic.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationPublic.swift; path = Sources/Public/Animation/AnimationPublic.swift; sourceTree = ""; }; - B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFInternationalization.h; path = Sources/MDFInternationalization.h; sourceTree = ""; }; - B829E8E11D02D1970F8C8FF989A82B64 /* UIColor+MaterialDynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MaterialDynamic.h"; path = "components/private/Color/src/UIColor+MaterialDynamic.h"; sourceTree = ""; }; - B86684B080F0E3D4C1D38F093601783B /* ShapeItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeItem.swift; path = Sources/Private/Model/ShapeItems/ShapeItem.swift; sourceTree = ""; }; - B867765B8E02C5AE1B3C6A2B771DE65C /* UIFont+MaterialScalable.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialScalable.h"; path = "components/Typography/src/UIFont+MaterialScalable.h"; sourceTree = ""; }; - B9158E5A2523A49B53CFB65D466371AE /* Pods-iosTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iosTests-acknowledgements.markdown"; sourceTree = ""; }; - B93D411155688717E99B55AE5D57CF10 /* Chatto-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Chatto-dummy.m"; sourceTree = ""; }; - B995834698359995E0262F3F70C896A9 /* URLRequest+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLRequest+Alamofire.swift"; path = "Source/URLRequest+Alamofire.swift"; sourceTree = ""; }; - B9C5DEABB0B24A9A6A4FB076F829A033 /* SolidCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SolidCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift; sourceTree = ""; }; - BA395024B9E67C9EE8187839618E96FB /* MDCInkView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkView.m; path = components/Ink/src/MDCInkView.m; sourceTree = ""; }; - BA603856ED143A5A0536C7F9F8785FFF /* InputPositionControlling.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InputPositionControlling.swift; path = Chatto/Source/ChatController/InputPositionControlling.swift; sourceTree = ""; }; - BAA843D119FBBE1873DB868B2C42F47A /* ChatDataSourceProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatDataSourceProtocol.swift; path = Chatto/Source/ChatController/Collaborators/ChatDataSourceProtocol.swift; sourceTree = ""; }; - BBCBAB7ADDB2D552DB46A6DEE6C9F5A7 /* Chatto.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Chatto.modulemap; sourceTree = ""; }; - BBF566D92B960CE6B69285672FDE16A8 /* MDCShapedView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShapedView.m; path = components/Shapes/src/MDCShapedView.m; sourceTree = ""; }; - BC918096888E65550CADD29286F70E6B /* NSString+MaterialBidi.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+MaterialBidi.m"; path = "Sources/NSString+MaterialBidi.m"; sourceTree = ""; }; - BD14A3955F01AD7D390CC5763E43CEFE /* AnimatedSwitch.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedSwitch.swift; path = Sources/Public/iOS/AnimatedSwitch.swift; sourceTree = ""; }; - BE3893C08724F3D82CF1D199CF98F274 /* Chatto */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Chatto; path = Chatto.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BE5AE749191583AB288E955A216FBDEE /* MDFInternationalization.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFInternationalization.debug.xcconfig; sourceTree = ""; }; - BF467C869E0EBC17B9CB2B58A166300C /* Pods-ios-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ios-acknowledgements.plist"; sourceTree = ""; }; - BF7CDBB13E9F0AC3175BDF383C468787 /* MDCElevationOverriding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCElevationOverriding.h; path = components/Elevation/src/MDCElevationOverriding.h; sourceTree = ""; }; - BF9EDA0CCF4ACD15ECC442F77B0DD927 /* ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist"; sourceTree = ""; }; - BFA5A232C8B4D4671F587B33DCEF5FB4 /* CollectionChanges.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CollectionChanges.swift; path = Chatto/Source/ChatController/Collaborators/CollectionChanges.swift; sourceTree = ""; }; - C1C78482FC811B81F0499146DB6F8592 /* SizeValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SizeValueProvider.swift; path = Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift; sourceTree = ""; }; - C1FAB2482AA7F33F1F62927A25599EF4 /* GradientStrokeNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientStrokeNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift; sourceTree = ""; }; - C27430E91F40A49F302EA2C974453696 /* HTTPMethod.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HTTPMethod.swift; path = Source/HTTPMethod.swift; sourceTree = ""; }; - C290807FDFEFCB92E5FABE8FE851443C /* DashPattern.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DashPattern.swift; path = Sources/Private/Model/Objects/DashPattern.swift; sourceTree = ""; }; - C30F85DF0D50528EF9BEE8A2BA482034 /* LayerDebugging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerDebugging.swift; path = Sources/Private/Utility/Debugging/LayerDebugging.swift; sourceTree = ""; }; - C31592E117E0046F2678B3066EB06AC3 /* CompositionLayersInitializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompositionLayersInitializer.swift; path = Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift; sourceTree = ""; }; - C364DE9E9FEDDD730DBF09F77CB0B143 /* MDCIcons.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCIcons.h; path = components/private/Icons/src/MDCIcons.h; sourceTree = ""; }; - C43092A50861A2F799989439A7DCBCAF /* Alamofire.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.debug.xcconfig; sourceTree = ""; }; - C5494CF8D45C3C7880D1C7684088125B /* Chatto.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Chatto.release.xcconfig; sourceTree = ""; }; - C59B9A40073373B78D37C4937FD3E223 /* Chatto-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Chatto-Info.plist"; sourceTree = ""; }; - C67AF078C9EE64DED1961778EFF012A0 /* EllipseNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EllipseNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift; sourceTree = ""; }; - C73D5045364BD3202565B4FDB54AE37A /* MDCShadowsCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCShadowsCollection.h; path = components/Shadow/src/MDCShadowsCollection.h; sourceTree = ""; }; - C7B54C5101F5D61940E8C3AE0858E0A6 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; - C7C60015DC526524C5DC274704897564 /* MDCFloatingButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCFloatingButton.m; path = components/Buttons/src/MDCFloatingButton.m; sourceTree = ""; }; - C80B62ED9C592FA542C65C50680BD2D3 /* PolygonNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PolygonNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift; sourceTree = ""; }; - C8F19458534DF74ABBBC1D9171D2FA1A /* KeyframeGroup.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyframeGroup.swift; path = Sources/Private/Model/Keyframes/KeyframeGroup.swift; sourceTree = ""; }; - CA6F96BF2DFC0E11E6A55539B9225C94 /* ChatCollectionViewLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatCollectionViewLayout.swift; path = Chatto/Source/ChatController/Collaborators/ChatCollectionViewLayout.swift; sourceTree = ""; }; - CB094FC5A0B58CDEC708A4D9A8189F89 /* AuthenticationInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthenticationInterceptor.swift; path = Source/AuthenticationInterceptor.swift; sourceTree = ""; }; - CBBE66515626EA5EE7282EF95C8275B3 /* MDCCurvedCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCurvedCornerTreatment.m; path = components/ShapeLibrary/src/MDCCurvedCornerTreatment.m; sourceTree = ""; }; - CE0A65CE9CFA027DECC15DDE7C84261A /* GradientStrokeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientStrokeRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift; sourceTree = ""; }; - CE99EB6CC9C88A5D592843A9171EC23B /* AnyValueContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyValueContainer.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift; sourceTree = ""; }; - CEE878F352FF70522E2B4041CB792B24 /* MDFTextAccessibility.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MDFTextAccessibility.debug.xcconfig; sourceTree = ""; }; - CEF8701D965CC5D013B9CC49B8B228E8 /* SingleValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SingleValueProvider.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift; sourceTree = ""; }; - D0F908B38DD2B1F35BBC0B8F214C6DC0 /* MaterialShadowElevationsDummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MaterialShadowElevationsDummy.m; path = components/ShadowElevations/src/MaterialShadowElevationsDummy.m; sourceTree = ""; }; - D25173317D99ECFE9E0ECC69FCEC95FA /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = ""; }; - D2F86DF24DDC31E73E6DC1AD1E8E22E6 /* UIColor+MaterialElevation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MaterialElevation.h"; path = "components/Elevation/src/UIColor+MaterialElevation.h"; sourceTree = ""; }; - D3414E16D6767D8F8FBAB002ECBAA6CD /* MDCInkLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCInkLayer.m; path = components/Ink/src/private/MDCInkLayer.m; sourceTree = ""; }; - D390D3DEF1579285A22574E5E6EA97D0 /* UIFont+MaterialTypography.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialTypography.m"; path = "components/Typography/src/UIFont+MaterialTypography.m"; sourceTree = ""; }; - D3C80D0A870376D3DAB74C8D106BFF8C /* MaterialMathDummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MaterialMathDummy.m; path = components/private/Math/src/MaterialMathDummy.m; sourceTree = ""; }; - D6E67B621B39259F3CED1DE2F43E57AE /* MDCRippleTouchControllerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleTouchControllerDelegate.h; path = components/Ripple/src/MDCRippleTouchControllerDelegate.h; sourceTree = ""; }; - D720E1DADE8B1BB169230ADFA11012E3 /* Pods-ios-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-ios-umbrella.h"; sourceTree = ""; }; - D7F58CC76B154EE1FFCEBFD29458765F /* BaseChatViewController+CellPanGestureHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+CellPanGestureHandler.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+CellPanGestureHandler.swift"; sourceTree = ""; }; - D80EB9AAB47357453596394BFD2FBD6D /* ValueContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ValueContainer.swift; path = Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift; sourceTree = ""; }; - D8F1688277322547EA5D6CCDF2C94C1F /* MDCFloatingButton+Animation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MDCFloatingButton+Animation.m"; path = "components/Buttons/src/MDCFloatingButton+Animation.m"; sourceTree = ""; }; - DA0CD727B37100536B0582E4ACA83844 /* StarNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StarNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift; sourceTree = ""; }; - DAA556AD9FDEF109652EAA16FF14D299 /* UIView+MaterialElevationResponding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MaterialElevationResponding.h"; path = "components/Elevation/src/UIView+MaterialElevationResponding.h"; sourceTree = ""; }; - DAEF5AB5FA15E20986DA16EBD0840CA2 /* BundleImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BundleImageProvider.swift; path = Sources/Public/iOS/BundleImageProvider.swift; sourceTree = ""; }; - DB0EBEFA02E716358C707E8E92A56CC2 /* StrokeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StrokeRenderer.swift; path = Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift; sourceTree = ""; }; - DB1E998F48C65751461A4CB1E13E45C5 /* MDCCurvedRectShapeGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCCurvedRectShapeGenerator.h; path = components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.h; sourceTree = ""; }; - DB84425C5097649DB7039BCDFCA4DB6C /* Pods-ios */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-ios"; path = Pods_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - DBCD73AF1D7111668851A56E369B30AF /* Stroke.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Stroke.swift; path = Sources/Private/Model/ShapeItems/Stroke.swift; sourceTree = ""; }; - DE1B915229A73A86A29689D51EBCEE8E /* Chatto-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Chatto-umbrella.h"; sourceTree = ""; }; - DE6A0C29D359F8F037F7DF2A21B78FC4 /* EventMonitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EventMonitor.swift; path = Source/EventMonitor.swift; sourceTree = ""; }; - DF79A6BB5BC283B6F1FFF7394D1ABD51 /* MaterialIcons+ic_check_circle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MaterialIcons+ic_check_circle.h"; path = "components/private/Icons/icons/ic_check_circle/src/MaterialIcons+ic_check_circle.h"; sourceTree = ""; }; - E0CB865665244183E4B9659E94101F9B /* TrimPathNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TrimPathNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift; sourceTree = ""; }; - E0D7B9CC58E107433D11681261211BE1 /* LayerFontProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayerFontProvider.swift; path = Sources/Private/LayerContainers/Utility/LayerFontProvider.swift; sourceTree = ""; }; - E10CAC47530EA0936ECF4F5B18F41899 /* ShapeCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift; sourceTree = ""; }; - E1470EA198559C0288DDF4068CAFB6ED /* MDFInternationalization.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MDFInternationalization.modulemap; sourceTree = ""; }; - E20AA7DE19CA8DB5FD1FE6636C4C6E89 /* RectNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RectNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/RectNode.swift; sourceTree = ""; }; - E22A0783A99782B1368A7C295F05B893 /* Marker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Marker.swift; path = Sources/Private/Model/Objects/Marker.swift; sourceTree = ""; }; - E3B32DE56F884A29871DDC06DEFDD299 /* FillNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift; sourceTree = ""; }; - E3D75AB0F4CDA59E7BB16BD81745B956 /* Shape.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Shape.swift; path = Sources/Private/Model/ShapeItems/Shape.swift; sourceTree = ""; }; - E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - E58F9B821896E6DE3629364AB9B21F35 /* MaterialAvailability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MaterialAvailability.h; path = components/Availability/src/MaterialAvailability.h; sourceTree = ""; }; - E5A162FEC0123631BFA766AC1CD2E593 /* AnyValueProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyValueProvider.swift; path = Sources/Public/DynamicProperties/AnyValueProvider.swift; sourceTree = ""; }; - E6966E76E2E05FEC74DB6972B4F38FCC /* AnimationViewInitializers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationViewInitializers.swift; path = Sources/Public/Animation/AnimationViewInitializers.swift; sourceTree = ""; }; - E69B2A401E928B1CBB9CD4B02BEE853D /* UIView+MDCTimingFunction.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MDCTimingFunction.h"; path = "components/AnimationTiming/src/UIView+MDCTimingFunction.h"; sourceTree = ""; }; - E6ACC937F5C195801B15F2AB3B92101F /* GradientStroke.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientStroke.swift; path = Sources/Private/Model/ShapeItems/GradientStroke.swift; sourceTree = ""; }; - E741DFF8BC3B7CB733B6417AE217AEC1 /* Pods-iosTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosTests-frameworks.sh"; sourceTree = ""; }; - E81ECAF8BBE1DB54276ED819382CA9FA /* MDCRippleLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCRippleLayer.h; path = components/Ripple/src/private/MDCRippleLayer.h; sourceTree = ""; }; - E94E88E183E7AC90A64BD46E39E2C85E /* LRUAnimationCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LRUAnimationCache.swift; path = Sources/Public/AnimationCache/LRUAnimationCache.swift; sourceTree = ""; }; - E9E21A4FEF3A460EFF2C8B38BBA65C25 /* Rectangle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Rectangle.swift; path = Sources/Private/Model/ShapeItems/Rectangle.swift; sourceTree = ""; }; - EA26F20034BDBF94780CF01106D4CD64 /* Alamofire-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Alamofire-Info.plist"; sourceTree = ""; }; - EA378A26B99BBD94560AE1C8C02B11F6 /* BaseChatViewController+Changes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BaseChatViewController+Changes.swift"; path = "Chatto/Source/ChatController/BaseChatViewController+Changes.swift"; sourceTree = ""; }; - EA9BAA45A6BA523F01C4735963DDDADC /* MDCCutCornerTreatment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCutCornerTreatment.m; path = components/ShapeLibrary/src/MDCCutCornerTreatment.m; sourceTree = ""; }; - EB57E704596E48FF3325A1F46B6E783C /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; - EB7B1A690B1D232B90A70E4E318BB297 /* Asset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Asset.swift; path = Sources/Private/Model/Assets/Asset.swift; sourceTree = ""; }; - EEBE2EE981029A2CA291F17EA8723B03 /* UIFontDescriptor+MaterialTypography.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFontDescriptor+MaterialTypography.m"; path = "components/Typography/src/UIFontDescriptor+MaterialTypography.m"; sourceTree = ""; }; - F1CD9DD5A9448FED236DB4E8AAF6C8A4 /* Trim.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Trim.swift; path = Sources/Private/Model/ShapeItems/Trim.swift; sourceTree = ""; }; - F1E0BB0AD007791BD42D66DF1F4DB387 /* NullCompositionLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NullCompositionLayer.swift; path = Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift; sourceTree = ""; }; - F2F4B5DB7011E5176A063CF3110CD09E /* AnimationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationView.swift; path = Sources/Public/Animation/AnimationView.swift; sourceTree = ""; }; - F49594B80BEB495C857FE4A0C034A387 /* UIFont+MaterialTypographyPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+MaterialTypographyPrivate.h"; path = "components/Typography/src/private/UIFont+MaterialTypographyPrivate.h"; sourceTree = ""; }; - F4F84C77FED35072FBA03ADBD28FF9F3 /* AnimatedButton.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedButton.swift; path = Sources/Public/iOS/AnimatedButton.swift; sourceTree = ""; }; - F5922665F1DC85A6D8A92E2E648D19BE /* Pods-iosTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iosTests-dummy.m"; sourceTree = ""; }; - F5CCE660C00123EB989E5679091A134A /* MDFColorCalculations.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDFColorCalculations.h; path = src/private/MDFColorCalculations.h; sourceTree = ""; }; - F6090940C42D19C30699D9DFCD46FEA1 /* RequestTaskMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestTaskMap.swift; path = Source/RequestTaskMap.swift; sourceTree = ""; }; - F6BB438AED2F3B6F61B65BEBABF41B1F /* MDCShapeMediator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShapeMediator.m; path = components/Shapes/src/MDCShapeMediator.m; sourceTree = ""; }; - F6E10A41E382E72F54FC92EF85F00963 /* AnimationCacheProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationCacheProvider.swift; path = Sources/Public/AnimationCache/AnimationCacheProvider.swift; sourceTree = ""; }; - F6EB232EC11EB49D9DA52EDFB9660747 /* AnimatedControl.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedControl.swift; path = Sources/Public/iOS/AnimatedControl.swift; sourceTree = ""; }; - F711AC4E23408CDD020388C5E5D71009 /* AnimationImageProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationImageProvider.swift; path = Sources/Public/ImageProvider/AnimationImageProvider.swift; sourceTree = ""; }; - F75BEC31BE7157E03DF220C390111D13 /* AnimationTextProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationTextProvider.swift; path = Sources/Public/TextProvider/AnimationTextProvider.swift; sourceTree = ""; }; - F873EC876A8574DA2C7425CA4C3BC418 /* MDCFloatingButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MDCFloatingButton.h; path = components/Buttons/src/MDCFloatingButton.h; sourceTree = ""; }; - F90CBF2D1339BB6CDDAFF78CC3B59761 /* MDCButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCButton.m; path = components/Buttons/src/MDCButton.m; sourceTree = ""; }; - F9C9C35B39EFD7AD82253D0A2AD91B56 /* Pods-iosTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosTests.release.xcconfig"; sourceTree = ""; }; - FA343ABB695B730239978538A94F38D6 /* ParameterEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoder.swift; path = Source/ParameterEncoder.swift; sourceTree = ""; }; - FB8220FA4D9117310AEFC629A6EE4A08 /* MDFTextAccessibility */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MDFTextAccessibility; path = MDFTextAccessibility.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - FBCF7000A4C9DC9C24B02BFE17451F85 /* ShapeNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeNode.swift; path = Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift; sourceTree = ""; }; - FBD0995EC979D7375ABF14FB9A0A9EB0 /* ChatLayoutConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChatLayoutConfiguration.swift; path = Chatto/Source/ChatController/ChatLayoutConfiguration.swift; sourceTree = ""; }; - FBE0655303C1520FF0EF50C57907E661 /* MDFTextAccessibility-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MDFTextAccessibility-umbrella.h"; sourceTree = ""; }; - FC5CBEF7738EFE577EEAE11F00FF329E /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = ""; }; - FC7593B690F872386EAC6DA2F85BD36D /* MDFRTL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDFRTL.m; path = Sources/MDFRTL.m; sourceTree = ""; }; - FDEEADA23D6F4C3DFE04A4117338D768 /* MDCShapedShadowLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCShapedShadowLayer.m; path = components/Shapes/src/MDCShapedShadowLayer.m; sourceTree = ""; }; - FE68F0EF6188796115A2408E20255E7C /* UIFont+MaterialTypographyPrivate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+MaterialTypographyPrivate.m"; path = "components/Typography/src/private/UIFont+MaterialTypographyPrivate.m"; sourceTree = ""; }; - FE6DB58D8654DC8B5F88E4D12FD74409 /* ShapeRenderLayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShapeRenderLayer.swift; path = Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift; sourceTree = ""; }; - FEB0A58B0BC617DECB8D3427A1405634 /* ImageLayerModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageLayerModel.swift; path = Sources/Private/Model/Layers/ImageLayerModel.swift; sourceTree = ""; }; - FEC9B0599DF18776D54CF802CB9FFFD9 /* MDCCurvedRectShapeGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MDCCurvedRectShapeGenerator.m; path = components/ShapeLibrary/src/MDCCurvedRectShapeGenerator.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 052B815FEFBCEB5CBBA08741F810D971 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 192B25A167B62AA5C7B878CE91F3582E /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 1D5C89C35ED64587AA3F3CC9FB379C53 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A6F57488F348E0036579E5B9DD98CAA2 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 28BA49E77F658083C4A924833FF0CF7D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7E636D3BE68B055932C1DBF52E9E5F8A /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 39D530C2A3085A1033683EC9AE0BC313 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 33F86FAB918B148A63A1575667F9B570 /* CFNetwork.framework in Frameworks */, - 9C0BE8FA0030B2BC1DF7C159FA059389 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 78FC9E52ECE20C8C02B38B218A9F8D6C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7E4FBF289DDBE5FE3D144CF1347D4092 /* CoreGraphics.framework in Frameworks */, - FBBCCDCCAFD735DA076AEC214B31DEBE /* Foundation.framework in Frameworks */, - 58287E8A2D3CC19A61F6EC3D5138CED3 /* QuartzCore.framework in Frameworks */, - F45BE4AD9E22109487817B3E369E8774 /* UIKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A46E636AA9FD7591DACA5A99D724E081 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A563BE445A159E73000F25A2BC3B940E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C033F3E94355E90147C197D165E7DEE0 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B05B2D2B6059C5982EE1D520AA115030 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B3901084F60BD621D593F30B15E9D5C0 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9B2E1995C19B72DC80DBCAAE398BC33 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 49F6DAAA65DA40549E2D693A7E1B9E85 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0CE2657B3BF086744C29E6B209CFDE15 /* Ink */ = { - isa = PBXGroup; - children = ( - A8AB1E1A488B0F97C034BFD4ECB9C11B /* MaterialInk.h */, - A67A50DB4E82C2861D15BCA11383D919 /* MDCInkGestureRecognizer.h */, - 9906975C7ABA6E0912B4362EFF02B32C /* MDCInkGestureRecognizer.m */, - 92BF3EBBBF215FDB97014A1C7F674282 /* MDCInkLayer.h */, - D3414E16D6767D8F8FBAB002ECBAA6CD /* MDCInkLayer.m */, - AF43E371B84D82B4BE12084C4D22A945 /* MDCInkLayerDelegate.h */, - AB54FAD6E9F36E88C0A18E94EB070CA3 /* MDCInkTouchController.h */, - 02F95682318FB0084CD0FAE9E698A337 /* MDCInkTouchController.m */, - 229265038E32F623E46E6B51D41CC33C /* MDCInkTouchControllerDelegate.h */, - 79EA03EF06AA77426C2C84981497D6F8 /* MDCInkView.h */, - BA395024B9E67C9EE8187839618E96FB /* MDCInkView.m */, - 42C1DD5860EC1699A757C9BAB12A66D7 /* MDCInkViewDelegate.h */, - B15025A80B5750A7F05A489683577910 /* MDCLegacyInkLayer.h */, - 600ED7A50816D1A3222193C97DEA9BC3 /* MDCLegacyInkLayer.m */, - 16900192B6F1F880A2BEEE4A15228EE3 /* MDCLegacyInkLayer+Private.h */, - 3DCA91058EFB02847D0DA47F4F3195AC /* MDCLegacyInkLayerDelegate.h */, - 6F5C3BCCB2FBBD3DC6701A99A1EC16E2 /* MDCLegacyInkLayerRippleDelegate.h */, - ); - name = Ink; - sourceTree = ""; - }; - 144970CAC0814448ACFB4EE458FC2F64 /* Alamofire */ = { - isa = PBXGroup; - children = ( - 6BDB7E8223919BD683DE47719CEA1A69 /* AFError.swift */, - 854600D850F2C3A98DF50071F6F04C09 /* Alamofire.swift */, - 701CF46760379C28084078F7D4BAFF32 /* AlamofireExtended.swift */, - CB094FC5A0B58CDEC708A4D9A8189F89 /* AuthenticationInterceptor.swift */, - 748CA1EDB7765D88F678941AA746AA9E /* CachedResponseHandler.swift */, - 93520D487909F1B889485508132F1A96 /* Combine.swift */, - 931593E088B694A7D6B0D1FC4FE6C080 /* DispatchQueue+Alamofire.swift */, - DE6A0C29D359F8F037F7DF2A21B78FC4 /* EventMonitor.swift */, - 21FFD3AD6BFF43E494BD4BD3E560F4A9 /* HTTPHeaders.swift */, - C27430E91F40A49F302EA2C974453696 /* HTTPMethod.swift */, - 7F557E15F0C636BE0C4547A358DA8C93 /* MultipartFormData.swift */, - 7872898EA812A796E27F3664AA901902 /* MultipartUpload.swift */, - D25173317D99ECFE9E0ECC69FCEC95FA /* NetworkReachabilityManager.swift */, - AE7DFA57EB0C03A4757CAB9256086346 /* Notifications.swift */, - 30734D2F4660366E1C28FA6E2E4345B1 /* OperationQueue+Alamofire.swift */, - FA343ABB695B730239978538A94F38D6 /* ParameterEncoder.swift */, - FC5CBEF7738EFE577EEAE11F00FF329E /* ParameterEncoding.swift */, - 73D70DCDE236059947C0BF795B512C84 /* Protected.swift */, - 2B1E20D6B957EF263B8B500DA4F13C39 /* RedirectHandler.swift */, - 7800E126D44EA6D8FC842E5214971EF6 /* Request.swift */, - 097A230A81C9305A1D7CAF969BD3B009 /* RequestInterceptor.swift */, - F6090940C42D19C30699D9DFCD46FEA1 /* RequestTaskMap.swift */, - 1962BFA68BF11D74BDD363384439BAC5 /* Response.swift */, - B411BF2903D79884DFBA1F089404388E /* ResponseSerialization.swift */, - 246B8D62D146A318E129AB0C450D33BA /* Result+Alamofire.swift */, - A3619A8910E5127A2A7EBD2417924A3D /* RetryPolicy.swift */, - 1A8B9BF018FD470AE5EBE4F9FF3808CD /* ServerTrustEvaluation.swift */, - 6684560C13B020634CFCE92295A15B72 /* Session.swift */, - C7B54C5101F5D61940E8C3AE0858E0A6 /* SessionDelegate.swift */, - 7BF3276002056A8603CF35E5D0FDBB3B /* StringEncoding+Alamofire.swift */, - 57C4846B514755F166442D8D10CEA565 /* URLConvertible+URLRequestConvertible.swift */, - 3D28FDB1911F3BD71EEBE077FA899488 /* URLEncodedFormEncoder.swift */, - B995834698359995E0262F3F70C896A9 /* URLRequest+Alamofire.swift */, - 6587D8532A9E9E13BA4B37E713566C3B /* URLSessionConfiguration+Alamofire.swift */, - 8C971CF5443E97989CD0DF44D9C18846 /* Validation.swift */, - A95CB1598E998B48CD1D973159ECC08E /* Support Files */, - ); - name = Alamofire; - path = Alamofire; - sourceTree = ""; - }; - 1AF48580728743ABEEC0DA59D34B78E2 /* Support Files */ = { - isa = PBXGroup; - children = ( - AD54C4E77B772162BFA739B3159C945F /* MDFTextAccessibility.modulemap */, - 4E2AA82D677A35A05B022A324E49F50C /* MDFTextAccessibility-dummy.m */, - B50C69F9BC8FB745BE744D03394900F8 /* MDFTextAccessibility-Info.plist */, - 5D06BA269EAAEF5A59A416177B7571E8 /* MDFTextAccessibility-prefix.pch */, - FBE0655303C1520FF0EF50C57907E661 /* MDFTextAccessibility-umbrella.h */, - CEE878F352FF70522E2B4041CB792B24 /* MDFTextAccessibility.debug.xcconfig */, - 0EA7FAE72F27B4A66C2CAEEC25C31634 /* MDFTextAccessibility.release.xcconfig */, - ); - name = "Support Files"; - path = "../Target Support Files/MDFTextAccessibility"; - sourceTree = ""; - }; - 21AC7CA70041658C6EBD3A1F4775F58C /* lottie-ios */ = { - isa = PBXGroup; - children = ( - F4F84C77FED35072FBA03ADBD28FF9F3 /* AnimatedButton.swift */, - F6EB232EC11EB49D9DA52EDFB9660747 /* AnimatedControl.swift */, - BD14A3955F01AD7D390CC5763E43CEFE /* AnimatedSwitch.swift */, - 2D6557B599662265675CB317D7A3AA35 /* Animation.swift */, - F6E10A41E382E72F54FC92EF85F00963 /* AnimationCacheProvider.swift */, - 970D404B267F9ADF520FA00EC3700F65 /* AnimationContainer.swift */, - 708C0285547318992A3989A6415E5D6B /* AnimationContext.swift */, - AD480CF250BF63F248837A06A6F55B90 /* AnimationFontProvider.swift */, - F711AC4E23408CDD020388C5E5D71009 /* AnimationImageProvider.swift */, - 4259483CFD0AC1C7F1295B7B354781A1 /* AnimationKeypath.swift */, - B3F0F84872DE9FA147E94B5562B8FE68 /* AnimationKeypathExtension.swift */, - B613FA13F2264BCF72F6DE1BD3588C52 /* AnimationPublic.swift */, - 5BAC390311C727CE7C129F84BD9B09B9 /* AnimationSubview.swift */, - F75BEC31BE7157E03DF220C390111D13 /* AnimationTextProvider.swift */, - 0A5DF95E38B16446E73BCFAC6F53DA2E /* AnimationTime.swift */, - F2F4B5DB7011E5176A063CF3110CD09E /* AnimationView.swift */, - E6966E76E2E05FEC74DB6972B4F38FCC /* AnimationViewInitializers.swift */, - 78E1DDE875714E724C5D0109D3735095 /* AnimatorNode.swift */, - 7C6DBEEB82DA69272704408A3AEBA6EC /* AnimatorNodeDebugging.swift */, - 678552B49FF59718A815C8CB72951E75 /* AnyNodeProperty.swift */, - CE99EB6CC9C88A5D592843A9171EC23B /* AnyValueContainer.swift */, - E5A162FEC0123631BFA766AC1CD2E593 /* AnyValueProvider.swift */, - EB7B1A690B1D232B90A70E4E318BB297 /* Asset.swift */, - 31D7840BB6B99AC092EAB4942FDD3F53 /* AssetLibrary.swift */, - 3AEB21337FEDA85FD4CE4B5534D436F9 /* BezierPath.swift */, - AEFD189E2BBDDBFEFE3D285ECEA7330D /* Bundle.swift */, - DAEF5AB5FA15E20986DA16EBD0840CA2 /* BundleImageProvider.swift */, - 62FE3D47542C17E69E982939D7C4342C /* CGFloatExtensions.swift */, - 6F2AF02B3727FEC99B62286905D9F172 /* Color.swift */, - 7E145BBB7A0266AF3CEF37E16F141F03 /* ColorExtension.swift */, - 1C0B52C8F35A67626A42F0430F36E070 /* ColorValueProvider.swift */, - 8A95BFC36D1019120C1DEC0F6758A0ED /* CompatibleAnimationKeypath.swift */, - 3D6F69E4E368E5F576A3D6AF24022D2D /* CompatibleAnimationView.swift */, - 65EAC9DA9D8A3173E14F6714F6DD597E /* CompositionLayer.swift */, - C31592E117E0046F2678B3066EB06AC3 /* CompositionLayersInitializer.swift */, - B2EE0D77753F90766C6F5C64AAADA005 /* CompoundBezierPath.swift */, - 74405B4230DF6726303FA767434E6A14 /* CurveVertex.swift */, - C290807FDFEFCB92E5FABE8FE851443C /* DashPattern.swift */, - 7FA25E16755D429C6CC771AD6044CEED /* Ellipse.swift */, - C67AF078C9EE64DED1961778EFF012A0 /* EllipseNode.swift */, - 64882FB458891751B0C4E20C1C23D5F1 /* FilepathImageProvider.swift */, - A2502619916EAC0AB2E21B1A54DB347D /* FillI.swift */, - E3B32DE56F884A29871DDC06DEFDD299 /* FillNode.swift */, - A07731940B92711A5CA2D9CF2C7DA38F /* FillRenderer.swift */, - 686DE3CEFB348F67B7F2E0CCDBED00A7 /* FloatValueProvider.swift */, - A45EFEC8F1A0F1093631AF60C90BB060 /* Font.swift */, - B4C869DD8FBC246E0FE2199DCEB50D3D /* Glyph.swift */, - 9B6A73993CD9CF59863BB4C36386EEAE /* GradientFill.swift */, - B19302162D052125FE5F7992C054A240 /* GradientFillNode.swift */, - 619B96C839D9C1480A0A40E988C63AFD /* GradientFillRenderer.swift */, - E6ACC937F5C195801B15F2AB3B92101F /* GradientStroke.swift */, - C1FAB2482AA7F33F1F62927A25599EF4 /* GradientStrokeNode.swift */, - CE0A65CE9CFA027DECC15DDE7C84261A /* GradientStrokeRenderer.swift */, - 553199DA713332B2A7CE2BC1B053344C /* GradientValueProvider.swift */, - 6E035C3FBB06190E0CE480284B8CF014 /* Group.swift */, - 5739897ED45AAC422F0EECBA1D75F58A /* GroupInterpolator.swift */, - 63431295907542E5A9DD8630DC9338BA /* GroupNode.swift */, - 058498847739D73FC959AC6FAC7A0D9F /* GroupOutputNode.swift */, - 3D884B74C43A162D8B9756985E116E84 /* ImageAsset.swift */, - 35A0FCAED698C32DB774702E78F6A9BB /* ImageCompositionLayer.swift */, - FEB0A58B0BC617DECB8D3427A1405634 /* ImageLayerModel.swift */, - 91855A9940FB42F19FD2875C45E2CB01 /* Interpolatable.swift */, - 805B67A1EC691E2F297D0CB0A125C31B /* InterpolatableExtensions.swift */, - 1E1B1AAE95B01FE76CC5E0D131135D20 /* InvertedMatteLayer.swift */, - 61814AFE969DF03C54E6B9E503F28302 /* ItemsExtension.swift */, - 111FAD1AC980B2D67B27016C087B61ED /* KeyedDecodingContainerExtensions.swift */, - 3984A2B5195B698FF523B7D82CD694F0 /* Keyframe.swift */, - 8ABFF571A3232481B34A73FDDE0F2B55 /* KeyframeExtensions.swift */, - C8F19458534DF74ABBBC1D9171D2FA1A /* KeyframeGroup.swift */, - 174A76F8A707305878153E63E7AF69F3 /* KeyframeInterpolator.swift */, - 0FC5A268C4EB4A715BD6C5613A37B5A9 /* KeypathSearchable.swift */, - C30F85DF0D50528EF9BEE8A2BA482034 /* LayerDebugging.swift */, - E0D7B9CC58E107433D11681261211BE1 /* LayerFontProvider.swift */, - 35BD0FEEB2EF9ADE87455CB6949E56DC /* LayerImageProvider.swift */, - 525767FD21D481C033ED537000C2A5B1 /* LayerModel.swift */, - A0D4358B34E866143ECD08151FA7D559 /* LayerTextProvider.swift */, - 9B246A845EEBC2196E07F54E35ACA25A /* LayerTransformNode.swift */, - B17C76564686F46F80432DCE2D053940 /* LottieView.swift */, - E94E88E183E7AC90A64BD46E39E2C85E /* LRUAnimationCache.swift */, - E22A0783A99782B1368A7C295F05B893 /* Marker.swift */, - 77EF08C7D4C2345726A07A8B9A265452 /* Mask.swift */, - 5241F42DE93198A9743B520528DEBE0E /* MaskContainerLayer.swift */, - 919EA980AF20A4FC75CE16B54373D398 /* MathKit.swift */, - 713A971DE11ED11949FB41259A01995C /* Merge.swift */, - 55C5A1F93EFADC2096EE6DC9303B38A8 /* NodeProperty.swift */, - 5AF2CED0DB3829D4D4BE54A78C1072DB /* NodePropertyMap.swift */, - F1E0BB0AD007791BD42D66DF1F4DB387 /* NullCompositionLayer.swift */, - B25B52A26AE822B9B2F58EA6B9E7E240 /* PassThroughOutputNode.swift */, - 096CBFAE6AD4730265460085F9186006 /* PathElement.swift */, - 7BC6506BF9A73061A58868C0D592B9B1 /* PathNode.swift */, - 940160C49FE72F93E569C21314AB30EE /* PathOutputNode.swift */, - 76D52B7A10D74C906F9C84CC976B1B2D /* PointValueProvider.swift */, - C80B62ED9C592FA542C65C50680BD2D3 /* PolygonNode.swift */, - 81EFFCF10BFC1B08CD7DBED57D297675 /* PrecompAsset.swift */, - 82AB7D6FD755ADF708F94AF789430203 /* PreCompLayerModel.swift */, - 2EE66424995BD90C997E2CEA9946F2AA /* PreCompositionLayer.swift */, - E9E21A4FEF3A460EFF2C8B38BBA65C25 /* Rectangle.swift */, - E20AA7DE19CA8DB5FD1FE6636C4C6E89 /* RectNode.swift */, - 9E173EE03A996655ED2311253DA58EB9 /* RenderNode.swift */, - 0F59122698AC6FE35BC3BC2998203C14 /* Repeater.swift */, - E3D75AB0F4CDA59E7BB16BD81745B956 /* Shape.swift */, - E10CAC47530EA0936ECF4F5B18F41899 /* ShapeCompositionLayer.swift */, - 7B96F646D146FE43BCB3C820D9573D99 /* ShapeContainerLayer.swift */, - B86684B080F0E3D4C1D38F093601783B /* ShapeItem.swift */, - 8AF96553AC1A4981ED8F74D3571CB86C /* ShapeLayerModel.swift */, - FBCF7000A4C9DC9C24B02BFE17451F85 /* ShapeNode.swift */, - FE6DB58D8654DC8B5F88E4D12FD74409 /* ShapeRenderLayer.swift */, - 8DBE44442938F2CBE3624C972232C24E /* ShapeTransform.swift */, - CEF8701D965CC5D013B9CC49B8B228E8 /* SingleValueProvider.swift */, - C1C78482FC811B81F0499146DB6F8592 /* SizeValueProvider.swift */, - B9C5DEABB0B24A9A6A4FB076F829A033 /* SolidCompositionLayer.swift */, - 187B43272E4EAEF4B35EA65BBA8B1334 /* SolidLayerModel.swift */, - 8EE689E19EA5C8EF636167214A787615 /* Star.swift */, - DA0CD727B37100536B0582E4ACA83844 /* StarNode.swift */, - 6BB9978D69E4ABC8F7AE8425D9E1F048 /* StringExtensions.swift */, - DBCD73AF1D7111668851A56E369B30AF /* Stroke.swift */, - 84C11F8849A9F9FA96193BDF6D47B305 /* StrokeNode.swift */, - DB0EBEFA02E716358C707E8E92A56CC2 /* StrokeRenderer.swift */, - 75A55C9E6101E233016AB66DD634B926 /* TextAnimator.swift */, - 9CF0A7BE4E58D6697CCF7D0FA0CBBDFB /* TextAnimatorNode.swift */, - 18C8E244ED61A9B57F738C5DB46FCC12 /* TextCompositionLayer.swift */, - 5DDC53D7CBF3A251E463BB224DD12DCE /* TextDocument.swift */, - 22BBD822E6408AD8D9D1D46DBCBA9381 /* TextLayer.swift */, - 7639C506BC1DBC0CA831E0E2A106D658 /* TextLayerModel.swift */, - A1BFB72AD30F0DE0585E804791B833D6 /* Transform.swift */, - F1CD9DD5A9448FED236DB4E8AAF6C8A4 /* Trim.swift */, - E0CB865665244183E4B9659E94101F9B /* TrimPathNode.swift */, - 3D5C4656A9953B89898D46D2942F6E89 /* UIColorExtension.swift */, - D80EB9AAB47357453596394BFD2FBD6D /* ValueContainer.swift */, - A422591030AC5B802703CAAFA3536ED3 /* Vectors.swift */, - 2AD410EF9494BB5D4662CBBE59C2C06C /* VectorsExtensions.swift */, - CA0DA1B170B1BF48603D3494C22C1A5A /* Support Files */, - ); - name = "lottie-ios"; - path = "lottie-ios"; - sourceTree = ""; - }; - 2A51557057FA173A61DBFFD784D3E10D /* MaterialComponents */ = { - isa = PBXGroup; - children = ( - 6668C74F59A9263767D39E92D8EC1380 /* AnimationTiming */, - B804F1C35AC6246E197C691500949F79 /* Availability */, - FDD60C62F8B0E342211EE4076CD31BEA /* Buttons */, - 7238C8EF7151CBF33AFBFA36C9499A2A /* Cards */, - 4CF444DEE5745EE8B1A83511D00B308C /* Elevation */, - 0CE2657B3BF086744C29E6B209CFDE15 /* Ink */, - CCFA8110E1E64DA6077C0338044C3E4B /* private */, - 69BA61372DCE0F8E82D5724DF36FFA19 /* Ripple */, - A144D5F9E55266835341542282BF0476 /* Shadow */, - D462E74D0157D8AFD873CF8DF7F4AF97 /* ShadowElevations */, - C50C54DDDB0DD81635159B9C32709868 /* ShadowLayer */, - A4690F5B79FFBD6E9DA16E0A3DB0C5AC /* ShapeLibrary */, - 6EFCCFBAFC9CA7F391382F9E21C659A9 /* Shapes */, - 6566FDA23C9DB31DF0C67300D4266218 /* Support Files */, - CAE3BBA14C7998BA5F99F91C08201D62 /* Typography */, - ); - name = MaterialComponents; - path = MaterialComponents; - sourceTree = ""; - }; - 420A7BACED161C14C7AD7697744FD6E6 /* Targets Support Files */ = { - isa = PBXGroup; - children = ( - B85D66BE51608559AC086C64C5C8BF7C /* Pods-ios */, - A890DC0BE85742E0FA551932F177000B /* Pods-iosTests */, - ); - name = "Targets Support Files"; - sourceTree = ""; - }; - 42C82A0937E7B91672FB4E7A426B9951 /* Base */ = { - isa = PBXGroup; - children = ( - 8D0323B0058AFC25122470F8D9558B7D /* MaterialIcons.h */, - C364DE9E9FEDDD730DBF09F77CB0B143 /* MDCIcons.h */, - 3A7AEC8A27ED687F3745E5FF8E0FEB75 /* MDCIcons.m */, - 3FCF28462DDA6182768A18DEE727FA6C /* MDCIcons+BundleLoader.h */, - ); - name = Base; - sourceTree = ""; - }; - 45CC7D2BF1DD5127F441F274153CD862 /* Application */ = { - isa = PBXGroup; - children = ( - 7CE46363B5ADC47CE9B57A3335A26016 /* MaterialApplication.h */, - B27333CDD645D2D5CF612BB67776A72B /* UIApplication+MDCAppExtensions.h */, - AC2834BC1BEF7C025DE72333BE447006 /* UIApplication+MDCAppExtensions.m */, - ); - name = Application; - sourceTree = ""; - }; - 4CF444DEE5745EE8B1A83511D00B308C /* Elevation */ = { - isa = PBXGroup; - children = ( - 8DD0AD806399CC6AE3AA2B4F75C77C23 /* MaterialElevation.h */, - 26833FC1284ED4F21560352EB2FA83B1 /* MDCElevatable.h */, - BF7CDBB13E9F0AC3175BDF383C468787 /* MDCElevationOverriding.h */, - D2F86DF24DDC31E73E6DC1AD1E8E22E6 /* UIColor+MaterialElevation.h */, - 8C64B5F902ABF8446821C23B3779E31D /* UIColor+MaterialElevation.m */, - DAA556AD9FDEF109652EAA16FF14D299 /* UIView+MaterialElevationResponding.h */, - 5EA8C38C23FD8FDD5FE62F2CFD7B55B9 /* UIView+MaterialElevationResponding.m */, - ); - name = Elevation; - sourceTree = ""; - }; - 54004DD572C0D1AC3C340551497E2194 /* Support Files */ = { - isa = PBXGroup; - children = ( - BBCBAB7ADDB2D552DB46A6DEE6C9F5A7 /* Chatto.modulemap */, - B93D411155688717E99B55AE5D57CF10 /* Chatto-dummy.m */, - C59B9A40073373B78D37C4937FD3E223 /* Chatto-Info.plist */, - B1F29114317DB38E4711B41B1C6376C9 /* Chatto-prefix.pch */, - DE1B915229A73A86A29689D51EBCEE8E /* Chatto-umbrella.h */, - 8186408B3030AB74BE1331A431FB83D4 /* Chatto.debug.xcconfig */, - C5494CF8D45C3C7880D1C7684088125B /* Chatto.release.xcconfig */, - ); - name = "Support Files"; - path = "../Target Support Files/Chatto"; - sourceTree = ""; - }; - 5C1CCF8EE2CEEFA1A058DC0BBC9FDF58 /* MDFInternationalization */ = { - isa = PBXGroup; - children = ( - B62785530D197000F35E1FA3A916DD4D /* MDFInternationalization.h */, - 49F4CF11ECB97D9BF5A1760B339EF0D8 /* MDFRTL.h */, - FC7593B690F872386EAC6DA2F85BD36D /* MDFRTL.m */, - AFC0EDA9602EEDC9C5FB7C3250669BD9 /* NSLocale+MaterialRTL.h */, - 1A85C401C2117402508517940B47B11D /* NSLocale+MaterialRTL.m */, - 5AA45B2919A91AAAC163BE6346D14AC0 /* NSString+MaterialBidi.h */, - BC918096888E65550CADD29286F70E6B /* NSString+MaterialBidi.m */, - 2D533D29C0552AC35D5207E01BF6EB55 /* UIImage+MaterialRTL.h */, - 4DE016D373F57DCED6E20186B20A6596 /* UIImage+MaterialRTL.m */, - 1626951F62B660B4C8E4DF8F8D5FFB67 /* UIView+MaterialRTL.h */, - A2783799D5308869A35311BBDF845364 /* UIView+MaterialRTL.m */, - 63B93E12B391EF766B0A1A6EBC6727C3 /* Support Files */, - ); - name = MDFInternationalization; - path = MDFInternationalization; - sourceTree = ""; - }; - 63B72EF56693213EBD10E1799FD5D999 /* Icons */ = { - isa = PBXGroup; - children = ( - 42C82A0937E7B91672FB4E7A426B9951 /* Base */, - F9CF631A6EA3425C2C202FD24CC328CC /* ic_check_circle */, - ); - name = Icons; - sourceTree = ""; - }; - 63B93E12B391EF766B0A1A6EBC6727C3 /* Support Files */ = { - isa = PBXGroup; - children = ( - E1470EA198559C0288DDF4068CAFB6ED /* MDFInternationalization.modulemap */, - 8F1F807B149E883C56B85C5C055D651D /* MDFInternationalization-dummy.m */, - 1D76179FC8700321E982CBF63CBE974C /* MDFInternationalization-Info.plist */, - 5278EC208A2BA0E35B57E4AE2C020987 /* MDFInternationalization-prefix.pch */, - B120981A19A9C956A2941EB064D1FD1D /* MDFInternationalization-umbrella.h */, - BE5AE749191583AB288E955A216FBDEE /* MDFInternationalization.debug.xcconfig */, - 43E52D204C785271C19FE614C61534A4 /* MDFInternationalization.release.xcconfig */, - ); - name = "Support Files"; - path = "../Target Support Files/MDFInternationalization"; - sourceTree = ""; - }; - 6566FDA23C9DB31DF0C67300D4266218 /* Support Files */ = { - isa = PBXGroup; - children = ( - 3198F273FE8A5D0B39F6AA64EB844FAD /* MaterialComponents.modulemap */, - 8F3ED95F3D3F150E9EF3F7B365B3BE9C /* MaterialComponents-dummy.m */, - 5FD3683EBDED4FBCF7CF58DBB4864E15 /* MaterialComponents-Info.plist */, - 3E7BEEB423FD0BB8206D7833EAFB5A94 /* MaterialComponents-prefix.pch */, - 6741DCC6A7B256467254EDFBBDF05B3F /* MaterialComponents-umbrella.h */, - 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */, - 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */, - BF9EDA0CCF4ACD15ECC442F77B0DD927 /* ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist */, - ); - name = "Support Files"; - path = "../Target Support Files/MaterialComponents"; - sourceTree = ""; - }; - 6668C74F59A9263767D39E92D8EC1380 /* AnimationTiming */ = { - isa = PBXGroup; - children = ( - 37F289E6F2E521F6C7C3C1203689674C /* CAMediaTimingFunction+MDCAnimationTiming.h */, - A4D99A5054C55BC32C98A5EF91CB5BDD /* CAMediaTimingFunction+MDCAnimationTiming.m */, - B443745DA64B8F614899680B793F7108 /* MaterialAnimationTiming.h */, - E69B2A401E928B1CBB9CD4B02BEE853D /* UIView+MDCTimingFunction.h */, - 8D1A663AAA238F5A6580850607B58FB9 /* UIView+MDCTimingFunction.m */, - ); - name = AnimationTiming; - sourceTree = ""; - }; - 69BA61372DCE0F8E82D5724DF36FFA19 /* Ripple */ = { - isa = PBXGroup; - children = ( - 33FE8470F10646E30DF3366F83774F58 /* MaterialRipple.h */, - E81ECAF8BBE1DB54276ED819382CA9FA /* MDCRippleLayer.h */, - 2931B1BB799911DD00AA57C202BA2A6F /* MDCRippleLayer.m */, - 6F6DE9329322864658D938C211EAFA09 /* MDCRippleLayerDelegate.h */, - 62A2F94AFB07146E8A9A5DC4985F19BA /* MDCRippleTouchController.h */, - 5DC202126AC7DEA521D46EACE0A8CB33 /* MDCRippleTouchController.m */, - D6E67B621B39259F3CED1DE2F43E57AE /* MDCRippleTouchControllerDelegate.h */, - 31F194A86821D5801BEACFA2DADF397D /* MDCRippleView.h */, - 65D2F1380362B7EE62D2678F79650A3A /* MDCRippleView.m */, - A71810DBABD1DE3268F0A777230A85A2 /* MDCRippleViewDelegate.h */, - 3F6AA23B87CFACBF3F6DA8E1FFE48A3A /* MDCStatefulRippleView.h */, - 1BD15A4D6488FC7DCA7F0F18E0CAC1B9 /* MDCStatefulRippleView.m */, - ); - name = Ripple; - sourceTree = ""; - }; - 6EFCCFBAFC9CA7F391382F9E21C659A9 /* Shapes */ = { - isa = PBXGroup; - children = ( - 6A7B2BF1390CAC2A1A79D280BA2AA739 /* MaterialShapes.h */, - 7E28ED602A75DB92340D7444DB1995A1 /* MDCCornerTreatment.h */, - 2B2BD90586E1B7C4656D777C09F8F99D /* MDCCornerTreatment.m */, - 1E786DB3BAA72871D457F5DC195A08AD /* MDCEdgeTreatment.h */, - 19E09DB898B5D3E34265E0EE3D2FB559 /* MDCEdgeTreatment.m */, - 9D1132D92154CA46250E791A60FC2C43 /* MDCPathGenerator.h */, - 8246440519C85238FCFCF8F58392A86F /* MDCPathGenerator.m */, - 909F969A28FE09D5039EF1A274202B6D /* MDCRectangleShapeGenerator.h */, - 5FB83587411063DC53155C1A2C082DD3 /* MDCRectangleShapeGenerator.m */, - 4E0B142ACFD6D223F820B0A902B17ADB /* MDCShapedShadowLayer.h */, - FDEEADA23D6F4C3DFE04A4117338D768 /* MDCShapedShadowLayer.m */, - 4157CBEADF18154FD243E39C50468258 /* MDCShapedView.h */, - BBF566D92B960CE6B69285672FDE16A8 /* MDCShapedView.m */, - 46AE7E00528585497EA59CD94F669E3A /* MDCShapeGenerating.h */, - A287971E3E70E72ECCDFC5485A8B6D35 /* MDCShapeMediator.h */, - F6BB438AED2F3B6F61B65BEBABF41B1F /* MDCShapeMediator.m */, - ); - name = Shapes; - sourceTree = ""; - }; - 7238C8EF7151CBF33AFBFA36C9499A2A /* Cards */ = { - isa = PBXGroup; - children = ( - A6DCF5983D0F4C2FB0E19A0D320B789B /* MaterialCards.h */, - 11A09793C107A7840B6234B0400A787E /* MDCCard.h */, - 0D46DD556D1F791A1853E2A6E2506FF6 /* MDCCard.m */, - B518C0072DFC3DBA1FA3C49569B1F3DA /* MDCCardCollectionCell.h */, - 6A035D2874E49F84C6A8FF8FBF6503C8 /* MDCCardCollectionCell.m */, - 4FF269172DE0E3442E369BAF00EAB3CD /* UICollectionViewController+MDCCardReordering.h */, - 9A370E644FBCA6F70723C4C1D6720158 /* UICollectionViewController+MDCCardReordering.m */, - ); - name = Cards; - sourceTree = ""; - }; - 7D6083E3CF506A7CD7A7FDACF3C3F86F /* Math */ = { - isa = PBXGroup; - children = ( - 98EB1455F30B28A8F4F59C33E8CB38D2 /* MaterialMath.h */, - D3C80D0A870376D3DAB74C8D106BFF8C /* MaterialMathDummy.m */, - 4AE9A7C0A475863CB257C9866B1EB05E /* MDCMath.h */, - ); - name = Math; - sourceTree = ""; - }; - 8DBB1FD9C1111191D999111C75E2985E /* Products */ = { - isa = PBXGroup; - children = ( - 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */, - BE3893C08724F3D82CF1D199CF98F274 /* Chatto */, - 51BA97E8B5085EFFB47BC9C0B785CEA7 /* lottie-ios */, - A34157B3708EDB7D17422B3D92F591D2 /* MaterialComponents */, - 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */, - 9A2AC0B8BAC6EF061EBC28825653DF99 /* MDFInternationalization */, - FB8220FA4D9117310AEFC629A6EE4A08 /* MDFTextAccessibility */, - DB84425C5097649DB7039BCDFCA4DB6C /* Pods-ios */, - 924ED3C5CACD9B01ED8F6ACDCE0843EE /* Pods-iosTests */, - ); - name = Products; - sourceTree = ""; - }; - 97EE99652D0948DBD9E105410379C433 /* Chatto */ = { - isa = PBXGroup; - children = ( - 6102438DC85CE0EBEE3E8E37512F0869 /* BaseChatItemPresenter.swift */, - 30C3EF2AB0724BFD75D6FE28F5429702 /* BaseChatViewController.swift */, - D7F58CC76B154EE1FFCEBFD29458765F /* BaseChatViewController+CellPanGestureHandler.swift */, - EA378A26B99BBD94560AE1C8C02B11F6 /* BaseChatViewController+Changes.swift */, - 0594042CC23BC8F1D7592815BDBFC2C6 /* BaseChatViewController+Presenters.swift */, - 0314F72C05B7FF63F27BE4646BD8305E /* BaseChatViewController+Scrolling.swift */, - 6C4F064EEF86F1829D1B928D11963739 /* BaseChatViewControllerView.swift */, - 2C197D9AE91D805F5FFDD745A3332768 /* CellPanGestureHandler.swift */, - CA6F96BF2DFC0E11E6A55539B9225C94 /* ChatCollectionViewLayout.swift */, - BAA843D119FBBE1873DB868B2C42F47A /* ChatDataSourceProtocol.swift */, - 777B7794311A16E26C00B24639C365CB /* ChatItemCompanion.swift */, - 2FB631B982623359A1D4E17E34F6369B /* ChatItemCompanionCollection.swift */, - 06460E2CD793BBF2C535219D13A50C05 /* ChatItemPresenterFactory.swift */, - A488EF6B3932789A199EFF0E48669A8F /* ChatItemProtocolDefinitions.swift */, - FBD0995EC979D7375ABF14FB9A0A9EB0 /* ChatLayoutConfiguration.swift */, - BFA5A232C8B4D4671F587B33DCEF5FB4 /* CollectionChanges.swift */, - 6A9EA755014DD5DD4C27D7ECD93783FD /* DummyChatItemPresenter.swift */, - BA603856ED143A5A0536C7F9F8785FFF /* InputPositionControlling.swift */, - 97374F6103D866145B36386C14D1D8E5 /* KeyboardTracker.swift */, - 0F40483DF444914DE771891BCED1F593 /* ReplyFeedbackGenerator.swift */, - 536C19C18245423A6AFB8166C8F93F35 /* SerialTaskQueue.swift */, - 1E9DE4BB5450E42050B34C1355AB7E72 /* Utils.swift */, - 54004DD572C0D1AC3C340551497E2194 /* Support Files */, - ); - name = Chatto; - path = Chatto; - sourceTree = ""; - }; - A144D5F9E55266835341542282BF0476 /* Shadow */ = { - isa = PBXGroup; - children = ( - ABED2A4D6F3787803F5376C66CB9544C /* MaterialShadow.h */, - 9625E67A414049AFFBCF73EDB3F00B1B /* MDCShadow.h */, - 27B3D295F42CEAEA35B48C43AD477C61 /* MDCShadow.m */, - C73D5045364BD3202565B4FDB54AE37A /* MDCShadowsCollection.h */, - 5D714F498A6A5E7FC8FB92A3819A449A /* MDCShadowsCollection.m */, - ); - name = Shadow; - sourceTree = ""; - }; - A4690F5B79FFBD6E9DA16E0A3DB0C5AC /* ShapeLibrary */ = { - isa = PBXGroup; - children = ( - 90A761C8F692CCA4FDA5BFC5DEBACE74 /* MaterialShapeLibrary.h */, - 40FC4A25E6CBE6CD94D65C7642079986 /* MDCCornerTreatment+CornerTypeInitalizer.h */, - 24866442BD723570188362AFBC61ECA3 /* MDCCornerTreatment+CornerTypeInitalizer.m */, - 1A09CD4BB399191B8387A7D72E4B2909 /* MDCCurvedCornerTreatment.h */, - CBBE66515626EA5EE7282EF95C8275B3 /* MDCCurvedCornerTreatment.m */, - DB1E998F48C65751461A4CB1E13E45C5 /* MDCCurvedRectShapeGenerator.h */, - FEC9B0599DF18776D54CF802CB9FFFD9 /* MDCCurvedRectShapeGenerator.m */, - 43693CCEC265B75D660F71A9422AC97E /* MDCCutCornerTreatment.h */, - EA9BAA45A6BA523F01C4735963DDDADC /* MDCCutCornerTreatment.m */, - 2882BFF487CA5879D2F68C6B944595F5 /* MDCPillShapeGenerator.h */, - 2102D1FE11630B5091FE865DDD43F05A /* MDCPillShapeGenerator.m */, - 95DF7386D55C9F9642DC5077C341E35E /* MDCRoundedCornerTreatment.h */, - 3A16E9C361081323779110A2E0EE4F83 /* MDCRoundedCornerTreatment.m */, - 34EBCCA2E4424A06FB1C11FFF86AE0E2 /* MDCSlantedRectShapeGenerator.h */, - 3EEB4FCAEBF9555B90A5EC48FE3D6B4F /* MDCSlantedRectShapeGenerator.m */, - AC80A65D7F1D3E258FEC205047C3EB0A /* MDCTriangleEdgeTreatment.h */, - 3B5993FADA59638CD437C1C18E3DDA1D /* MDCTriangleEdgeTreatment.m */, - ); - name = ShapeLibrary; - sourceTree = ""; - }; - A890DC0BE85742E0FA551932F177000B /* Pods-iosTests */ = { - isa = PBXGroup; - children = ( - A9B79521A3553AD1005FA6A9670CEBF2 /* Pods-iosTests.modulemap */, - B9158E5A2523A49B53CFB65D466371AE /* Pods-iosTests-acknowledgements.markdown */, - 341DED9C8A182716EE1504F58E3BBA27 /* Pods-iosTests-acknowledgements.plist */, - F5922665F1DC85A6D8A92E2E648D19BE /* Pods-iosTests-dummy.m */, - E741DFF8BC3B7CB733B6417AE217AEC1 /* Pods-iosTests-frameworks.sh */, - 5923674729AC49BE14806A1AEB0FB8DE /* Pods-iosTests-Info.plist */, - 1CEC781B13B51E108C5658001DCCCD2A /* Pods-iosTests-umbrella.h */, - 72E3FDB351A03387E5608B6C84700D31 /* Pods-iosTests.debug.xcconfig */, - F9C9C35B39EFD7AD82253D0A2AD91B56 /* Pods-iosTests.release.xcconfig */, - ); - name = "Pods-iosTests"; - path = "Target Support Files/Pods-iosTests"; - sourceTree = ""; - }; - A95CB1598E998B48CD1D973159ECC08E /* Support Files */ = { - isa = PBXGroup; - children = ( - 32E84BC23BD4ED514C1BB73378A0EF16 /* Alamofire.modulemap */, - 7DF304359AEBCFADD6BBD420523B223F /* Alamofire-dummy.m */, - EA26F20034BDBF94780CF01106D4CD64 /* Alamofire-Info.plist */, - 9A247D7873C2106F7E0006C6D3FA0DC9 /* Alamofire-prefix.pch */, - 1742EDF4DDCBB582B88771AEB1201E1A /* Alamofire-umbrella.h */, - C43092A50861A2F799989439A7DCBCAF /* Alamofire.debug.xcconfig */, - 3CDCB1C8D529F8DE664D2E4C96BD3A7D /* Alamofire.release.xcconfig */, - ); - name = "Support Files"; - path = "../Target Support Files/Alamofire"; - sourceTree = ""; - }; - AABDEFB693E15B50C7698B1026DED0F3 /* Resources */ = { - isa = PBXGroup; - children = ( - 427AFF770A87FFC8BA489ECCD299D58F /* MaterialIcons_ic_check_circle.xcassets */, - ); - name = Resources; - sourceTree = ""; - }; - B804F1C35AC6246E197C691500949F79 /* Availability */ = { - isa = PBXGroup; - children = ( - E58F9B821896E6DE3629364AB9B21F35 /* MaterialAvailability.h */, - 3647DF7F6475E74ED3EB0CD6926B63DB /* MDCAvailability.h */, - ); - name = Availability; - sourceTree = ""; - }; - B85D66BE51608559AC086C64C5C8BF7C /* Pods-ios */ = { - isa = PBXGroup; - children = ( - A5ED19C47A3B111AEEAE6C09A8E508BB /* Pods-ios.modulemap */, - 9ECD5C7C84C99618AF2620886CE9964E /* Pods-ios-acknowledgements.markdown */, - BF467C869E0EBC17B9CB2B58A166300C /* Pods-ios-acknowledgements.plist */, - 100C94E08EE07B09E19F115713E5B184 /* Pods-ios-dummy.m */, - 25A7B53BBB25EA665175DB0281DBDE11 /* Pods-ios-frameworks.sh */, - 952959F2E1983393A6EE1CAF37FB84E1 /* Pods-ios-Info.plist */, - D720E1DADE8B1BB169230ADFA11012E3 /* Pods-ios-umbrella.h */, - 0C07D8193799950468B6CF3885E1193B /* Pods-ios.debug.xcconfig */, - B1F119B189824C232697C892AF030E98 /* Pods-ios.release.xcconfig */, - ); - name = "Pods-ios"; - path = "Target Support Files/Pods-ios"; - sourceTree = ""; - }; - B94D7768568A9992200DB461E8CF687F /* Frameworks */ = { - isa = PBXGroup; - children = ( - D21A228721E7A09BEF782E68D4232144 /* iOS */, - ); - name = Frameworks; - sourceTree = ""; - }; - BB7FF24AAB31B14A5EC03B11DF4CCECB /* MDFTextAccessibility */ = { - isa = PBXGroup; - children = ( - F5CCE660C00123EB989E5679091A134A /* MDFColorCalculations.h */, - 3BEEEC76296F936D8FA70CA0604BB74F /* MDFColorCalculations.m */, - 7053BE770B19CADCC67259D2058FBFE5 /* MDFImageCalculations.h */, - 408FEF5380BBEF2A9A4EF9878452691D /* MDFImageCalculations.m */, - 8C2F6B0AED78566F963846D8D10A8CC9 /* MDFTextAccessibility.h */, - 14889A17A94EB87B76300716DD2A9F36 /* MDFTextAccessibility.m */, - 97A0DF460C679110DBBD0ED8B315B64D /* MDFTextAccessibility-Bridging-Header.h */, - 4ADBAC831F15371103E6075A7099776A /* NSArray+MDFUtils.h */, - A2C83FB5770E9E4004C80C5217E3CD69 /* NSArray+MDFUtils.m */, - 1AF48580728743ABEEC0DA59D34B78E2 /* Support Files */, - ); - name = MDFTextAccessibility; - path = MDFTextAccessibility; - sourceTree = ""; - }; - C50C54DDDB0DD81635159B9C32709868 /* ShadowLayer */ = { - isa = PBXGroup; - children = ( - B3F1C077ED9B83ED31FAF400299DC965 /* MaterialShadowLayer.h */, - 6ED311731D2D73DBC56836B34F3D634A /* MDCShadowLayer.h */, - 561E46EAC325F966EA9DBA55F0689607 /* MDCShadowLayer.m */, - ); - name = ShadowLayer; - sourceTree = ""; - }; - C7D23E365D4BA5B320803F40CDCBC339 /* Color */ = { - isa = PBXGroup; - children = ( - A575DBC967BD1B9824384DE5EAC71824 /* MaterialColor.h */, - 73500719BEBC67E98C2432BA726CF2BD /* UIColor+MaterialBlending.h */, - 1495FD5410C5BA9748915AE95726D978 /* UIColor+MaterialBlending.m */, - B829E8E11D02D1970F8C8FF989A82B64 /* UIColor+MaterialDynamic.h */, - 1555D5D35385492B8D027E811BE666CB /* UIColor+MaterialDynamic.m */, - ); - name = Color; - sourceTree = ""; - }; - CA0DA1B170B1BF48603D3494C22C1A5A /* Support Files */ = { - isa = PBXGroup; - children = ( - 1A9AE3381193F742879F523CCE471749 /* lottie-ios.modulemap */, - 1E5379887AF991A23AF0071E5BBA66A3 /* lottie-ios-dummy.m */, - 21ABC4AABBA66B5F87EF5A5C789FDC8F /* lottie-ios-Info.plist */, - 7821F9D3A25C026AA6AFACB2A720A953 /* lottie-ios-prefix.pch */, - 631CC31B1866E988A56C286024B8A805 /* lottie-ios-umbrella.h */, - 615BC7DD9F6466F845804084D0B12D66 /* lottie-ios.debug.xcconfig */, - 1CBA47FDA3E910C7EDFE324EFA897551 /* lottie-ios.release.xcconfig */, - ); - name = "Support Files"; - path = "../Target Support Files/lottie-ios"; - sourceTree = ""; - }; - CAE3BBA14C7998BA5F99F91C08201D62 /* Typography */ = { - isa = PBXGroup; - children = ( - 2FA60992213EE87D4775A402EF9DE39B /* MaterialTypography.h */, - 42A318810EC6F37DE9379EF21DE434C3 /* MDCFontScaler.h */, - 0D37C9BDBB809F1F5B2D0212FF9CFE99 /* MDCFontScaler.m */, - 87107EE4D974CAE959A375B662F4204F /* MDCFontTextStyle.h */, - 275AD0BC20557E9DC939C53DFF5BB2F3 /* MDCFontTraits.h */, - 789CAA7B6C1E469B4554820ADC2147D3 /* MDCFontTraits.m */, - 9DE63B387DA24E48BA2EDE84C68DC8DE /* MDCTypography.h */, - 5264280FEB93B40117162EB11A934890 /* MDCTypography.m */, - 8D638A90314C4C723DF58CBA2D02DCF3 /* MDCTypographyUtilities.h */, - 49FB87A2C6705951A697BFD94E3FFB81 /* MDCTypographyUtilities.m */, - B867765B8E02C5AE1B3C6A2B771DE65C /* UIFont+MaterialScalable.h */, - 8CE15CF0E1981C1CA722C1B5121C088C /* UIFont+MaterialScalable.m */, - 2AB8B4FEB07692832F02CED08212015A /* UIFont+MaterialSimpleEquality.h */, - 039BA34B5A5B9FE2B7971FE6E64557BB /* UIFont+MaterialSimpleEquality.m */, - 6E28A208954B54643898470FABAC2905 /* UIFont+MaterialTypography.h */, - D390D3DEF1579285A22574E5E6EA97D0 /* UIFont+MaterialTypography.m */, - F49594B80BEB495C857FE4A0C034A387 /* UIFont+MaterialTypographyPrivate.h */, - FE68F0EF6188796115A2408E20255E7C /* UIFont+MaterialTypographyPrivate.m */, - 4040341BD83B0FAE1A20FF1D71955BB5 /* UIFontDescriptor+MaterialTypography.h */, - EEBE2EE981029A2CA291F17EA8723B03 /* UIFontDescriptor+MaterialTypography.m */, - ); - name = Typography; - sourceTree = ""; - }; - CCFA8110E1E64DA6077C0338044C3E4B /* private */ = { - isa = PBXGroup; - children = ( - 45CC7D2BF1DD5127F441F274153CD862 /* Application */, - C7D23E365D4BA5B320803F40CDCBC339 /* Color */, - 63B72EF56693213EBD10E1799FD5D999 /* Icons */, - 7D6083E3CF506A7CD7A7FDACF3C3F86F /* Math */, - ); - name = private; - sourceTree = ""; - }; - CF1408CF629C7361332E53B88F7BD30C = { - isa = PBXGroup; - children = ( - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, - B94D7768568A9992200DB461E8CF687F /* Frameworks */, - D284217121FE14E169EC541EA0329E52 /* Pods */, - 8DBB1FD9C1111191D999111C75E2985E /* Products */, - 420A7BACED161C14C7AD7697744FD6E6 /* Targets Support Files */, - ); - sourceTree = ""; - }; - D21A228721E7A09BEF782E68D4232144 /* iOS */ = { - isa = PBXGroup; - children = ( - 870DC3835055FF27A861C151FF69B97C /* CFNetwork.framework */, - 3117010738ED3E7B5080810EBF99FBBD /* CoreGraphics.framework */, - E56704CF70A00F501482C6EEC5A3EF8F /* Foundation.framework */, - EB57E704596E48FF3325A1F46B6E783C /* QuartzCore.framework */, - 617F2D7F52382550906F7F058A9681F9 /* UIKit.framework */, - ); - name = iOS; - sourceTree = ""; - }; - D284217121FE14E169EC541EA0329E52 /* Pods */ = { - isa = PBXGroup; - children = ( - 144970CAC0814448ACFB4EE458FC2F64 /* Alamofire */, - 97EE99652D0948DBD9E105410379C433 /* Chatto */, - 21AC7CA70041658C6EBD3A1F4775F58C /* lottie-ios */, - 2A51557057FA173A61DBFFD784D3E10D /* MaterialComponents */, - 5C1CCF8EE2CEEFA1A058DC0BBC9FDF58 /* MDFInternationalization */, - BB7FF24AAB31B14A5EC03B11DF4CCECB /* MDFTextAccessibility */, - ); - name = Pods; - sourceTree = ""; - }; - D462E74D0157D8AFD873CF8DF7F4AF97 /* ShadowElevations */ = { - isa = PBXGroup; - children = ( - 0056C47896966D18A6A1A5446B66D105 /* MaterialShadowElevations.h */, - D0F908B38DD2B1F35BBC0B8F214C6DC0 /* MaterialShadowElevationsDummy.m */, - 5BA153CC755CD4895140457FB4274997 /* MDCShadowElevations.h */, - ); - name = ShadowElevations; - sourceTree = ""; - }; - F9CF631A6EA3425C2C202FD24CC328CC /* ic_check_circle */ = { - isa = PBXGroup; - children = ( - DF79A6BB5BC283B6F1FFF7394D1ABD51 /* MaterialIcons+ic_check_circle.h */, - 2AF20B708D8368FA42456BB1F0DF92F7 /* MaterialIcons+ic_check_circle.m */, - AABDEFB693E15B50C7698B1026DED0F3 /* Resources */, - ); - name = ic_check_circle; - sourceTree = ""; - }; - FDD60C62F8B0E342211EE4076CD31BEA /* Buttons */ = { - isa = PBXGroup; - children = ( - 8DA90325405F33A5808A47E8BFFAEE6F /* MaterialButtons.h */, - 97BE17BC31FAF7F8B04D6EFFBBE78C80 /* MDCButton.h */, - F90CBF2D1339BB6CDDAFF78CC3B59761 /* MDCButton.m */, - 4C628C67659ABBADF02BA056396F87E1 /* MDCButton+Subclassing.h */, - 985D00F7F6511C37B9DECF5DBEB3B451 /* MDCFlatButton.h */, - 6828FC052E6421784F770E0D6078DDB6 /* MDCFlatButton.m */, - F873EC876A8574DA2C7425CA4C3BC418 /* MDCFloatingButton.h */, - C7C60015DC526524C5DC274704897564 /* MDCFloatingButton.m */, - 4838FCADE32426C4BC81299CE2906DE1 /* MDCFloatingButton+Animation.h */, - D8F1688277322547EA5D6CCDF2C94C1F /* MDCFloatingButton+Animation.m */, - 4148219036DBEF09983EE603792119B1 /* MDCFloatingButtonModeAnimator.h */, - 9A1732F4D5612B18CF4BCB5A0D5F9A8C /* MDCFloatingButtonModeAnimator.m */, - 1B239243820382C1C6E7038645E10958 /* MDCFloatingButtonModeAnimatorDelegate.h */, - 4BF5C7C1F8F18927BF9A4EBDE9886621 /* MDCRaisedButton.h */, - 9FF5E5B45041E7444D6447C17872668F /* MDCRaisedButton.m */, - ); - name = Buttons; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 14FF1799C5ADBC71E1DB963F2AF8853D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 0B399DCF32F8FE4F09B03B6E7B65E0D1 /* Alamofire-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 283F66161952BCC65E708C095C437A15 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 023569941EF2F377A6F87A87062B479A /* MDFColorCalculations.h in Headers */, - 59DB1972DC0FB82B837041D7ADA10E9B /* MDFImageCalculations.h in Headers */, - E46C272C4B95CBC92CDA9A2519D37833 /* MDFTextAccessibility.h in Headers */, - EAAFAB74BE099CA0217F13D56892CC50 /* MDFTextAccessibility-Bridging-Header.h in Headers */, - 84F43C1B8B0B179BBFF86DE99BBF4422 /* MDFTextAccessibility-umbrella.h in Headers */, - 32E691399B269968E2DD2CAD9F66C4F9 /* NSArray+MDFUtils.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 415B9BA66497D555D9D735FC58884B30 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 7D990BA3B83998A2A1ABD69E0F3D44CE /* Pods-ios-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4E88A7409AFD7F544020657A40A06594 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A0E0BC90F446AAFA84B16933CE45D172 /* CAMediaTimingFunction+MDCAnimationTiming.h in Headers */, - FC622CB054985417281FE0FB6755C6DD /* MaterialAnimationTiming.h in Headers */, - 85AF5816CC3948E8200711491BA938E5 /* MaterialApplication.h in Headers */, - D4939BB9A0101F96A5D863184AE21A7F /* MaterialAvailability.h in Headers */, - 27004AABBE0496F9D5C7B3D8450F657B /* MaterialButtons.h in Headers */, - 02E41CCFF8E90ACE9B3F5267F6FE3DB7 /* MaterialCards.h in Headers */, - 4B9A1C30219659C0AA8D1AE79832347B /* MaterialColor.h in Headers */, - 22AA981BFEFAFBA8C6B511348D60D284 /* MaterialComponents-umbrella.h in Headers */, - 9E361C56F23EEFCEBF62E79BF5165AD6 /* MaterialElevation.h in Headers */, - 41A27CED3C6DB6554EBDD9BA47356969 /* MaterialIcons.h in Headers */, - 785E547BC62FE180EDB4F80976888437 /* MaterialIcons+ic_check_circle.h in Headers */, - 2866EB6C78D5E0A3256F868DA33E06DA /* MaterialInk.h in Headers */, - 597AB8E13D0B44321059C630A47807D7 /* MaterialMath.h in Headers */, - E1B1195009B9F9BD2E5EFE2113CFE20A /* MaterialRipple.h in Headers */, - 9D880C5CFBE97A142FC516777217C492 /* MaterialShadow.h in Headers */, - 4B4B75F4B215FD3E854871B3E1F31CD3 /* MaterialShadowElevations.h in Headers */, - C52D6111A5A724CED4C0451F2D8D7F76 /* MaterialShadowLayer.h in Headers */, - 002C20BF2F798D2C64290D641171C7F3 /* MaterialShapeLibrary.h in Headers */, - 96A6F504929F868038D46B5B6A03038B /* MaterialShapes.h in Headers */, - FDE49FD4B92B698131BA846A877E4F5C /* MaterialTypography.h in Headers */, - E9CD4CB2CB433D9920922378F7886DAF /* MDCAvailability.h in Headers */, - 94C1D0EC92D3122730BAC6014910D19F /* MDCButton.h in Headers */, - AEDBDAA38F82EC2FA51759F782D4D5AC /* MDCButton+Subclassing.h in Headers */, - C78971D6EF0C7FFBCF847261C2015F62 /* MDCCard.h in Headers */, - D3B9D2340450E1A03FC812FED6DE209C /* MDCCardCollectionCell.h in Headers */, - A093909FE7269A84364560DDC9375F31 /* MDCCornerTreatment.h in Headers */, - 9DAA6E25378FE244F7D4451A5D35FB51 /* MDCCornerTreatment+CornerTypeInitalizer.h in Headers */, - 7429801C83C663E2AC03DF593EB09F26 /* MDCCurvedCornerTreatment.h in Headers */, - E80D58FB9E5F025ED34C4817E1D03D20 /* MDCCurvedRectShapeGenerator.h in Headers */, - 7B71F5D8CEF38A4E44B101984BF32A22 /* MDCCutCornerTreatment.h in Headers */, - 1D313F4071D041D30364BC379612A7C8 /* MDCEdgeTreatment.h in Headers */, - DF4526CA0220910B45398C91C207AB2A /* MDCElevatable.h in Headers */, - BC95E3D42356C72892097F3C727F8ED4 /* MDCElevationOverriding.h in Headers */, - F59346B37473C393BC5B9573CA75D311 /* MDCFlatButton.h in Headers */, - B713DE1857A1BE1020811FB842D67B1F /* MDCFloatingButton.h in Headers */, - 97030F2617BD73C118C8334624A84458 /* MDCFloatingButton+Animation.h in Headers */, - 5CDEA5D93507BB56778DFE0FF9D755B6 /* MDCFloatingButtonModeAnimator.h in Headers */, - 32DA6FCE9DD284D13AC2A4CE1259320D /* MDCFloatingButtonModeAnimatorDelegate.h in Headers */, - D04A750433B71EFFE369931A5990825E /* MDCFontScaler.h in Headers */, - 9FA8E4B6DCC77FC54C0E72979D87FDDA /* MDCFontTextStyle.h in Headers */, - 7187BA8BB4975F949B9A3E5413EF29E6 /* MDCFontTraits.h in Headers */, - 3C1AF825830E083E899D53A5C2F7E3EB /* MDCIcons.h in Headers */, - 59CA53BBA8B52B7565A5B31E205B5DBE /* MDCIcons+BundleLoader.h in Headers */, - 34426DF353C060CB74307404D7788A7F /* MDCInkGestureRecognizer.h in Headers */, - 048E7F185D045CEAB317FF30D739E39C /* MDCInkLayer.h in Headers */, - FAF429A0D030A7708AC3E1C890CDFBBC /* MDCInkLayerDelegate.h in Headers */, - 6BFC3EBA0293805C586DED141F57D440 /* MDCInkTouchController.h in Headers */, - CA13097C8726C13E7D19984C391E156B /* MDCInkTouchControllerDelegate.h in Headers */, - 5269279BA5DAB06E59635312FF2FCC76 /* MDCInkView.h in Headers */, - E47B9E036187569C65F248B6D527020E /* MDCInkViewDelegate.h in Headers */, - 8BBBA0F8098CEDCDE429C22E32810395 /* MDCLegacyInkLayer.h in Headers */, - 04FBE0F7CABE3A8D6913CC677B5557DB /* MDCLegacyInkLayer+Private.h in Headers */, - A957485EF720B71593D3C8160C44B9DF /* MDCLegacyInkLayerDelegate.h in Headers */, - B3CB521DFC1B2FC4C400466B2636C9DA /* MDCLegacyInkLayerRippleDelegate.h in Headers */, - E70A755D57CC8683634F611358142A89 /* MDCMath.h in Headers */, - 7A42366DBD41A248734C3A9973EC9DE9 /* MDCPathGenerator.h in Headers */, - 336037502C5A263DA6CC3831EF8EFD9F /* MDCPillShapeGenerator.h in Headers */, - BB9A0A0E2A295ECBE4DF5DD2BCC49A6E /* MDCRaisedButton.h in Headers */, - 0EDBF34C06554E9537C4DFCBAF24433E /* MDCRectangleShapeGenerator.h in Headers */, - 8B8F97130DA7C5ACCF2ADBD9A8B8C138 /* MDCRippleLayer.h in Headers */, - A4D811A62B4CD50FEF2943AB7A090055 /* MDCRippleLayerDelegate.h in Headers */, - 0F7ADF590B5BBABFEE77D9E562058102 /* MDCRippleTouchController.h in Headers */, - 370A0DE2184C02814935D61032C2E189 /* MDCRippleTouchControllerDelegate.h in Headers */, - D620FDB5BDD15F3DF283F6989DAEF5C7 /* MDCRippleView.h in Headers */, - 1A3B96F4D5840C14E50F8320D2E81542 /* MDCRippleViewDelegate.h in Headers */, - 74B1E17BF464786B34DEB58A508B48B4 /* MDCRoundedCornerTreatment.h in Headers */, - F129D1B53CBB7D4F4F19F7C651C9EF04 /* MDCShadow.h in Headers */, - 0828664E6967E49C11D8F03D40827F40 /* MDCShadowElevations.h in Headers */, - AA41EBE1B990DE355E95E626BCDE0D20 /* MDCShadowLayer.h in Headers */, - 378706EAE3C525A5A25FD652CF35ED3A /* MDCShadowsCollection.h in Headers */, - B59FA52B2FA220A61E7B371F6902BF61 /* MDCShapedShadowLayer.h in Headers */, - 70BC4C88A01730C10637E04170D18842 /* MDCShapedView.h in Headers */, - 87882C5A446E492E7BC82763056E299E /* MDCShapeGenerating.h in Headers */, - 6FF5AC95881C5994A17183FCD68D9760 /* MDCShapeMediator.h in Headers */, - AD9CE391F7F42CA08809086C88DA94F8 /* MDCSlantedRectShapeGenerator.h in Headers */, - F337417E09334F51565A55905275A05E /* MDCStatefulRippleView.h in Headers */, - 10F1A59D76699A85A3D3F9CE14D68340 /* MDCTriangleEdgeTreatment.h in Headers */, - 3CE69A673E4CCBBC2BF363C34B3E4702 /* MDCTypography.h in Headers */, - 1C63F278B31BE01E9A399C213588B1F5 /* MDCTypographyUtilities.h in Headers */, - 7514339E0BB67279B4151F7E7C9DF20D /* UIApplication+MDCAppExtensions.h in Headers */, - E06D3EE4B89C14DF0F1EACA6DB9DCF87 /* UICollectionViewController+MDCCardReordering.h in Headers */, - 731FB6C1A89663C6273A5ED2A93838A2 /* UIColor+MaterialBlending.h in Headers */, - F03C958A2DC83E362F4E76C1758F2E99 /* UIColor+MaterialDynamic.h in Headers */, - C1C2719D78A6372AF387756B6D2FB07B /* UIColor+MaterialElevation.h in Headers */, - 64A1B6BAE055F3BEAD2244B06B758B47 /* UIFont+MaterialScalable.h in Headers */, - A6487D3314DFDA70D3EF7D709AFD1A17 /* UIFont+MaterialSimpleEquality.h in Headers */, - 82558E399C6304091C91304BC4E9FAD1 /* UIFont+MaterialTypography.h in Headers */, - 74804BD8118FAA4C1A0F62E7A9E8F8B7 /* UIFont+MaterialTypographyPrivate.h in Headers */, - FCA5FAFF46860FFD052B5A96A4253915 /* UIFontDescriptor+MaterialTypography.h in Headers */, - 7EF5AA59D08C107D83A5E3FE51633211 /* UIView+MaterialElevationResponding.h in Headers */, - 0FF000CEEA87B583977819DE456F949B /* UIView+MDCTimingFunction.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4EA3A0C175DC3B08CF7DF0AC686D56CA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 114CD9D207171C75415C5B60E0609675 /* Pods-iosTests-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 61AEBA9D4508B355E6E380F1D4D3988A /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - FFFCB73998E59CCA79007CF915D56E22 /* Chatto-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 82946A5BB19511200C787055222CC155 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 1062EDD2C5C3B8D7A91592F639600D74 /* MDFInternationalization.h in Headers */, - 1DCF9A800AE274F89C29078A6E212B7D /* MDFInternationalization-umbrella.h in Headers */, - 68C81A493C4CD3F1D8425C3C12B6F1E3 /* MDFRTL.h in Headers */, - 3CE30BDD410305F4D3C8C13D12507A4A /* NSLocale+MaterialRTL.h in Headers */, - 9F64E84B54A721D525FB5FEB6B04AD92 /* NSString+MaterialBidi.h in Headers */, - F95134B340BDE8537CEDC91162AE776B /* UIImage+MaterialRTL.h in Headers */, - E56C430EEEEAF6E39FF0942CCD78A061 /* UIView+MaterialRTL.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DAE149930D25CBB9B05F744248F36732 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 535D24555149DB4F16003AEB62B2F4FE /* lottie-ios-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 0B967D7F8561D42493EE289EC8D450D1 /* lottie-ios */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0358E8E05175A7E59A2AEECD12A1801E /* Build configuration list for PBXNativeTarget "lottie-ios" */; - buildPhases = ( - DAE149930D25CBB9B05F744248F36732 /* Headers */, - 37AE70FCA26952762C9821CF88074221 /* Sources */, - 78FC9E52ECE20C8C02B38B218A9F8D6C /* Frameworks */, - D3974EDA7759ECBA83AABE9D7FCFA671 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "lottie-ios"; - productName = Lottie; - productReference = 51BA97E8B5085EFFB47BC9C0B785CEA7 /* lottie-ios */; - productType = "com.apple.product-type.framework"; - }; - 2508BC3B908322C9E85E61AE743C9842 /* Chatto */ = { - isa = PBXNativeTarget; - buildConfigurationList = 8FE49F8617D5D677A3808ABE3B492E82 /* Build configuration list for PBXNativeTarget "Chatto" */; - buildPhases = ( - 61AEBA9D4508B355E6E380F1D4D3988A /* Headers */, - 66C6B183F08D1564AE5191E32440E60D /* Sources */, - 28BA49E77F658083C4A924833FF0CF7D /* Frameworks */, - 3429006454DB433D0F4FBF2F4E05E02F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Chatto; - productName = Chatto; - productReference = BE3893C08724F3D82CF1D199CF98F274 /* Chatto */; - productType = "com.apple.product-type.framework"; - }; - 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */ = { - isa = PBXNativeTarget; - buildConfigurationList = 29235CC975477CF0AE111D8533A857C9 /* Build configuration list for PBXNativeTarget "MDFInternationalization" */; - buildPhases = ( - 82946A5BB19511200C787055222CC155 /* Headers */, - 85B3B64C90CFC972449F93DC4A812BBD /* Copy . Public Headers */, - 75C42EE808FFAC35041568CFE92847AD /* Sources */, - A563BE445A159E73000F25A2BC3B940E /* Frameworks */, - AED6EDF087DBE63D55C51AEE20F3373F /* Resources */, - 498506168756522E454E1B48EC2886A7 /* Create Symlinks to Header Folders */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = MDFInternationalization; - productName = MDFInternationalization; - productReference = 9A2AC0B8BAC6EF061EBC28825653DF99 /* MDFInternationalization */; - productType = "com.apple.product-type.framework"; - }; - 7F4022CD4249985E761F745058CAB372 /* MaterialComponents-MaterialIcons_ic_check_circle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 81EE041A2E698878FED9512843DEEE02 /* Build configuration list for PBXNativeTarget "MaterialComponents-MaterialIcons_ic_check_circle" */; - buildPhases = ( - 04C07BCDE98F7ACB2E4A7BB253EB79D4 /* Sources */, - A46E636AA9FD7591DACA5A99D724E081 /* Frameworks */, - 6ECA9F6DF30D309B065300A72DD99BC8 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "MaterialComponents-MaterialIcons_ic_check_circle"; - productName = MaterialIcons_ic_check_circle; - productReference = 3CA6F19A19E2E67F5778834EA376B306 /* MaterialComponents-MaterialIcons_ic_check_circle */; - productType = "com.apple.product-type.bundle"; - }; - A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */ = { - isa = PBXNativeTarget; - buildConfigurationList = E9D2A6F4024608ED9B15BD3B46315019 /* Build configuration list for PBXNativeTarget "MDFTextAccessibility" */; - buildPhases = ( - 283F66161952BCC65E708C095C437A15 /* Headers */, - 2A5DC5F3AAB66AD530D07A5A2ED44DBF /* Sources */, - 052B815FEFBCEB5CBBA08741F810D971 /* Frameworks */, - 3EED30752BC2816FBBD27B8B9CBF4E0C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = MDFTextAccessibility; - productName = MDFTextAccessibility; - productReference = FB8220FA4D9117310AEFC629A6EE4A08 /* MDFTextAccessibility */; - productType = "com.apple.product-type.framework"; - }; - B4723B5744013DF08BE3A5FEAA286944 /* MaterialComponents */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4D07C4D46B8088CB6927DB0DC7D38E87 /* Build configuration list for PBXNativeTarget "MaterialComponents" */; - buildPhases = ( - 4E88A7409AFD7F544020657A40A06594 /* Headers */, - 390B4779DF669199CC4F894284D2B29B /* Sources */, - B05B2D2B6059C5982EE1D520AA115030 /* Frameworks */, - 8CBB757F4B8EF000D844E9F6690D3F8E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 8907A64A1B702FF7E8F6F71C8D6AE9A2 /* PBXTargetDependency */, - 12A0FCF3F802AE49C0D0CB64AA7E43A7 /* PBXTargetDependency */, - 6C1B5C5B5ECFC4AE40FE227FD0468177 /* PBXTargetDependency */, - ); - name = MaterialComponents; - productName = MaterialComponents; - productReference = A34157B3708EDB7D17422B3D92F591D2 /* MaterialComponents */; - productType = "com.apple.product-type.framework"; - }; - CB5AA7A4FA94F4765AE579D6F4491956 /* Pods-ios */ = { - isa = PBXNativeTarget; - buildConfigurationList = 994FA3DA95C9295DD26DDD2C48D33FDF /* Build configuration list for PBXNativeTarget "Pods-ios" */; - buildPhases = ( - 415B9BA66497D555D9D735FC58884B30 /* Headers */, - 621071A550AC52510305FD30232CA78F /* Sources */, - 1D5C89C35ED64587AA3F3CC9FB379C53 /* Frameworks */, - 6800757B2C01C036D46DC98BA04AFFE2 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 14095B8F88A69E2127AC168198A47484 /* PBXTargetDependency */, - 824DAEEDCF96F9DE264359D187EE3D7C /* PBXTargetDependency */, - DA0287AB868FBE7422C398193D171599 /* PBXTargetDependency */, - BD0671634A3423E811F1923A94A58215 /* PBXTargetDependency */, - 05A590F9EC9D0B3EA47F7735F7CA587A /* PBXTargetDependency */, - ); - name = "Pods-ios"; - productName = Pods_ios; - productReference = DB84425C5097649DB7039BCDFCA4DB6C /* Pods-ios */; - productType = "com.apple.product-type.framework"; - }; - D9756ACF066CEFEBC5A1189E58A912E8 /* Pods-iosTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 04278286CEB96ED1B5A7A932E21D012F /* Build configuration list for PBXNativeTarget "Pods-iosTests" */; - buildPhases = ( - 4EA3A0C175DC3B08CF7DF0AC686D56CA /* Headers */, - 874513F819B24A84534CB3731FFFCB2E /* Sources */, - F9B2E1995C19B72DC80DBCAAE398BC33 /* Frameworks */, - BDEE1FCBE18460C6B864496DCB3ACF0D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 95A503489BD245D0353433835A3DD6A8 /* PBXTargetDependency */, - 8E26205BE12108119E077CD520CE9795 /* PBXTargetDependency */, - ); - name = "Pods-iosTests"; - productName = Pods_iosTests; - productReference = 924ED3C5CACD9B01ED8F6ACDCE0843EE /* Pods-iosTests */; - productType = "com.apple.product-type.framework"; - }; - EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */ = { - isa = PBXNativeTarget; - buildConfigurationList = 9C98220D3187BF01A20E296DC128BED4 /* Build configuration list for PBXNativeTarget "Alamofire" */; - buildPhases = ( - 14FF1799C5ADBC71E1DB963F2AF8853D /* Headers */, - 5FE9836A67EA3E51CA889A1AB95BC874 /* Sources */, - 39D530C2A3085A1033683EC9AE0BC313 /* Frameworks */, - 93ECA2D9F79614966DFA76280ABFEF67 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Alamofire; - productName = Alamofire; - productReference = 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BFDFE7DC352907FC980B868725387E98 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1300; - }; - buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; - compatibilityVersion = "Xcode 10.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - Base, - en, - ); - mainGroup = CF1408CF629C7361332E53B88F7BD30C; - productRefGroup = 8DBB1FD9C1111191D999111C75E2985E /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */, - 2508BC3B908322C9E85E61AE743C9842 /* Chatto */, - 0B967D7F8561D42493EE289EC8D450D1 /* lottie-ios */, - B4723B5744013DF08BE3A5FEAA286944 /* MaterialComponents */, - 7F4022CD4249985E761F745058CAB372 /* MaterialComponents-MaterialIcons_ic_check_circle */, - 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */, - A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */, - CB5AA7A4FA94F4765AE579D6F4491956 /* Pods-ios */, - D9756ACF066CEFEBC5A1189E58A912E8 /* Pods-iosTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 3429006454DB433D0F4FBF2F4E05E02F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3EED30752BC2816FBBD27B8B9CBF4E0C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6800757B2C01C036D46DC98BA04AFFE2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6ECA9F6DF30D309B065300A72DD99BC8 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B2CE1396D0B81AF08A5F6C886FF6E61C /* MaterialIcons_ic_check_circle.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 8CBB757F4B8EF000D844E9F6690D3F8E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2DC8E9D2FBC9CB113C32F1F2DD4DCA22 /* MaterialComponents-MaterialIcons_ic_check_circle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 93ECA2D9F79614966DFA76280ABFEF67 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AED6EDF087DBE63D55C51AEE20F3373F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BDEE1FCBE18460C6B864496DCB3ACF0D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D3974EDA7759ECBA83AABE9D7FCFA671 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 498506168756522E454E1B48EC2886A7 /* Create Symlinks to Header Folders */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Create Symlinks to Header Folders"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "cd \"$CONFIGURATION_BUILD_DIR/$WRAPPER_NAME\" || exit 1\nif [ ! -d Versions ]; then\n # Not a versioned framework, so no need to do anything\n exit 0\nfi\n\npublic_path=\"${PUBLIC_HEADERS_FOLDER_PATH#$CONTENTS_FOLDER_PATH/}\"\nif [ ! -f \"$public_path\" ]; then\n ln -fs \"${PUBLIC_HEADERS_FOLDER_PATH#$WRAPPER_NAME/}\" \"$public_path\"\nfi\n\nprivate_path=\"${PRIVATE_HEADERS_FOLDER_PATH#$CONTENTS_FOLDER_PATH/}\"\nif [ ! -f \"$private_path\" ]; then\n ln -fs \"${PRIVATE_HEADERS_FOLDER_PATH#$WRAPPER_NAME/}\" \"$private_path\"\nfi\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 04C07BCDE98F7ACB2E4A7BB253EB79D4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2A5DC5F3AAB66AD530D07A5A2ED44DBF /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3C51EC6664C4867B564D0B6604C7738E /* MDFColorCalculations.m in Sources */, - 0C7FEE5AB0CD94782BD549DCF9974205 /* MDFImageCalculations.m in Sources */, - D323E03646FFB2D4FA1C0633BE363EC0 /* MDFTextAccessibility.m in Sources */, - 92DA5E1124FFD63F1FD2CB5B3A106B3F /* MDFTextAccessibility-dummy.m in Sources */, - 47A9B68E4429C21FB25951BF68989FA8 /* NSArray+MDFUtils.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 37AE70FCA26952762C9821CF88074221 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 40FA888B44A7961B13F0E7B332102CFC /* AnimatedButton.swift in Sources */, - 7D6751B1F74E12C7B85C8A86EF398113 /* AnimatedControl.swift in Sources */, - AA578A39DA8FF95C6E3B387DE3C8654A /* AnimatedSwitch.swift in Sources */, - 0711A9391B98D049C553FEF13D38C1AD /* Animation.swift in Sources */, - 185E3D77C15AC7CE8A4870A909237FCA /* AnimationCacheProvider.swift in Sources */, - DC21E754F631A9780BEF04CB9EC24C23 /* AnimationContainer.swift in Sources */, - 004D58D25F2EB03B40ADB43039641BC0 /* AnimationContext.swift in Sources */, - 654D4B8FB3BA49FB52CF733DF0F28CDF /* AnimationFontProvider.swift in Sources */, - EC2275BC165A2F54AC3498E0FDB085C9 /* AnimationImageProvider.swift in Sources */, - 335CCDCEBD9EB7CB33697888F3E0D6F8 /* AnimationKeypath.swift in Sources */, - 37E3D167D051B6E64ACCC71D8111CBE1 /* AnimationKeypathExtension.swift in Sources */, - BF61BD1B780F84EB855CDC8D07C70CF6 /* AnimationPublic.swift in Sources */, - D9D325BD0837A5B3F2C4368B4AF42D8D /* AnimationSubview.swift in Sources */, - 25C84FA79FF9DCAA88669C3380168F44 /* AnimationTextProvider.swift in Sources */, - 12AC4439EB9B96B847691B34BB75656D /* AnimationTime.swift in Sources */, - 2ADBB4BE80F61C7FF2AC39558549DDC1 /* AnimationView.swift in Sources */, - 0505586ED69AFD180618E639E5526C63 /* AnimationViewInitializers.swift in Sources */, - A6735D2299B4CAEA6BE631073D8BA734 /* AnimatorNode.swift in Sources */, - F659E0EBE31959B1758B3628AB4720B4 /* AnimatorNodeDebugging.swift in Sources */, - F8460445640FB3C461E58C1DFA2E0F16 /* AnyNodeProperty.swift in Sources */, - 35A265AD5CA62B93ED1BE1780D22B92B /* AnyValueContainer.swift in Sources */, - 9476C7CB756F02F62FF85F3AC6A74FDE /* AnyValueProvider.swift in Sources */, - 626F1E2369BBC5E226FCB50B0B3D74A2 /* Asset.swift in Sources */, - 8C59CE48E9D049159ECD769482024C7A /* AssetLibrary.swift in Sources */, - 1A7D0E6F5105EC32427DBB1983EC27BE /* BezierPath.swift in Sources */, - 4D06CA57EA8BFCEC35A0E97BEA2DF034 /* Bundle.swift in Sources */, - 856565C8AF270085E80E72C5D81FA186 /* BundleImageProvider.swift in Sources */, - AABFEA684B7B708D497FCACE7A03FFF7 /* CGFloatExtensions.swift in Sources */, - C32D94C3DBA969C96E803632C0F15D5E /* Color.swift in Sources */, - 5E479B4E6701DA0CEA6D0A57C50DD9DC /* ColorExtension.swift in Sources */, - 7C49833872601FCD2511E3717A726467 /* ColorValueProvider.swift in Sources */, - D1662BB63BC57C2FEAA5E16F73DFB9C9 /* CompatibleAnimationKeypath.swift in Sources */, - 92D5715E90A1540B39AFE10F3D2A5443 /* CompatibleAnimationView.swift in Sources */, - DB65627B75E519F4B1B65221BA00176A /* CompositionLayer.swift in Sources */, - B7964B8982C863F7AA9A9A0051387976 /* CompositionLayersInitializer.swift in Sources */, - EB1C650699E9DA20023D24CCB95944D4 /* CompoundBezierPath.swift in Sources */, - D681FB9211B3EB99507A7659A926AD3B /* CurveVertex.swift in Sources */, - A9778B7C3AC696E47DF7D19ABEBA80C9 /* DashPattern.swift in Sources */, - C16B4056D4AF47CF2BDF2B30E9ED27B1 /* Ellipse.swift in Sources */, - A76309F8030847107CE541B3B173FC24 /* EllipseNode.swift in Sources */, - C58D6FE775EF73EB56A0DB0F81E9F505 /* FilepathImageProvider.swift in Sources */, - B5C83F05C6D42CD1F1AA5322952A475F /* FillI.swift in Sources */, - 6BC837979F6FD58ABE2029174745CF8F /* FillNode.swift in Sources */, - 06894D1A68123F284D70772314BBC171 /* FillRenderer.swift in Sources */, - A9B10FC18D020818728E285314FDEDAD /* FloatValueProvider.swift in Sources */, - A32194DA20A525E2A0DD2C1DC780FF99 /* Font.swift in Sources */, - 3ED06C6A277226FD2D9410BAC8E6649D /* Glyph.swift in Sources */, - B5EB74E6B21E71690D59818B99FD9F4B /* GradientFill.swift in Sources */, - 6CEDCE10C0318529B41437117724D2A3 /* GradientFillNode.swift in Sources */, - FF82E104EC48B26651A60B944BFFA297 /* GradientFillRenderer.swift in Sources */, - 461BEAD29EE3D6E33BCDD8951CA80FD3 /* GradientStroke.swift in Sources */, - 8917A3295F44CCF46EEB66EC69B6DC31 /* GradientStrokeNode.swift in Sources */, - 5E5703B9B2DCBD225054E143EDE69080 /* GradientStrokeRenderer.swift in Sources */, - D28B26A308824EEAE6D48615A1E0D14C /* GradientValueProvider.swift in Sources */, - 388CB67650DEC60578CD193C099F93C7 /* Group.swift in Sources */, - 388951286A46DDEBC11081395CE19424 /* GroupInterpolator.swift in Sources */, - 489308FC142DC204B40A895C3A6F3A1B /* GroupNode.swift in Sources */, - 84C8B10EB6AFC1DB7574650206F73019 /* GroupOutputNode.swift in Sources */, - B50CAE951FE6B2B57ADDE94632B53B82 /* ImageAsset.swift in Sources */, - BECE55736F8FE0964E095D93B134E4E0 /* ImageCompositionLayer.swift in Sources */, - 509492048834DCECFB5FAC619B80CDBA /* ImageLayerModel.swift in Sources */, - 49E32757F5D405E67A46B71E862DE633 /* Interpolatable.swift in Sources */, - A9613D5848A69B5554E49963050E189E /* InterpolatableExtensions.swift in Sources */, - EB0960E12A2CB2EDF98C11A39F506A15 /* InvertedMatteLayer.swift in Sources */, - 42DD01B84A5FF2063B4C6ECA671FCE3E /* ItemsExtension.swift in Sources */, - 0E97277DFF7EA15585CCD7FD4F881799 /* KeyedDecodingContainerExtensions.swift in Sources */, - AFE7E0A95861830B7BE0CFCF493F662E /* Keyframe.swift in Sources */, - 1C994289D94896DEAE034CD8A0B4CDF0 /* KeyframeExtensions.swift in Sources */, - B061007C0C097FF93364AC9411CAFCDD /* KeyframeGroup.swift in Sources */, - 728FBE5D5AFDA125AB4D0FF6F66DDDA0 /* KeyframeInterpolator.swift in Sources */, - 3730BD7F19450F90CC632D633517DCD8 /* KeypathSearchable.swift in Sources */, - F1665937EE88175C13BD066950658EDB /* LayerDebugging.swift in Sources */, - DEBE3EC1FC8DCA47B2FFC71153B0636B /* LayerFontProvider.swift in Sources */, - 45E483574827876CEA2E3D2DE49257BF /* LayerImageProvider.swift in Sources */, - 874F69B4F2476462C2E8952A231DEF39 /* LayerModel.swift in Sources */, - CE150C3D6D3F9D4DA4F25E6B2831A385 /* LayerTextProvider.swift in Sources */, - F79954C7D48B356B32ACA8BDDFDD183E /* LayerTransformNode.swift in Sources */, - FF11F9BF6D01BF9AFD49B1831C74E77F /* lottie-ios-dummy.m in Sources */, - 2E8A20C40A0F48D1F8C0806CE740853D /* LottieView.swift in Sources */, - 3C044F4DA0AAF294B5B40D8D61F76067 /* LRUAnimationCache.swift in Sources */, - 442D9429E34A98231CFCDE3F82B30DE5 /* Marker.swift in Sources */, - DC6BF07D4BE73BA3084613035CD88F56 /* Mask.swift in Sources */, - C6464DD1F08A953F238EAD6328090708 /* MaskContainerLayer.swift in Sources */, - CA9F252FDC0DCF869BDE8A27E5B03BA0 /* MathKit.swift in Sources */, - 2CE76602B51D001D59FCC5CA12BEC37B /* Merge.swift in Sources */, - 092666672EA5B706BA5DAEAE9B49444F /* NodeProperty.swift in Sources */, - 7B90B8B1E94AA7BCB6F8195B232C0504 /* NodePropertyMap.swift in Sources */, - EB4388F68E8E658447BDAC97AE5805C2 /* NullCompositionLayer.swift in Sources */, - C97B9E8FA09F8CDA9458EC1387BBB6A2 /* PassThroughOutputNode.swift in Sources */, - E2BFA4A6A408809ECF183418A7368E1C /* PathElement.swift in Sources */, - 059F26929B3EB05770116463CC9DE9BD /* PathNode.swift in Sources */, - 59E047AE8E4BE5C88696952252BF2AF4 /* PathOutputNode.swift in Sources */, - 1899682FBADFB9FAE8EBCF416FD2C577 /* PointValueProvider.swift in Sources */, - CD82340C0739E932322EB77FBCFA6D29 /* PolygonNode.swift in Sources */, - CFD442A95C590C28AA710A2A228F9F4B /* PrecompAsset.swift in Sources */, - 22FFC6C3AAFCD260E0DAB32352B704A9 /* PreCompLayerModel.swift in Sources */, - CEDB69EFF8FEDC065680C80B49BDC5EE /* PreCompositionLayer.swift in Sources */, - C02D5ACD4E1A80E8AE4FFC958652C0AA /* Rectangle.swift in Sources */, - 51BE4D64CB57A36659FD73D354F810F0 /* RectNode.swift in Sources */, - 274C601104AC3151F7A65A4F15A82F3D /* RenderNode.swift in Sources */, - DB44F781C343FE7260506125A2B217D5 /* Repeater.swift in Sources */, - F457424CFB7849798D80D62A5EF98BB0 /* Shape.swift in Sources */, - B1F8084190567650796949754C0D25E7 /* ShapeCompositionLayer.swift in Sources */, - D2EB1D5874469F96E26EF47829867231 /* ShapeContainerLayer.swift in Sources */, - 5DEC3A3BBE20B44348CE23832360AB86 /* ShapeItem.swift in Sources */, - 4FAB52F6DBCC8A48A6FC5B865CF4984D /* ShapeLayerModel.swift in Sources */, - 49D5F0C6A4922BCF3D0B41DFDCE905CA /* ShapeNode.swift in Sources */, - 8AEE5FA821A6A42749CFDF8DD6CDF413 /* ShapeRenderLayer.swift in Sources */, - 3A8BFA61B606E1EA5527EF2D18D49D4D /* ShapeTransform.swift in Sources */, - 99D85B4B92B509E63CB8C3056CF601C5 /* SingleValueProvider.swift in Sources */, - 569D15C26BA16947069372B7D6269C51 /* SizeValueProvider.swift in Sources */, - 629777492DD9FCEC0E6BADDED7A06292 /* SolidCompositionLayer.swift in Sources */, - 6C945C98391E1347EE968F36DABA4B3D /* SolidLayerModel.swift in Sources */, - 4AF0F0425EE13873D1A3D72C87FD0CFE /* Star.swift in Sources */, - 40F06CD846C60BCA8E500808BFC5D8AB /* StarNode.swift in Sources */, - BD275ABF1DF797F0313866144001AE2D /* StringExtensions.swift in Sources */, - 9D3A2386D2F126134C1A61111BAF2B8C /* Stroke.swift in Sources */, - 6A6B387C7487CCA8B700C94CAE4297C3 /* StrokeNode.swift in Sources */, - 7004F49ED8B75EF71E86E1BB0BF7F31B /* StrokeRenderer.swift in Sources */, - 0716AAD8964E0F60518EF92178614471 /* TextAnimator.swift in Sources */, - 20F80E96D387808DB7427A1249E8EADF /* TextAnimatorNode.swift in Sources */, - 84DA7DB4EF4E0E8B628B429882B14FBB /* TextCompositionLayer.swift in Sources */, - A3D5AADA1B068C272F4C41E9771EE195 /* TextDocument.swift in Sources */, - 8143C58AA2F1E117E2A7BF20E9582EF3 /* TextLayer.swift in Sources */, - 2A94738462743EA303248E706CF448BB /* TextLayerModel.swift in Sources */, - C6A87EEB51FF906E9FF947949211D34E /* Transform.swift in Sources */, - D66484BFD46F95EE237049C7F9150C76 /* Trim.swift in Sources */, - 86DB9FBEAE3DE9E248D74A04EEE10875 /* TrimPathNode.swift in Sources */, - EC9E770804940B15285BD22E29A6FCBA /* UIColorExtension.swift in Sources */, - 4FC0BE22D1389E78DCDC8515F3C1F124 /* ValueContainer.swift in Sources */, - 0A4178C93B7F1DF931BF92BC4D971841 /* Vectors.swift in Sources */, - 1F3D77EAFD74D2CFE5C43D46FD93C6D8 /* VectorsExtensions.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 390B4779DF669199CC4F894284D2B29B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7154A1C49053E5493963962660A03513 /* CAMediaTimingFunction+MDCAnimationTiming.m in Sources */, - CA24AD420264A0A9E0481C2F2E062AB0 /* MaterialComponents-dummy.m in Sources */, - E990970757CDEBE53FA26AA40A17BD7E /* MaterialIcons+ic_check_circle.m in Sources */, - 593F9E60F9CA978FD7DEF9A6C0EB917D /* MaterialMathDummy.m in Sources */, - E43BAEAC9F260161234AFCA6EE7CA2B9 /* MaterialShadowElevationsDummy.m in Sources */, - 1378724980581463BB5A2AD8A3B39BC3 /* MDCButton.m in Sources */, - 9535D614A8A0FD3B56CA7E8BACA11451 /* MDCCard.m in Sources */, - 3B214DA1F9E0A2F6BAA4DF248BE271AE /* MDCCardCollectionCell.m in Sources */, - 69200AC3BAAF8F7878D72847AB8DDB8C /* MDCCornerTreatment.m in Sources */, - D9F13F6D976BDA43867D98A5F1CF6BF1 /* MDCCornerTreatment+CornerTypeInitalizer.m in Sources */, - 6A7604C990C5DFF551C625E6EBC1E93A /* MDCCurvedCornerTreatment.m in Sources */, - 39874777CA1F53BB8BF51F9C0DF90173 /* MDCCurvedRectShapeGenerator.m in Sources */, - A76F7B04181F8D8A0F931424F54C4C07 /* MDCCutCornerTreatment.m in Sources */, - 75A51A634B4414C4BC6CE7DA089CAEC8 /* MDCEdgeTreatment.m in Sources */, - 4F63D1C9A3C378378BEBA8171E0EB4B3 /* MDCFlatButton.m in Sources */, - ABFFB41EC17E1CC5B8A7B0266718DAE1 /* MDCFloatingButton.m in Sources */, - 0D9E44B0B2AC6D75ACB5D1D1B8EB6090 /* MDCFloatingButton+Animation.m in Sources */, - 74129ED9A3E24DFAFFC726872335B23E /* MDCFloatingButtonModeAnimator.m in Sources */, - 4CD67A5130FE795F570FA71E4A0F0817 /* MDCFontScaler.m in Sources */, - D0CC26A37DD0CD758EA556DD00D6503F /* MDCFontTraits.m in Sources */, - CBC6305777E98D5C59FC5ACAB00A17D1 /* MDCIcons.m in Sources */, - 34F8E92AE1B47864EB2828243BEA8378 /* MDCInkGestureRecognizer.m in Sources */, - 437EF01383D730AE242D947B9B58AA18 /* MDCInkLayer.m in Sources */, - 1BA653A4F6BE2F4443363A09C3E99628 /* MDCInkTouchController.m in Sources */, - 61FC00C7FCA912D5B60ACFCD7B93CB32 /* MDCInkView.m in Sources */, - 0F1B88139EBAE43C4D04C4D238AE8F51 /* MDCLegacyInkLayer.m in Sources */, - 2155754362430305C54CBCC65CC1A1A1 /* MDCPathGenerator.m in Sources */, - 3064376167F8EFF6F63AEC652D55E9A0 /* MDCPillShapeGenerator.m in Sources */, - 3F016AAA33E691084D294F7A12AC56AD /* MDCRaisedButton.m in Sources */, - 138A6746D98577BC9AEFBA7E0DBCE9A7 /* MDCRectangleShapeGenerator.m in Sources */, - B4B385550E3C5121D46697EB10E83C02 /* MDCRippleLayer.m in Sources */, - 8E9D0B3DD987E4503F62401A7F412FCA /* MDCRippleTouchController.m in Sources */, - 7873F0E585A1DD314E9A681B5E4C050C /* MDCRippleView.m in Sources */, - 9165FD4860C36068CF81600ED9992F02 /* MDCRoundedCornerTreatment.m in Sources */, - 1C3510C6893F019ABE94367A4EE9D07E /* MDCShadow.m in Sources */, - 24B503B9E52E65303577FE6910BF3D15 /* MDCShadowLayer.m in Sources */, - 482827C9A937BDC9B86EC162974D8F3E /* MDCShadowsCollection.m in Sources */, - DAF43358D7A8ED2F222994AA7F1EDED6 /* MDCShapedShadowLayer.m in Sources */, - E1149D3565B395487CF0EB4487448957 /* MDCShapedView.m in Sources */, - EED90CBEBBCB86F3FE4215070F7737E0 /* MDCShapeMediator.m in Sources */, - 960892840B75C67C133BAA7E39C35D55 /* MDCSlantedRectShapeGenerator.m in Sources */, - 3B8C4CEB676840C42DE451C65C4D79E9 /* MDCStatefulRippleView.m in Sources */, - 41C21FEBC88B3A3F97B18DBDF929C751 /* MDCTriangleEdgeTreatment.m in Sources */, - 17CEA056B7594689546EAB74E595D060 /* MDCTypography.m in Sources */, - 77F503B82A75DA59A61413C1D5371422 /* MDCTypographyUtilities.m in Sources */, - 6DADCA7F70AA8718696BB357CD2D6140 /* UIApplication+MDCAppExtensions.m in Sources */, - 48A55C7FBBE4B4F30AEA67E3A50006EE /* UICollectionViewController+MDCCardReordering.m in Sources */, - 11E325AD2BE51DA7C48EB54B7737B4BF /* UIColor+MaterialBlending.m in Sources */, - AB0E5CD170B9656EA06269823DD89CB1 /* UIColor+MaterialDynamic.m in Sources */, - 490142072F8EE916442517A5884282E0 /* UIColor+MaterialElevation.m in Sources */, - 0B6124DD33313BE9E4A71126F8F3DB75 /* UIFont+MaterialScalable.m in Sources */, - 822EE6C99BED7B9C6A92C87D7717EECC /* UIFont+MaterialSimpleEquality.m in Sources */, - E5F04CB5F20F4E13DCFBC88AB7506E55 /* UIFont+MaterialTypography.m in Sources */, - 12017489E0E497EC8A12E9418643AA6D /* UIFont+MaterialTypographyPrivate.m in Sources */, - D0C349CE9DB7214843BCDE9B8A4E4AF9 /* UIFontDescriptor+MaterialTypography.m in Sources */, - F363D238E6734C6503D7E8AACFB6F01A /* UIView+MaterialElevationResponding.m in Sources */, - 71EEB4CB0D6FF002E43D958FAE52AF63 /* UIView+MDCTimingFunction.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 5FE9836A67EA3E51CA889A1AB95BC874 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A664924D6CCE2922A3F81EC932F4D476 /* AFError.swift in Sources */, - FEDBAD32E2EDA85AD6E362B82892A74A /* Alamofire.swift in Sources */, - 1773084DECF68CADD45567FBEC56036D /* Alamofire-dummy.m in Sources */, - A3153333FC136836B0028E6AB2A56BEE /* AlamofireExtended.swift in Sources */, - F36D96A4346C90A2D11CB3B6A2ECF4CF /* AuthenticationInterceptor.swift in Sources */, - 13E62623092B680C6A5C349D48B8A4FD /* CachedResponseHandler.swift in Sources */, - 97584BC08D2B494417BDEE268CFF38C9 /* Combine.swift in Sources */, - DCD0C33A2B50811D53CF68F021284B47 /* DispatchQueue+Alamofire.swift in Sources */, - 9CFDA7C92E0EEA31F709663B0E727ABA /* EventMonitor.swift in Sources */, - 52BE6F747C26DF2A24532458E55DC10F /* HTTPHeaders.swift in Sources */, - 02621C4B82398D0657F474E21493A3A2 /* HTTPMethod.swift in Sources */, - C16A047C4E8D856309A486182A490993 /* MultipartFormData.swift in Sources */, - 30A331CD9286145E92DB11D671664C63 /* MultipartUpload.swift in Sources */, - B6473B8E8353317F75D6800D4F7054CB /* NetworkReachabilityManager.swift in Sources */, - 471611F482CDC15BF464E3BA9CB83968 /* Notifications.swift in Sources */, - 2550F0D474DE846FEC5C76CBE85F927E /* OperationQueue+Alamofire.swift in Sources */, - 8B9CDBE3FFD712120CD66DD8B06C44E4 /* ParameterEncoder.swift in Sources */, - 4634BA717BFCE522E5B42304C6A78B5D /* ParameterEncoding.swift in Sources */, - 02DB462B121245593CE653B9B377F970 /* Protected.swift in Sources */, - DD58A00EACBEE274C381B491519C6B8C /* RedirectHandler.swift in Sources */, - E0C65E16219718869CD2AFCA2C5465CB /* Request.swift in Sources */, - F63BE0585331CAA3482EF736803F8243 /* RequestInterceptor.swift in Sources */, - 1D17B83410DC98911D539F2BD5254C05 /* RequestTaskMap.swift in Sources */, - C7F66519CE6148F21D7DB11423F1D34D /* Response.swift in Sources */, - 941822CDF68EB8F4D49F150457A82616 /* ResponseSerialization.swift in Sources */, - A4F1202CE5BBE79F3BBCAE3D2B16BC03 /* Result+Alamofire.swift in Sources */, - 512FAFBD71830F126224C033B6C45F4E /* RetryPolicy.swift in Sources */, - 8F9E1EEF2FE52E3231A769722D5C4148 /* ServerTrustEvaluation.swift in Sources */, - EC11B17DA78F7EEBEBC3EFAF68C6DF9F /* Session.swift in Sources */, - 688337B18659C4BF722F87AFC4FEEF81 /* SessionDelegate.swift in Sources */, - E1769C267E82B0C24FE0FFBF949F0A6E /* StringEncoding+Alamofire.swift in Sources */, - 5E594FA3290D3D70F500572D0AC100DB /* URLConvertible+URLRequestConvertible.swift in Sources */, - F5D2A31C7EB1DE010771140B6E7ABAD8 /* URLEncodedFormEncoder.swift in Sources */, - B89D1C69742F61878115334A1D2DFFE7 /* URLRequest+Alamofire.swift in Sources */, - E857ADCAD7B647883D5B2AEC3F16D1D5 /* URLSessionConfiguration+Alamofire.swift in Sources */, - D15FEA31AA9625BBF041FB91E48A9995 /* Validation.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 621071A550AC52510305FD30232CA78F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0E5AF92BC5AD820D750CDDE0DDC887D2 /* Pods-ios-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 66C6B183F08D1564AE5191E32440E60D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 78A557FFD62CE14E4D6B85C238D6D367 /* BaseChatItemPresenter.swift in Sources */, - DA24453A3C4C0EFDC25A253712BCEFB6 /* BaseChatViewController.swift in Sources */, - 31B827F3FA1AB67F3139AA92AA3C9C88 /* BaseChatViewController+CellPanGestureHandler.swift in Sources */, - 415CF97552CF15ABEB0FBF16BB7CBC0C /* BaseChatViewController+Changes.swift in Sources */, - F0501EA15D82306F07F094CA2558130F /* BaseChatViewController+Presenters.swift in Sources */, - A94F1967A266EA1EB3E4910073BE41F4 /* BaseChatViewController+Scrolling.swift in Sources */, - 98E4EFD77D2015E422923ED1EAD4AF7F /* BaseChatViewControllerView.swift in Sources */, - 8AC8E7FC1C4A48B36484927306556F31 /* CellPanGestureHandler.swift in Sources */, - 267ED94E779B638FF06A3796A5DBA7E5 /* ChatCollectionViewLayout.swift in Sources */, - 847D96F05D6EF0D2AC3E54036A72843F /* ChatDataSourceProtocol.swift in Sources */, - C4DA75B4F7DD07F64D5B2FB7B83421D5 /* ChatItemCompanion.swift in Sources */, - C5248490BD1AB45374D2AFA6BC3C5C32 /* ChatItemCompanionCollection.swift in Sources */, - 252655EF3425EC8EF3154477B9B4C3F1 /* ChatItemPresenterFactory.swift in Sources */, - 3C1C2FE0D3AD4E5B2EEDF80C83AD822F /* ChatItemProtocolDefinitions.swift in Sources */, - 7F1DEF5C995E93F3EEDEE6562BD3FBF4 /* ChatLayoutConfiguration.swift in Sources */, - 7D3E0B70A1031AF6F52BB2F2704C5D7A /* Chatto-dummy.m in Sources */, - D6245FA6A979B1028595B60789B07701 /* CollectionChanges.swift in Sources */, - 8C802F4EE8500BDBC0D000E200B162F2 /* DummyChatItemPresenter.swift in Sources */, - 74C2C803859AE1F9EDBB8BC6D027ACE9 /* InputPositionControlling.swift in Sources */, - ED10CCA16008FE6450460EADE24C758B /* KeyboardTracker.swift in Sources */, - 198CE4B8F3B6704CB4AD916821DC22A1 /* ReplyFeedbackGenerator.swift in Sources */, - 49A404FC51F1F7CDCDF7FD0112F00A5F /* SerialTaskQueue.swift in Sources */, - EDC23A30E84A45747FC576F67901CCE3 /* Utils.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 75C42EE808FFAC35041568CFE92847AD /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 25ABCCB506CD51393434BE44F446AD5F /* MDFInternationalization-dummy.m in Sources */, - BF7F9976FA80C821BC13E58E30E94B5B /* MDFRTL.m in Sources */, - 8C050C985EF60CCC3B9E25050BE20F3E /* NSLocale+MaterialRTL.m in Sources */, - 124EEAE22B8077371D55FDB1AC4F4D30 /* NSString+MaterialBidi.m in Sources */, - 03219B99BEEC9AB447188784DFDF4E19 /* UIImage+MaterialRTL.m in Sources */, - 984167BC563C943B94B43A01DECBEAB0 /* UIView+MaterialRTL.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 874513F819B24A84534CB3731FFFCB2E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 496713FA3DBA4082C7948ECF7349A992 /* Pods-iosTests-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 05A590F9EC9D0B3EA47F7735F7CA587A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MaterialComponents; - target = B4723B5744013DF08BE3A5FEAA286944 /* MaterialComponents */; - targetProxy = 86D033B1EAA6DB4A9250A038CF62AF07 /* PBXContainerItemProxy */; - }; - 12A0FCF3F802AE49C0D0CB64AA7E43A7 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MDFTextAccessibility; - target = A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */; - targetProxy = D397BCB4986AFF763539DD0013C5417D /* PBXContainerItemProxy */; - }; - 14095B8F88A69E2127AC168198A47484 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Alamofire; - target = EAAA1AD3A8A1B59AB91319EE40752C6D /* Alamofire */; - targetProxy = 6ED33D3BA173DAE7C34C14C6974F4E02 /* PBXContainerItemProxy */; - }; - 6C1B5C5B5ECFC4AE40FE227FD0468177 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "MaterialComponents-MaterialIcons_ic_check_circle"; - target = 7F4022CD4249985E761F745058CAB372 /* MaterialComponents-MaterialIcons_ic_check_circle */; - targetProxy = 09ADAB9C1EBB34AFF27286BC540ECBF9 /* PBXContainerItemProxy */; - }; - 824DAEEDCF96F9DE264359D187EE3D7C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Chatto; - target = 2508BC3B908322C9E85E61AE743C9842 /* Chatto */; - targetProxy = 37B8EC58BCB778F91D6C37656EDA3111 /* PBXContainerItemProxy */; - }; - 8907A64A1B702FF7E8F6F71C8D6AE9A2 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MDFInternationalization; - target = 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */; - targetProxy = 7678DE8865A337D774511B701E4FB23B /* PBXContainerItemProxy */; - }; - 8E26205BE12108119E077CD520CE9795 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "lottie-ios"; - target = 0B967D7F8561D42493EE289EC8D450D1 /* lottie-ios */; - targetProxy = 547A50F181286054E7D1AD178F2C9D36 /* PBXContainerItemProxy */; - }; - 95A503489BD245D0353433835A3DD6A8 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "Pods-ios"; - target = CB5AA7A4FA94F4765AE579D6F4491956 /* Pods-ios */; - targetProxy = 1E07159C86DCD357DDD6438B3A657BB7 /* PBXContainerItemProxy */; - }; - BD0671634A3423E811F1923A94A58215 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MDFTextAccessibility; - target = A8E4C927B2E899954BC2FD3314BCA9DA /* MDFTextAccessibility */; - targetProxy = CCFB263D6803CCB26550A6888F18657A /* PBXContainerItemProxy */; - }; - DA0287AB868FBE7422C398193D171599 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MDFInternationalization; - target = 55A01F8E987A6E2F7F0ED141A1ECA406 /* MDFInternationalization */; - targetProxy = 8AAE22C70F1836AC9C87C67A00D249D2 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 0134A33392E024A08A8B77B12A149AAC /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */; - buildSettings = { - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MaterialComponents"; - IBSC_MODULE = MaterialComponents; - INFOPLIST_FILE = "Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - PRODUCT_NAME = MaterialIcons_ic_check_circle; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - 082649ABF65D30C0B8732BDDADE9DC4C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 43E52D204C785271C19FE614C61534A4 /* MDFInternationalization.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization.modulemap"; - PRODUCT_MODULE_NAME = MDFInternationalization; - PRODUCT_NAME = MDFInternationalization; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 0D7C086888D375AA26A6F1AC139D726E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 6439854B8FE18D971BADA3FD4D2F00F8 /* MaterialComponents.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MaterialComponents/MaterialComponents-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MaterialComponents/MaterialComponents-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MaterialComponents/MaterialComponents.modulemap"; - PRODUCT_MODULE_NAME = MaterialComponents; - PRODUCT_NAME = MaterialComponents; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 2A7DA26AF0A7C5162BE1469DC8E89D03 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3CDCB1C8D529F8DE664D2E4C96BD3A7D /* Alamofire.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; - PRODUCT_MODULE_NAME = Alamofire; - PRODUCT_NAME = Alamofire; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.3; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 330BE68AFFEED76FA4D133838B94A1C1 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 1CBA47FDA3E910C7EDFE324EFA897551 /* lottie-ios.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/lottie-ios/lottie-ios-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/lottie-ios/lottie-ios-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/lottie-ios/lottie-ios.modulemap"; - PRODUCT_MODULE_NAME = Lottie; - PRODUCT_NAME = Lottie; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.4; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 4EA1C47EA1053331DBCA33FF4B9D3A0D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = BE5AE749191583AB288E955A216FBDEE /* MDFInternationalization.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MDFInternationalization/MDFInternationalization.modulemap"; - PRODUCT_MODULE_NAME = MDFInternationalization; - PRODUCT_NAME = MDFInternationalization; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 5CA0DF9E981B8FEA7F0E9A5D37503B13 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CEE878F352FF70522E2B4041CB792B24 /* MDFTextAccessibility.debug.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-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/MDFTextAccessibility/MDFTextAccessibility.modulemap"; - PRODUCT_MODULE_NAME = MDFTextAccessibility; - PRODUCT_NAME = MDFTextAccessibility; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 6847C40F99A8980CD7758FB325464959 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C5494CF8D45C3C7880D1C7684088125B /* Chatto.release.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/Chatto/Chatto-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Chatto/Chatto-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/Chatto/Chatto.modulemap"; - PRODUCT_MODULE_NAME = Chatto; - PRODUCT_NAME = Chatto; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.3; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 79CAC264ADFE5B001A5B7C3B7E3C344F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C43092A50861A2F799989439A7DCBCAF /* Alamofire.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; - PRODUCT_MODULE_NAME = Alamofire; - PRODUCT_NAME = Alamofire; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.3; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 8DE5143C03248BB6CD542DE3963D6F3A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - 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_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - 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_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Debug; - }; - 9476DECF90EB61954A99CD8B31CE09BE /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0C07D8193799950468B6CF3885E1193B /* Pods-ios.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-ios/Pods-ios-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-ios/Pods-ios.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 96964714EF72EDBB552C46AEF5FE7534 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = B1F119B189824C232697C892AF030E98 /* Pods-ios.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-ios/Pods-ios-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-ios/Pods-ios.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 975BE5E092809620E2F8FB75D1F36C85 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 615BC7DD9F6466F845804084D0B12D66 /* lottie-ios.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/lottie-ios/lottie-ios-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/lottie-ios/lottie-ios-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/lottie-ios/lottie-ios.modulemap"; - PRODUCT_MODULE_NAME = Lottie; - PRODUCT_NAME = Lottie; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.4; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 9E406C6AAF85E580207CD97B0044DEAB /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - 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_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - 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_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Release; - }; - A2E59AE2DABA2794E4EC62B9981FF842 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0EA7FAE72F27B4A66C2CAEEC25C31634 /* MDFTextAccessibility.release.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MDFTextAccessibility/MDFTextAccessibility-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/MDFTextAccessibility/MDFTextAccessibility.modulemap"; - PRODUCT_MODULE_NAME = MDFTextAccessibility; - PRODUCT_NAME = MDFTextAccessibility; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - B53E854E91BA2A628572DC2785525912 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MaterialComponents/MaterialComponents-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MaterialComponents/MaterialComponents-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MaterialComponents/MaterialComponents.modulemap"; - PRODUCT_MODULE_NAME = MaterialComponents; - PRODUCT_NAME = MaterialComponents; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - B6F84F36E13ABE8EBBD6B23191196B4F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 8186408B3030AB74BE1331A431FB83D4 /* Chatto.debug.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/Chatto/Chatto-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Chatto/Chatto-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/Chatto/Chatto.modulemap"; - PRODUCT_MODULE_NAME = Chatto; - PRODUCT_NAME = Chatto; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.3; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - CBE8964E5C394F9774D2231A5C783AB9 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 72E3FDB351A03387E5608B6C84700D31 /* Pods-iosTests.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - EAFEC7653599177751A2AF5990C7A92B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3EA7965C6F4ADAB8C1D9392030D26C15 /* MaterialComponents.debug.xcconfig */; - buildSettings = { - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MaterialComponents"; - IBSC_MODULE = MaterialComponents; - INFOPLIST_FILE = "Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - PRODUCT_NAME = MaterialIcons_ic_check_circle; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - EBDD9D76E91A50F71A2A604AD4F544B4 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F9C9C35B39EFD7AD82253D0A2AD91B56 /* Pods-iosTests.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-iosTests/Pods-iosTests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0358E8E05175A7E59A2AEECD12A1801E /* Build configuration list for PBXNativeTarget "lottie-ios" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 975BE5E092809620E2F8FB75D1F36C85 /* Debug */, - 330BE68AFFEED76FA4D133838B94A1C1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 04278286CEB96ED1B5A7A932E21D012F /* Build configuration list for PBXNativeTarget "Pods-iosTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CBE8964E5C394F9774D2231A5C783AB9 /* Debug */, - EBDD9D76E91A50F71A2A604AD4F544B4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 29235CC975477CF0AE111D8533A857C9 /* Build configuration list for PBXNativeTarget "MDFInternationalization" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4EA1C47EA1053331DBCA33FF4B9D3A0D /* Debug */, - 082649ABF65D30C0B8732BDDADE9DC4C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 8DE5143C03248BB6CD542DE3963D6F3A /* Debug */, - 9E406C6AAF85E580207CD97B0044DEAB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4D07C4D46B8088CB6927DB0DC7D38E87 /* Build configuration list for PBXNativeTarget "MaterialComponents" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B53E854E91BA2A628572DC2785525912 /* Debug */, - 0D7C086888D375AA26A6F1AC139D726E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 81EE041A2E698878FED9512843DEEE02 /* Build configuration list for PBXNativeTarget "MaterialComponents-MaterialIcons_ic_check_circle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - EAFEC7653599177751A2AF5990C7A92B /* Debug */, - 0134A33392E024A08A8B77B12A149AAC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 8FE49F8617D5D677A3808ABE3B492E82 /* Build configuration list for PBXNativeTarget "Chatto" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B6F84F36E13ABE8EBBD6B23191196B4F /* Debug */, - 6847C40F99A8980CD7758FB325464959 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 994FA3DA95C9295DD26DDD2C48D33FDF /* Build configuration list for PBXNativeTarget "Pods-ios" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 9476DECF90EB61954A99CD8B31CE09BE /* Debug */, - 96964714EF72EDBB552C46AEF5FE7534 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 9C98220D3187BF01A20E296DC128BED4 /* Build configuration list for PBXNativeTarget "Alamofire" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 79CAC264ADFE5B001A5B7C3B7E3C344F /* Debug */, - 2A7DA26AF0A7C5162BE1469DC8E89D03 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - E9D2A6F4024608ED9B15BD3B46315019 /* Build configuration list for PBXNativeTarget "MDFTextAccessibility" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5CA0DF9E981B8FEA7F0E9A5D37503B13 /* Debug */, - A2E59AE2DABA2794E4EC62B9981FF842 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; -} diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme deleted file mode 100644 index 3658e118..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Alamofire.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme deleted file mode 100644 index adbd96b8..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Chatto.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme deleted file mode 100644 index 7c7a9df3..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFInternationalization.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme deleted file mode 100644 index d8d7eb75..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MDFTextAccessibility.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme deleted file mode 100644 index 97a4eede..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents-MaterialIcons_ic_check_circle.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme deleted file mode 100644 index fc44e3c9..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/MaterialComponents.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme deleted file mode 100644 index db1b9261..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-ios.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme deleted file mode 100644 index 7d3b5359..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/Pods-iosTests.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme deleted file mode 100644 index 67c96f89..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/lottie-ios.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist b/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 8b2e7909..00000000 --- a/test/fixtures/cocoapods/Pods/Pods.xcodeproj/xcuserdata/louis.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - SchemeUserState - - Alamofire.xcscheme - - isShown - - - Chatto.xcscheme - - isShown - - - MDFInternationalization.xcscheme - - isShown - - - MDFTextAccessibility.xcscheme - - isShown - - - MaterialComponents-MaterialIcons_ic_check_circle.xcscheme - - isShown - - - MaterialComponents.xcscheme - - isShown - - - Pods-ios.xcscheme - - isShown - - - Pods-iosTests.xcscheme - - isShown - - - lottie-ios.xcscheme - - isShown - - - - SuppressBuildableAutocreation - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist deleted file mode 100644 index f6f37a1a..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 5.4.3 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m deleted file mode 100644 index a6c45942..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Alamofire : NSObject -@end -@implementation PodsDummy_Alamofire -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch deleted file mode 100644 index beb2a244..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h deleted file mode 100644 index 00014e3c..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double AlamofireVersionNumber; -FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig deleted file mode 100644 index 7d169c44..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap deleted file mode 100644 index d1f125fa..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Alamofire { - umbrella header "Alamofire-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig deleted file mode 100644 index 7d169c44..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist deleted file mode 100644 index c26f36f0..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 4.1.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m deleted file mode 100644 index 94fb320b..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Chatto : NSObject -@end -@implementation PodsDummy_Chatto -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch deleted file mode 100644 index beb2a244..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h deleted file mode 100644 index ef0320a4..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double ChattoVersionNumber; -FOUNDATION_EXPORT const unsigned char ChattoVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig deleted file mode 100644 index 2464a471..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.debug.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Chatto -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/Chatto -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap deleted file mode 100644 index cbc152f5..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Chatto { - umbrella header "Chatto-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig deleted file mode 100644 index 2464a471..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Chatto/Chatto.release.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Chatto -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/Chatto -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist deleted file mode 100644 index 45226757..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 3.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m deleted file mode 100644 index 15ac2499..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MDFInternationalization : NSObject -@end -@implementation PodsDummy_MDFInternationalization -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch deleted file mode 100644 index beb2a244..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h deleted file mode 100644 index 09365d06..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization-umbrella.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - -#import "MDFInternationalization.h" -#import "MDFRTL.h" -#import "NSLocale+MaterialRTL.h" -#import "NSString+MaterialBidi.h" -#import "UIImage+MaterialRTL.h" -#import "UIView+MaterialRTL.h" - -FOUNDATION_EXPORT double MDFInternationalizationVersionNumber; -FOUNDATION_EXPORT const unsigned char MDFInternationalizationVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig deleted file mode 100644 index 4e047ef0..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.debug.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFInternationalization -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap deleted file mode 100644 index 0c635178..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module MDFInternationalization { - umbrella header "MDFInternationalization-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig deleted file mode 100644 index 4e047ef0..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFInternationalization/MDFInternationalization.release.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFInternationalization -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist deleted file mode 100644 index bdac57c6..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 2.0.1 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m deleted file mode 100644 index 1c4041ac..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MDFTextAccessibility : NSObject -@end -@implementation PodsDummy_MDFTextAccessibility -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch deleted file mode 100644 index beb2a244..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h deleted file mode 100644 index dc28d78e..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility-umbrella.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - -#import "MDFTextAccessibility.h" - -FOUNDATION_EXPORT double MDFTextAccessibilityVersionNumber; -FOUNDATION_EXPORT const unsigned char MDFTextAccessibilityVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig deleted file mode 100644 index 9b8318df..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.debug.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFTextAccessibility -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap deleted file mode 100644 index db04c5fb..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module MDFTextAccessibility { - umbrella header "MDFTextAccessibility-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig deleted file mode 100644 index 9b8318df..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MDFTextAccessibility/MDFTextAccessibility.release.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/MDFTextAccessibility -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist deleted file mode 100644 index 08011709..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 124.2.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m deleted file mode 100644 index 6ba91fdc..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MaterialComponents : NSObject -@end -@implementation PodsDummy_MaterialComponents -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch deleted file mode 100644 index beb2a244..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h deleted file mode 100644 index 16b8cd3a..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents-umbrella.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - -#import "CAMediaTimingFunction+MDCAnimationTiming.h" -#import "MaterialAnimationTiming.h" -#import "UIView+MDCTimingFunction.h" -#import "MaterialAvailability.h" -#import "MDCAvailability.h" -#import "MaterialButtons.h" -#import "MDCButton.h" -#import "MDCFlatButton.h" -#import "MDCFloatingButton+Animation.h" -#import "MDCFloatingButton.h" -#import "MDCRaisedButton.h" -#import "MaterialCards.h" -#import "MDCCard.h" -#import "MDCCardCollectionCell.h" -#import "UICollectionViewController+MDCCardReordering.h" -#import "MaterialElevation.h" -#import "MDCElevatable.h" -#import "MDCElevationOverriding.h" -#import "UIColor+MaterialElevation.h" -#import "UIView+MaterialElevationResponding.h" -#import "MaterialInk.h" -#import "MDCInkGestureRecognizer.h" -#import "MDCInkTouchController.h" -#import "MDCInkTouchControllerDelegate.h" -#import "MDCInkView.h" -#import "MDCInkViewDelegate.h" -#import "MaterialRipple.h" -#import "MDCRippleTouchController.h" -#import "MDCRippleTouchControllerDelegate.h" -#import "MDCRippleView.h" -#import "MDCRippleViewDelegate.h" -#import "MDCStatefulRippleView.h" -#import "MaterialShadow.h" -#import "MDCShadow.h" -#import "MDCShadowsCollection.h" -#import "MaterialShadowElevations.h" -#import "MDCShadowElevations.h" -#import "MaterialShadowLayer.h" -#import "MDCShadowLayer.h" -#import "MaterialShapeLibrary.h" -#import "MDCCornerTreatment+CornerTypeInitalizer.h" -#import "MDCCurvedCornerTreatment.h" -#import "MDCCurvedRectShapeGenerator.h" -#import "MDCCutCornerTreatment.h" -#import "MDCPillShapeGenerator.h" -#import "MDCRoundedCornerTreatment.h" -#import "MDCSlantedRectShapeGenerator.h" -#import "MDCTriangleEdgeTreatment.h" -#import "MaterialShapes.h" -#import "MDCCornerTreatment.h" -#import "MDCEdgeTreatment.h" -#import "MDCPathGenerator.h" -#import "MDCRectangleShapeGenerator.h" -#import "MDCShapedShadowLayer.h" -#import "MDCShapedView.h" -#import "MDCShapeGenerating.h" -#import "MDCShapeMediator.h" -#import "MaterialTypography.h" -#import "MDCFontScaler.h" -#import "MDCFontTextStyle.h" -#import "MDCTypography.h" -#import "UIFont+MaterialScalable.h" -#import "UIFont+MaterialSimpleEquality.h" -#import "UIFont+MaterialTypography.h" -#import "UIFontDescriptor+MaterialTypography.h" -#import "MaterialApplication.h" -#import "UIApplication+MDCAppExtensions.h" -#import "MaterialColor.h" -#import "UIColor+MaterialBlending.h" -#import "UIColor+MaterialDynamic.h" -#import "MaterialIcons.h" -#import "MDCIcons+BundleLoader.h" -#import "MDCIcons.h" -#import "MaterialIcons+ic_check_circle.h" -#import "MaterialMath.h" -#import "MDCMath.h" - -FOUNDATION_EXPORT double MaterialComponentsVersionNumber; -FOUNDATION_EXPORT const unsigned char MaterialComponentsVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig deleted file mode 100644 index 48c72dea..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.debug.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -OTHER_LDFLAGS = $(inherited) -framework "MDFInternationalization" -framework "MDFTextAccessibility" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/MaterialComponents -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap deleted file mode 100644 index 052d8ae7..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module MaterialComponents { - umbrella header "MaterialComponents-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig deleted file mode 100644 index 48c72dea..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/MaterialComponents.release.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -OTHER_LDFLAGS = $(inherited) -framework "MDFInternationalization" -framework "MDFTextAccessibility" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/MaterialComponents -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist deleted file mode 100644 index e4e4dafb..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/MaterialComponents/ResourceBundle-MaterialIcons_ic_check_circle-MaterialComponents-Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - BNDL - CFBundleShortVersionString - 124.2.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist deleted file mode 100644 index 2243fe6e..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown deleted file mode 100644 index 7537c191..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.markdown +++ /dev/null @@ -1,670 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## Alamofire - -Copyright (c) 2014-2021 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. - - -## Chatto - -The MIT License (MIT) - -Copyright (c) 2015 Badoo Development - -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. - - - -## MDFInternationalization - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -## MDFTextAccessibility - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -## MaterialComponents - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -Generated by CocoaPods - https://cocoapods.org diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist deleted file mode 100644 index 316f230d..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-acknowledgements.plist +++ /dev/null @@ -1,726 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2014-2021 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. - - License - MIT - Title - Alamofire - Type - PSGroupSpecifier - - - FooterText - The MIT License (MIT) - -Copyright (c) 2015 Badoo Development - -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. - - - License - MIT - Title - Chatto - Type - PSGroupSpecifier - - - FooterText - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - License - Apache License, Version 2.0 - Title - MDFInternationalization - Type - PSGroupSpecifier - - - FooterText - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - License - Apache License, Version 2.0 - Title - MDFTextAccessibility - Type - PSGroupSpecifier - - - FooterText - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - License - Apache 2.0 - Title - MaterialComponents - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m deleted file mode 100644 index f8f6a16c..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_ios : NSObject -@end -@implementation PodsDummy_Pods_ios -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index 9ad26baa..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,6 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh -${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework -${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework -${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework -${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework -${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index 46772e9f..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1,5 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Chatto.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFInternationalization.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFTextAccessibility.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index 9ad26baa..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,6 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh -${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework -${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework -${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework -${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework -${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index 46772e9f..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1,5 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Chatto.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFInternationalization.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MDFTextAccessibility.framework -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MaterialComponents.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh deleted file mode 100755 index e8ff2d45..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-frameworks.sh +++ /dev/null @@ -1,194 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" - install_framework "${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework" - install_framework "${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework" - install_framework "${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework" - install_framework "${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" - install_framework "${BUILT_PRODUCTS_DIR}/Chatto/Chatto.framework" - install_framework "${BUILT_PRODUCTS_DIR}/MDFInternationalization/MDFInternationalization.framework" - install_framework "${BUILT_PRODUCTS_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework" - install_framework "${BUILT_PRODUCTS_DIR}/MaterialComponents/MaterialComponents.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h deleted file mode 100644 index aed76e33..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_iosVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_iosVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig deleted file mode 100644 index 5b054a3d..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.debug.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap deleted file mode 100644 index b2424665..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_ios { - umbrella header "Pods-ios-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig deleted file mode 100644 index 5b054a3d..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-ios/Pods-ios.release.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist deleted file mode 100644 index 2243fe6e..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown deleted file mode 100644 index 368ad77a..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.markdown +++ /dev/null @@ -1,208 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## lottie-ios - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018 Airbnb, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -Generated by CocoaPods - https://cocoapods.org diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist deleted file mode 100644 index 74f98b65..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-acknowledgements.plist +++ /dev/null @@ -1,240 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018 Airbnb, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - License - Apache - Title - lottie-ios - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m deleted file mode 100644 index cb3a6f24..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_iosTests : NSObject -@end -@implementation PodsDummy_Pods_iosTests -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index 2a9a41f1..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh -${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index d7bcf7a8..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index 2a9a41f1..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh -${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index d7bcf7a8..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework \ No newline at end of file diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh deleted file mode 100755 index eb1b8ff2..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h deleted file mode 100644 index 1733239c..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_iosTestsVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_iosTestsVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig deleted file mode 100644 index d0bf2ecb..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "CoreGraphics" -framework "Lottie" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" -framework "QuartzCore" -framework "UIKit" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap deleted file mode 100644 index 0948081a..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_iosTests { - umbrella header "Pods-iosTests-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig deleted file mode 100644 index d0bf2ecb..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Chatto/Chatto.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFInternationalization/MDFInternationalization.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MDFTextAccessibility/MDFTextAccessibility.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MaterialComponents/MaterialComponents.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Chatto" -framework "CoreGraphics" -framework "Lottie" -framework "MDFInternationalization" -framework "MDFTextAccessibility" -framework "MaterialComponents" -framework "QuartzCore" -framework "UIKit" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist deleted file mode 100644 index 3ac477e6..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 3.3.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m deleted file mode 100644 index 67e66c90..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_lottie_ios : NSObject -@end -@implementation PodsDummy_lottie_ios -@end diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch deleted file mode 100644 index beb2a244..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h deleted file mode 100644 index 287f9db5..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double LottieVersionNumber; -FOUNDATION_EXPORT const unsigned char LottieVersionString[]; - diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig deleted file mode 100644 index bc9cd344..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "QuartzCore" -framework "UIKit" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/lottie-ios -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap deleted file mode 100644 index 494806f0..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Lottie { - umbrella header "lottie-ios-umbrella.h" - - export * - module * { export * } -} diff --git a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig b/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig deleted file mode 100644 index bc9cd344..00000000 --- a/test/fixtures/cocoapods/Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "QuartzCore" -framework "UIKit" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/lottie-ios -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/LICENSE b/test/fixtures/cocoapods/Pods/lottie-ios/LICENSE deleted file mode 100644 index 55bb1787..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018 Airbnb, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/README.md b/test/fixtures/cocoapods/Pods/lottie-ios/README.md deleted file mode 100644 index 21432266..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Lottie for iOS, macOS (and [Android](https://github.com/airbnb/lottie-android) and [React Native](https://github.com/airbnb/lottie-react-native)) -[![Version](https://img.shields.io/cocoapods/v/lottie-ios.svg?style=flat)](https://cocoapods.org/pods/lottie-ios)[![License](https://img.shields.io/cocoapods/l/lottie-ios.svg?style=flat)](https://cocoapods.org/pods/lottie-ios)[![Platform](https://img.shields.io/cocoapods/p/lottie-ios.svg?style=flat)](https://cocoapods.org/pods/lottie-ios) - -# View documentation, FAQ, help, examples, and more at [airbnb.io/lottie](http://airbnb.io/lottie/) - -Lottie is a mobile library for Android and iOS that natively renders vector based animations and art in realtime with minimal code. - -Lottie loads and renders animations and vectors exported in the bodymovin JSON format. Bodymovin JSON can be created and exported from After Effects with [bodymovin](https://github.com/bodymovin/bodymovin), Sketch with [Lottie Sketch Export](https://github.com/buba447/Lottie-Sketch-Export), and from [Haiku](https://www.haiku.ai). - -For the first time, designers can create **and ship** beautiful animations without an engineer painstakingly recreating it by hand. -Since the animation is backed by JSON they are extremely small in size but can be large in complexity! -Animations can be played, resized, looped, sped up, slowed down, reversed, and even interactively scrubbed. -Lottie can play or loop just a portion of the animation as well, the possibilities are endless! -Animations can even be ***changed at runtime*** in various ways! Change the color, position or any keyframable value! -Lottie also supports native UIViewController Transitions out of the box! - -Here is just a small sampling of the power of Lottie - -![Example1](_Gifs/Examples1.gif) -![Example2](_Gifs/Examples2.gif) - - - -![Example3](_Gifs/Examples3.gif) - -![Abcs](_Gifs/Examples4.gif) - -## Installing Lottie -Lottie supports [CocoaPods](https://cocoapods.org/) and [Carthage](https://github.com/Carthage/Carthage) (Both dynamic and static). Lottie is written in ***Swift 4.2***. -### Github Repo - -You can pull the [Lottie Github Repo](https://github.com/airbnb/lottie-ios/) and include the Lottie.xcodeproj to build a dynamic or static library. - -### CocoaPods -Add the pod to your Podfile: -```ruby -pod 'lottie-ios' -``` - -And then run: -```ruby -pod install -``` -After installing the cocoapod into your project import Lottie with -```swift -import Lottie -``` -### Carthage -Add Lottie to your Cartfile: -``` -github "airbnb/lottie-ios" "master" -``` - -And then run: -``` -carthage update -``` -In your application targets “General” tab under the “Linked Frameworks and Libraries” section, drag and drop lottie-ios.framework from the Carthage/Build/iOS directory that `carthage update` produced. - -### Swift Package Manager -``` swift -// swift-tools-version:5.1 - -import PackageDescription - -let package = Package( - name: "YourTestProject", - platforms: [ - .iOS(.v12), - ], - dependencies: [ - .package(name: "Lottie", url: "https://github.com/airbnb/lottie-ios.git", from: "3.2.1") - ], - targets: [ - .target(name: "YourTestProject", dependencies: ["Lottie"]) - ] -) -``` -And then import wherever needed: ```import Lottie``` - -#### Adding it to an existent iOS Project via Swift Package Manager - -1. Using Xcode 11 go to File > Swift Packages > Add Package Dependency -2. Paste the project URL: https://github.com/airbnb/lottie-ios -3. Click on next and select the project target -4. Don't forget to set `DEAD_CODE_STRIPPING = NO` in your `Build Settings` (https://bugs.swift.org/plugins/servlet/mobile#issue/SR-11564) - -If you have doubts, please, check the following links: - -[How to use](https://developer.apple.com/videos/play/wwdc2019/408/) - -[Creating Swift Packages](https://developer.apple.com/videos/play/wwdc2019/410/) - -After successfully retrieved the package and added it to your project, just import `Lottie` and you can get the full benefits of it. - ------ - -### Objective-C Support - -As of 3.0 Lottie has been completely rewritten in Swift! - -For Objective-C support please use Lottie 2.5.3. Alternatively an Objective-C branch exists and is still active. - -The official objective c branch can be found here: - -[Objective-C Branch](https://github.com/airbnb/lottie-ios/tree/lottie/objectiveC) - -Also check out the documentation regarding it here: - -[iOS Migration](http://airbnb.io/lottie/#/ios-migration) - -### Data collection - -The Lottie SDK does not collect any data. We provide this notice to help you fill out [App Privacy Details](https://developer.apple.com/app-store/app-privacy-details/). diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift deleted file mode 100644 index 543202c0..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/AnimationContainer.swift +++ /dev/null @@ -1,244 +0,0 @@ -// -// AnimationContainer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/24/19. -// - -import Foundation -import QuartzCore - -// MARK: - AnimationContainer - -/** - The base animation container. - - This layer holds a single composition container and allows for animation of - the currentFrame property. - */ -final class AnimationContainer: CALayer { - - // MARK: Lifecycle - - init( - animation: Animation, - imageProvider: AnimationImageProvider, - textProvider: AnimationTextProvider, - fontProvider: AnimationFontProvider) - { - layerImageProvider = LayerImageProvider(imageProvider: imageProvider, assets: animation.assetLibrary?.imageAssets) - layerTextProvider = LayerTextProvider(textProvider: textProvider) - layerFontProvider = LayerFontProvider(fontProvider: fontProvider) - animationLayers = [] - super.init() - bounds = animation.bounds - let layers = animation.layers.initializeCompositionLayers( - assetLibrary: animation.assetLibrary, - layerImageProvider: layerImageProvider, - textProvider: textProvider, - fontProvider: fontProvider, - frameRate: CGFloat(animation.framerate)) - - var imageLayers = [ImageCompositionLayer]() - var textLayers = [TextCompositionLayer]() - - var mattedLayer: CompositionLayer? = nil - - for layer in layers.reversed() { - layer.bounds = bounds - animationLayers.append(layer) - if let imageLayer = layer as? ImageCompositionLayer { - imageLayers.append(imageLayer) - } - if let textLayer = layer as? TextCompositionLayer { - textLayers.append(textLayer) - } - if let matte = mattedLayer { - /// The previous layer requires this layer to be its matte - matte.matteLayer = layer - mattedLayer = nil - continue - } - if - let matte = layer.matteType, - matte == .add || matte == .invert - { - /// We have a layer that requires a matte. - mattedLayer = layer - } - addSublayer(layer) - } - - layerImageProvider.addImageLayers(imageLayers) - layerImageProvider.reloadImages() - layerTextProvider.addTextLayers(textLayers) - layerTextProvider.reloadTexts() - layerFontProvider.addTextLayers(textLayers) - layerFontProvider.reloadTexts() - setNeedsDisplay() - } - - /// For CAAnimation Use - public override init(layer: Any) { - animationLayers = [] - layerImageProvider = LayerImageProvider(imageProvider: BlankImageProvider(), assets: nil) - layerTextProvider = LayerTextProvider(textProvider: DefaultTextProvider()) - layerFontProvider = LayerFontProvider(fontProvider: DefaultFontProvider()) - super.init(layer: layer) - - guard let animationLayer = layer as? AnimationContainer else { return } - - currentFrame = animationLayer.currentFrame - - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Public - - public var respectAnimationFrameRate: Bool = false - - // MARK: CALayer Animations - - override public class func needsDisplay(forKey key: String) -> Bool { - if key == "currentFrame" { - return true - } - return super.needsDisplay(forKey: key) - } - - override public func action(forKey event: String) -> CAAction? { - if event == "currentFrame" { - let animation = CABasicAnimation(keyPath: event) - animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) - animation.fromValue = presentation()?.currentFrame - return animation - } - return super.action(forKey: event) - } - - public override func display() { - guard Thread.isMainThread else { return } - var newFrame: CGFloat - if - let animationKeys = animationKeys(), - !animationKeys.isEmpty - { - newFrame = presentation()?.currentFrame ?? currentFrame - } else { - // We ignore the presentation's frame if there's no animation in the layer. - newFrame = currentFrame - } - if respectAnimationFrameRate { - newFrame = floor(newFrame) - } - animationLayers.forEach( { $0.displayWithFrame(frame: newFrame, forceUpdates: false) }) - } - - // MARK: Internal - - /// The animatable Current Frame Property - @NSManaged var currentFrame: CGFloat - - var animationLayers: ContiguousArray - - var imageProvider: AnimationImageProvider { - get { - layerImageProvider.imageProvider - } - set { - layerImageProvider.imageProvider = newValue - } - } - - var renderScale: CGFloat = 1 { - didSet { - animationLayers.forEach({ $0.renderScale = renderScale }) - } - } - - var textProvider: AnimationTextProvider { - get { layerTextProvider.textProvider } - set { layerTextProvider.textProvider = newValue } - } - - var fontProvider: AnimationFontProvider { - get { layerFontProvider.fontProvider } - set { layerFontProvider.fontProvider = newValue } - } - - func reloadImages() { - layerImageProvider.reloadImages() - } - - /// Forces the view to update its drawing. - func forceDisplayUpdate() { - animationLayers.forEach( { $0.displayWithFrame(frame: currentFrame, forceUpdates: true) }) - } - - func logHierarchyKeypaths() { - print("Lottie: Logging Animation Keypaths") - animationLayers.forEach({ $0.logKeypaths(for: nil) }) - } - - func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) { - for layer in animationLayers { - if let foundProperties = layer.nodeProperties(for: keypath) { - for property in foundProperties { - property.setProvider(provider: valueProvider) - } - layer.displayWithFrame(frame: presentation()?.currentFrame ?? currentFrame, forceUpdates: true) - } - } - } - - func getValue(for keypath: AnimationKeypath, atFrame: CGFloat?) -> Any? { - for layer in animationLayers { - if - let foundProperties = layer.nodeProperties(for: keypath), - let first = foundProperties.first - { - return first.valueProvider.value(frame: atFrame ?? currentFrame) - } - } - return nil - } - - func layer(for keypath: AnimationKeypath) -> CALayer? { - for layer in animationLayers { - if let foundLayer = layer.layer(for: keypath) { - return foundLayer - } - } - return nil - } - - func animatorNodes(for keypath: AnimationKeypath) -> [AnimatorNode]? { - var results = [AnimatorNode]() - for layer in animationLayers { - if let nodes = layer.animatorNodes(for: keypath) { - results.append(contentsOf: nodes) - } - } - if results.count == 0 { - return nil - } - return results - } - - // MARK: Fileprivate - - fileprivate let layerImageProvider: LayerImageProvider - fileprivate let layerTextProvider: LayerTextProvider - fileprivate let layerFontProvider: LayerFontProvider -} - -// MARK: - BlankImageProvider - -fileprivate class BlankImageProvider: AnimationImageProvider { - func imageForAsset(asset _: ImageAsset) -> CGImage? { - nil - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift deleted file mode 100644 index 954c9ddc..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/CompositionLayer.swift +++ /dev/null @@ -1,160 +0,0 @@ -// -// LayerContainer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/22/19. -// - -import Foundation -import QuartzCore - -// MARK: - CompositionLayer - -/** - The base class for a child layer of CompositionContainer - */ -class CompositionLayer: CALayer, KeypathSearchable { - - // MARK: Lifecycle - - init(layer: LayerModel, size: CGSize) { - transformNode = LayerTransformNode(transform: layer.transform) - if let masks = layer.masks { - maskLayer = MaskContainerLayer(masks: masks) - } else { - maskLayer = nil - } - matteType = layer.matte - inFrame = layer.inFrame.cgFloat - outFrame = layer.outFrame.cgFloat - timeStretch = layer.timeStretch.cgFloat - startFrame = layer.startTime.cgFloat - keypathName = layer.name - childKeypaths = [transformNode.transformProperties] - super.init() - anchorPoint = .zero - actions = [ - "opacity" : NSNull(), - "transform" : NSNull(), - "bounds" : NSNull(), - "anchorPoint" : NSNull(), - "sublayerTransform" : NSNull(), - ] - - contentsLayer.anchorPoint = .zero - contentsLayer.bounds = CGRect(origin: .zero, size: size) - contentsLayer.actions = [ - "opacity" : NSNull(), - "transform" : NSNull(), - "bounds" : NSNull(), - "anchorPoint" : NSNull(), - "sublayerTransform" : NSNull(), - "hidden" : NSNull(), - ] - addSublayer(contentsLayer) - - if let maskLayer = maskLayer { - contentsLayer.mask = maskLayer - } - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? CompositionLayer else { - fatalError("Wrong Layer Class") - } - transformNode = layer.transformNode - matteType = layer.matteType - inFrame = layer.inFrame - outFrame = layer.outFrame - timeStretch = layer.timeStretch - startFrame = layer.startFrame - keypathName = layer.keypathName - childKeypaths = [transformNode.transformProperties] - maskLayer = nil - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - weak var layerDelegate: CompositionLayerDelegate? - - let transformNode: LayerTransformNode - - let contentsLayer = CALayer() - - let maskLayer: MaskContainerLayer? - - let matteType: MatteType? - - let inFrame: CGFloat - let outFrame: CGFloat - let startFrame: CGFloat - let timeStretch: CGFloat - - // MARK: Keypath Searchable - - let keypathName: String - - final var childKeypaths: [KeypathSearchable] - - var renderScale: CGFloat = 1 { - didSet { - updateRenderScale() - } - } - - var matteLayer: CompositionLayer? { - didSet { - if let matte = matteLayer { - if let type = matteType, type == .invert { - mask = InvertedMatteLayer(inputMatte: matte) - } else { - mask = matte - } - } else { - mask = nil - } - } - } - - var keypathProperties: [String: AnyNodeProperty] { - [:] - } - - var keypathLayer: CALayer? { - contentsLayer - } - - final func displayWithFrame(frame: CGFloat, forceUpdates: Bool) { - transformNode.updateTree(frame, forceUpdates: forceUpdates) - let layerVisible = frame.isInRangeOrEqual(inFrame, outFrame) - /// Only update contents if current time is within the layers time bounds. - if layerVisible { - displayContentsWithFrame(frame: frame, forceUpdates: forceUpdates) - maskLayer?.updateWithFrame(frame: frame, forceUpdates: forceUpdates) - } - contentsLayer.transform = transformNode.globalTransform - contentsLayer.opacity = transformNode.opacity - contentsLayer.isHidden = !layerVisible - layerDelegate?.frameUpdated(frame: frame) - } - - func displayContentsWithFrame(frame _: CGFloat, forceUpdates _: Bool) { - /// To be overridden by subclass - } - - func updateRenderScale() { - contentsScale = renderScale - } -} - -// MARK: - CompositionLayerDelegate - -protocol CompositionLayerDelegate: AnyObject { - func frameUpdated(frame: CGFloat) -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift deleted file mode 100644 index b1be9800..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ImageCompositionLayer.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// ImageCompositionLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -final class ImageCompositionLayer: CompositionLayer { - - // MARK: Lifecycle - - init(imageLayer: ImageLayerModel, size: CGSize) { - imageReferenceID = imageLayer.referenceID - super.init(layer: imageLayer, size: size) - contentsLayer.masksToBounds = true - contentsLayer.contentsGravity = CALayerContentsGravity.resize - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? ImageCompositionLayer else { - fatalError("init(layer:) Wrong Layer Class") - } - imageReferenceID = layer.imageReferenceID - image = nil - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - let imageReferenceID: String - - var image: CGImage? = nil { - didSet { - if let image = image { - contentsLayer.contents = image - } else { - contentsLayer.contents = nil - } - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift deleted file mode 100644 index 464316bd..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/MaskContainerLayer.swift +++ /dev/null @@ -1,190 +0,0 @@ -// -// MaskContainerLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import Foundation -import QuartzCore - -extension MaskMode { - var usableMode: MaskMode { - switch self { - case .add: - return .add - case .subtract: - return .subtract - case .intersect: - return .intersect - case .lighten: - return .add - case .darken: - return .darken - case .difference: - return .intersect - case .none: - return .none - } - } -} - -// MARK: - MaskContainerLayer - -final class MaskContainerLayer: CALayer { - - // MARK: Lifecycle - - init(masks: [Mask]) { - super.init() - anchorPoint = .zero - var containerLayer = CALayer() - var firstObject: Bool = true - for mask in masks { - let maskLayer = MaskLayer(mask: mask) - maskLayers.append(maskLayer) - if mask.mode.usableMode == .none { - continue - } else if mask.mode.usableMode == .add || firstObject { - firstObject = false - containerLayer.addSublayer(maskLayer) - } else { - containerLayer.mask = maskLayer - let newContainer = CALayer() - newContainer.addSublayer(containerLayer) - containerLayer = newContainer - } - } - addSublayer(containerLayer) - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? MaskContainerLayer else { - fatalError("init(layer:) Wrong Layer Class") - } - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - func updateWithFrame(frame: CGFloat, forceUpdates: Bool) { - maskLayers.forEach({ $0.updateWithFrame(frame: frame, forceUpdates: forceUpdates) }) - } - - // MARK: Fileprivate - - fileprivate var maskLayers: [MaskLayer] = [] -} - -extension CGRect { - static var veryLargeRect: CGRect { - CGRect( - x: -100_000_000, - y: -100_000_000, - width: 200_000_000, - height: 200_000_000) - } -} - -// MARK: - MaskLayer - -fileprivate class MaskLayer: CALayer { - - // MARK: Lifecycle - - init(mask: Mask) { - properties = MaskNodeProperties(mask: mask) - super.init() - addSublayer(maskLayer) - anchorPoint = .zero - maskLayer.fillColor = mask.mode == .add ? CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 0, 0, 1]) : - CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 1, 0, 1]) - maskLayer.fillRule = CAShapeLayerFillRule.evenOdd - actions = [ - "opacity" : NSNull(), - ] - - } - - override init(layer: Any) { - properties = nil - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - let properties: MaskNodeProperties? - - let maskLayer = CAShapeLayer() - - func updateWithFrame(frame: CGFloat, forceUpdates: Bool) { - guard let properties = properties else { return } - if properties.opacity.needsUpdate(frame: frame) || forceUpdates { - properties.opacity.update(frame: frame) - opacity = Float(properties.opacity.value.cgFloatValue) - } - - if properties.shape.needsUpdate(frame: frame) || forceUpdates { - properties.shape.update(frame: frame) - properties.expansion.update(frame: frame) - - let shapePath = properties.shape.value.cgPath() - var path = shapePath - if - properties.mode.usableMode == .subtract && !properties.inverted || - (properties.mode.usableMode == .add && properties.inverted) - { - /// Add a bounds rect to invert the mask - let newPath = CGMutablePath() - newPath.addRect(CGRect.veryLargeRect) - newPath.addPath(shapePath) - path = newPath - } - maskLayer.path = path - } - - } -} - -// MARK: - MaskNodeProperties - -fileprivate class MaskNodeProperties: NodePropertyMap { - - // MARK: Lifecycle - - init(mask: Mask) { - mode = mask.mode - inverted = mask.inverted - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.opacity.keyframes)) - shape = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.shape.keyframes)) - expansion = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.expansion.keyframes)) - propertyMap = [ - "Opacity" : opacity, - "Shape" : shape, - "Expansion" : expansion, - ] - properties = Array(propertyMap.values) - } - - // MARK: Internal - - var propertyMap: [String: AnyNodeProperty] - - var properties: [AnyNodeProperty] - - let mode: MaskMode - let inverted: Bool - - let opacity: NodeProperty - let shape: NodeProperty - let expansion: NodeProperty -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift deleted file mode 100644 index 3fdf1637..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/NullCompositionLayer.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// NullCompositionLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import Foundation - -final class NullCompositionLayer: CompositionLayer { - - init(layer: LayerModel) { - super.init(layer: layer, size: .zero) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? NullCompositionLayer else { - fatalError("init(layer:) Wrong Layer Class") - } - super.init(layer: layer) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift deleted file mode 100644 index d0722deb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/PreCompositionLayer.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// PreCompositionLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import Foundation -import QuartzCore - -final class PreCompositionLayer: CompositionLayer { - - // MARK: Lifecycle - - init( - precomp: PreCompLayerModel, - asset: PrecompAsset, - layerImageProvider: LayerImageProvider, - textProvider: AnimationTextProvider, - fontProvider: AnimationFontProvider, - assetLibrary: AssetLibrary?, - frameRate: CGFloat) - { - animationLayers = [] - if let keyframes = precomp.timeRemapping?.keyframes { - remappingNode = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframes)) - } else { - remappingNode = nil - } - self.frameRate = frameRate - super.init(layer: precomp, size: CGSize(width: precomp.width, height: precomp.height)) - bounds = CGRect(origin: .zero, size: CGSize(width: precomp.width, height: precomp.height)) - contentsLayer.masksToBounds = true - contentsLayer.bounds = bounds - - let layers = asset.layers.initializeCompositionLayers( - assetLibrary: assetLibrary, - layerImageProvider: layerImageProvider, - textProvider: textProvider, - fontProvider: fontProvider, - frameRate: frameRate) - - var imageLayers = [ImageCompositionLayer]() - - var mattedLayer: CompositionLayer? = nil - - for layer in layers.reversed() { - layer.bounds = bounds - animationLayers.append(layer) - if let imageLayer = layer as? ImageCompositionLayer { - imageLayers.append(imageLayer) - } - if let matte = mattedLayer { - /// The previous layer requires this layer to be its matte - matte.matteLayer = layer - mattedLayer = nil - continue - } - if - let matte = layer.matteType, - matte == .add || matte == .invert - { - /// We have a layer that requires a matte. - mattedLayer = layer - } - contentsLayer.addSublayer(layer) - } - - childKeypaths.append(contentsOf: layers) - - layerImageProvider.addImageLayers(imageLayers) - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? PreCompositionLayer else { - fatalError("init(layer:) Wrong Layer Class") - } - frameRate = layer.frameRate - remappingNode = nil - animationLayers = [] - - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - let frameRate: CGFloat - let remappingNode: NodeProperty? - - override var keypathProperties: [String: AnyNodeProperty] { - guard let remappingNode = remappingNode else { - return super.keypathProperties - } - return ["Time Remap" : remappingNode] - } - - override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { - let localFrame: CGFloat - if let remappingNode = remappingNode { - remappingNode.update(frame: frame) - localFrame = remappingNode.value.cgFloatValue * frameRate - } else { - localFrame = (frame - startFrame) / timeStretch - } - animationLayers.forEach( { $0.displayWithFrame(frame: localFrame, forceUpdates: forceUpdates) }) - } - - override func updateRenderScale() { - super.updateRenderScale() - animationLayers.forEach( { $0.renderScale = renderScale } ) - } - - // MARK: Fileprivate - - fileprivate var animationLayers: [CompositionLayer] -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift deleted file mode 100644 index ebcf2a85..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/ShapeCompositionLayer.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// ShapeLayerContainer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/22/19. -// - -import CoreGraphics -import Foundation - -/** - A CompositionLayer responsible for initializing and rendering shapes - */ -final class ShapeCompositionLayer: CompositionLayer { - - // MARK: Lifecycle - - init(shapeLayer: ShapeLayerModel) { - let results = shapeLayer.items.initializeNodeTree() - let renderContainer = ShapeContainerLayer() - self.renderContainer = renderContainer - rootNode = results.rootNode - super.init(layer: shapeLayer, size: .zero) - contentsLayer.addSublayer(renderContainer) - for container in results.renderContainers { - renderContainer.insertRenderLayer(container) - } - rootNode?.updateTree(0, forceUpdates: true) - childKeypaths.append(contentsOf: results.childrenNodes) - } - - override init(layer: Any) { - guard let layer = layer as? ShapeCompositionLayer else { - fatalError("init(layer:) wrong class.") - } - rootNode = nil - renderContainer = nil - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - let rootNode: AnimatorNode? - let renderContainer: ShapeContainerLayer? - - override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { - rootNode?.updateTree(frame, forceUpdates: forceUpdates) - renderContainer?.markRenderUpdates(forFrame: frame) - } - - override func updateRenderScale() { - super.updateRenderScale() - renderContainer?.renderScale = renderScale - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift deleted file mode 100644 index 76d6f86c..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/SolidCompositionLayer.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// SolidCompositionLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import Foundation -import QuartzCore - -final class SolidCompositionLayer: CompositionLayer { - - // MARK: Lifecycle - - init(solid: SolidLayerModel) { - let components = solid.colorHex.hexColorComponents() - colorProperty = - NodeProperty(provider: SingleValueProvider(Color( - r: Double(components.red), - g: Double(components.green), - b: Double(components.blue), - a: 1))) - - super.init(layer: solid, size: .zero) - solidShape.path = CGPath(rect: CGRect(x: 0, y: 0, width: solid.width, height: solid.height), transform: nil) - contentsLayer.addSublayer(solidShape) - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? SolidCompositionLayer else { - fatalError("init(layer:) Wrong Layer Class") - } - colorProperty = layer.colorProperty - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - let colorProperty: NodeProperty? - let solidShape = CAShapeLayer() - - override var keypathProperties: [String: AnyNodeProperty] { - guard let colorProperty = colorProperty else { return super.keypathProperties } - return ["Color" : colorProperty] - } - - override func displayContentsWithFrame(frame: CGFloat, forceUpdates _: Bool) { - guard let colorProperty = colorProperty else { return } - colorProperty.update(frame: frame) - solidShape.fillColor = colorProperty.value.cgColorValue - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift deleted file mode 100644 index 2ae50bb7..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/CompLayers/TextCompositionLayer.swift +++ /dev/null @@ -1,149 +0,0 @@ -// -// TextCompositionLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import CoreGraphics -import CoreText -import Foundation -import QuartzCore - -/// Needed for NSMutableParagraphStyle... -#if os(OSX) -import AppKit -#else -import UIKit -#endif - -extension TextJustification { - var textAlignment: NSTextAlignment { - switch self { - case .left: - return .left - case .right: - return .right - case .center: - return .center - } - } - - var caTextAlignement: CATextLayerAlignmentMode { - switch self { - case .left: - return .left - case .right: - return .right - case .center: - return .center - } - } -} - -// MARK: - TextCompositionLayer - -final class TextCompositionLayer: CompositionLayer { - - // MARK: Lifecycle - - init(textLayer: TextLayerModel, textProvider: AnimationTextProvider, fontProvider: AnimationFontProvider) { - var rootNode: TextAnimatorNode? - for animator in textLayer.animators { - rootNode = TextAnimatorNode(parentNode: rootNode, textAnimator: animator) - } - self.rootNode = rootNode - textDocument = KeyframeInterpolator(keyframes: textLayer.text.keyframes) - - self.textProvider = textProvider - self.fontProvider = fontProvider - - super.init(layer: textLayer, size: .zero) - contentsLayer.addSublayer(self.textLayer) - self.textLayer.masksToBounds = false - self.textLayer.isGeometryFlipped = true - - if let rootNode = rootNode { - childKeypaths.append(rootNode) - } - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override init(layer: Any) { - /// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init - guard let layer = layer as? TextCompositionLayer else { - fatalError("init(layer:) Wrong Layer Class") - } - rootNode = nil - textDocument = nil - - textProvider = DefaultTextProvider() - fontProvider = DefaultFontProvider() - - super.init(layer: layer) - } - - // MARK: Internal - - let rootNode: TextAnimatorNode? - let textDocument: KeyframeInterpolator? - - let textLayer = TextLayer() - var textProvider: AnimationTextProvider - var fontProvider: AnimationFontProvider - - override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { - guard let textDocument = textDocument else { return } - - textLayer.contentsScale = renderScale - - let documentUpdate = textDocument.hasUpdate(frame: frame) - let animatorUpdate = rootNode?.updateContents(frame, forceLocalUpdate: forceUpdates) ?? false - guard documentUpdate == true || animatorUpdate == true else { return } - - rootNode?.rebuildOutputs(frame: frame) - - // Get Text Attributes - let text = textDocument.value(frame: frame) as! TextDocument - let strokeColor = rootNode?.textOutputNode.strokeColor ?? text.strokeColorData?.cgColorValue - let strokeWidth = rootNode?.textOutputNode.strokeWidth ?? CGFloat(text.strokeWidth ?? 0) - let tracking = (CGFloat(text.fontSize) * (rootNode?.textOutputNode.tracking ?? CGFloat(text.tracking))) / 1000.0 - let matrix = rootNode?.textOutputNode.xform ?? CATransform3DIdentity - let textString = textProvider.textFor(keypathName: keypathName, sourceText: text.text) - let ctFont = fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize)) - - // Set all of the text layer options - textLayer.text = textString - textLayer.font = ctFont - textLayer.alignment = text.justification.textAlignment - textLayer.lineHeight = CGFloat(text.lineHeight) - textLayer.tracking = tracking - - if let fillColor = rootNode?.textOutputNode.fillColor { - textLayer.fillColor = fillColor - } else if let fillColor = text.fillColorData?.cgColorValue { - textLayer.fillColor = fillColor - } else { - textLayer.fillColor = nil - } - - textLayer.preferredSize = text.textFrameSize?.sizeValue - textLayer.strokeOnTop = text.strokeOverFill ?? false - textLayer.strokeWidth = strokeWidth - textLayer.strokeColor = strokeColor - textLayer.sizeToFit() - - textLayer.opacity = Float(rootNode?.textOutputNode.opacity ?? 1) - textLayer.transform = CATransform3DIdentity - textLayer.position = text.textFramePosition?.pointValue ?? CGPoint.zero - textLayer.transform = matrix - } - - override func updateRenderScale() { - super.updateRenderScale() - textLayer.contentsScale = renderScale - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift deleted file mode 100644 index fb4aa2b3..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/CompositionLayersInitializer.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// CompositionLayersInitializer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import CoreGraphics -import Foundation - -extension Array where Element == LayerModel { - - func initializeCompositionLayers( - assetLibrary: AssetLibrary?, - layerImageProvider: LayerImageProvider, - textProvider: AnimationTextProvider, - fontProvider: AnimationFontProvider, - frameRate: CGFloat) -> [CompositionLayer] - { - var compositionLayers = [CompositionLayer]() - var layerMap = [Int : CompositionLayer]() - - /// Organize the assets into a dictionary of [ID : ImageAsset] - var childLayers = [LayerModel]() - - for layer in self { - if layer.hidden == true { - let genericLayer = NullCompositionLayer(layer: layer) - compositionLayers.append(genericLayer) - layerMap[layer.index] = genericLayer - } else if let shapeLayer = layer as? ShapeLayerModel { - let shapeContainer = ShapeCompositionLayer(shapeLayer: shapeLayer) - compositionLayers.append(shapeContainer) - layerMap[layer.index] = shapeContainer - } else if let solidLayer = layer as? SolidLayerModel { - let solidContainer = SolidCompositionLayer(solid: solidLayer) - compositionLayers.append(solidContainer) - layerMap[layer.index] = solidContainer - } else if - let precompLayer = layer as? PreCompLayerModel, - let assetLibrary = assetLibrary, - let precompAsset = assetLibrary.precompAssets[precompLayer.referenceID] - { - let precompContainer = PreCompositionLayer( - precomp: precompLayer, - asset: precompAsset, - layerImageProvider: layerImageProvider, - textProvider: textProvider, - fontProvider: fontProvider, - assetLibrary: assetLibrary, - frameRate: frameRate) - compositionLayers.append(precompContainer) - layerMap[layer.index] = precompContainer - } else if - let imageLayer = layer as? ImageLayerModel, - let assetLibrary = assetLibrary, - let imageAsset = assetLibrary.imageAssets[imageLayer.referenceID] - { - let imageContainer = ImageCompositionLayer( - imageLayer: imageLayer, - size: CGSize(width: imageAsset.width, height: imageAsset.height)) - compositionLayers.append(imageContainer) - layerMap[layer.index] = imageContainer - } else if let textLayer = layer as? TextLayerModel { - let textContainer = TextCompositionLayer(textLayer: textLayer, textProvider: textProvider, fontProvider: fontProvider) - compositionLayers.append(textContainer) - layerMap[layer.index] = textContainer - } else { - let genericLayer = NullCompositionLayer(layer: layer) - compositionLayers.append(genericLayer) - layerMap[layer.index] = genericLayer - } - if layer.parent != nil { - childLayers.append(layer) - } - } - - /// Now link children with their parents - for layerModel in childLayers { - if let parentID = layerModel.parent { - let childLayer = layerMap[layerModel.index] - let parentLayer = layerMap[parentID] - childLayer?.transformNode.parentNode = parentLayer?.transformNode - } - } - - return compositionLayers - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift deleted file mode 100644 index 962cfdca..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/InvertedMatteLayer.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// InvertedMatteLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/28/19. -// - -import Foundation -import QuartzCore - -/** - A layer that inverses the alpha output of its input layer. - - WARNING: This is experimental and probably not very performant. - */ -final class InvertedMatteLayer: CALayer, CompositionLayerDelegate { - - // MARK: Lifecycle - - init(inputMatte: CompositionLayer) { - self.inputMatte = inputMatte - super.init() - inputMatte.layerDelegate = self - anchorPoint = .zero - bounds = inputMatte.bounds - setNeedsDisplay() - } - - override init(layer: Any) { - guard let layer = layer as? InvertedMatteLayer else { - fatalError("init(layer:) wrong class.") - } - inputMatte = nil - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - let inputMatte: CompositionLayer? - let wrapperLayer = CALayer() - - func frameUpdated(frame _: CGFloat) { - setNeedsDisplay() - displayIfNeeded() - } - - override func draw(in ctx: CGContext) { - guard let inputMatte = inputMatte else { return } - guard let fillColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 1]) - else { return } - ctx.setFillColor(fillColor) - ctx.fill(bounds) - ctx.setBlendMode(.destinationOut) - inputMatte.render(in: ctx) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift deleted file mode 100644 index dda3f372..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerFontProvider.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// LayerFontProvider.swift -// Lottie -// -// Created by Brandon Withrow on 8/5/20. -// Copyright © 2020 YurtvilleProds. All rights reserved. -// - -import Foundation - -/// Connects a LottieFontProvider to a group of text layers -final class LayerFontProvider { - - // MARK: Lifecycle - - init(fontProvider: AnimationFontProvider) { - self.fontProvider = fontProvider - textLayers = [] - reloadTexts() - } - - // MARK: Internal - - fileprivate(set) var textLayers: [TextCompositionLayer] - - var fontProvider: AnimationFontProvider { - didSet { - reloadTexts() - } - } - - func addTextLayers(_ layers: [TextCompositionLayer]) { - textLayers += layers - } - - func reloadTexts() { - textLayers.forEach { - $0.fontProvider = fontProvider - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift deleted file mode 100644 index 53bf9297..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerImageProvider.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// LayerImageProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import Foundation - -/// Connects a LottieImageProvider to a group of image layers -final class LayerImageProvider { - - // MARK: Lifecycle - - init(imageProvider: AnimationImageProvider, assets: [String: ImageAsset]?) { - self.imageProvider = imageProvider - imageLayers = [ImageCompositionLayer]() - if let assets = assets { - imageAssets = assets - } else { - imageAssets = [:] - } - reloadImages() - } - - // MARK: Internal - - fileprivate(set) var imageLayers: [ImageCompositionLayer] - let imageAssets: [String: ImageAsset] - - var imageProvider: AnimationImageProvider { - didSet { - reloadImages() - } - } - - func addImageLayers(_ layers: [ImageCompositionLayer]) { - for layer in layers { - if imageAssets[layer.imageReferenceID] != nil { - /// Found a linking asset in our asset library. Add layer - imageLayers.append(layer) - } - } - } - - func reloadImages() { - for imageLayer in imageLayers { - if let asset = imageAssets[imageLayer.imageReferenceID] { - imageLayer.image = imageProvider.imageForAsset(asset: asset) - } - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift deleted file mode 100644 index ce766a51..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTextProvider.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// LayerTextProvider.swift -// lottie-ios-iOS -// -// Created by Alexandr Goncharov on 07/06/2019. -// - -import Foundation - -/// Connects a LottieTextProvider to a group of text layers -final class LayerTextProvider { - - // MARK: Lifecycle - - init(textProvider: AnimationTextProvider) { - self.textProvider = textProvider - textLayers = [] - reloadTexts() - } - - // MARK: Internal - - fileprivate(set) var textLayers: [TextCompositionLayer] - - var textProvider: AnimationTextProvider { - didSet { - reloadTexts() - } - } - - func addTextLayers(_ layers: [TextCompositionLayer]) { - textLayers += layers - } - - func reloadTexts() { - textLayers.forEach { - $0.textProvider = textProvider - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift deleted file mode 100644 index 335b7808..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/LayerTransformNode.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// LayerTransformPropertyMap.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -// MARK: - LayerTransformProperties - -final class LayerTransformProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(transform: Transform) { - - anchor = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.anchorPoint.keyframes)) - scale = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.scale.keyframes)) - rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotation.keyframes)) - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.opacity.keyframes)) - - var propertyMap: [String: AnyNodeProperty] = [ - "Anchor Point" : anchor, - "Scale" : scale, - "Rotation" : rotation, - "Opacity" : opacity, - ] - - if - let positionKeyframesX = transform.positionX?.keyframes, - let positionKeyframesY = transform.positionY?.keyframes - { - let xPosition: NodeProperty = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframesX)) - let yPosition: NodeProperty = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframesY)) - propertyMap["X Position"] = xPosition - propertyMap["Y Position"] = yPosition - positionX = xPosition - positionY = yPosition - position = nil - } else if let positionKeyframes = transform.position?.keyframes { - let position: NodeProperty = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframes)) - propertyMap["Position"] = position - self.position = position - positionX = nil - positionY = nil - } else { - position = nil - positionY = nil - positionX = nil - } - - keypathProperties = propertyMap - properties = Array(propertyMap.values) - } - - // MARK: Internal - - let keypathProperties: [String: AnyNodeProperty] - var keypathName: String = "Transform" - - let properties: [AnyNodeProperty] - - let anchor: NodeProperty - let scale: NodeProperty - let rotation: NodeProperty - let position: NodeProperty? - let positionX: NodeProperty? - let positionY: NodeProperty? - let opacity: NodeProperty - - var childKeypaths: [KeypathSearchable] { - [] - } -} - -// MARK: - LayerTransformNode - -class LayerTransformNode: AnimatorNode { - - // MARK: Lifecycle - - init(transform: Transform) { - transformProperties = LayerTransformProperties(transform: transform) - } - - // MARK: Internal - - let outputNode: NodeOutput = PassThroughOutputNode(parent: nil) - - let transformProperties: LayerTransformProperties - - var parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - var isEnabled: Bool = true - - var opacity: Float = 1 - var localTransform: CATransform3D = CATransform3DIdentity - var globalTransform: CATransform3D = CATransform3DIdentity - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - transformProperties - } - - func shouldRebuildOutputs(frame _: CGFloat) -> Bool { - hasLocalUpdates || hasUpstreamUpdates - } - - func rebuildOutputs(frame _: CGFloat) { - opacity = Float(transformProperties.opacity.value.cgFloatValue) * 0.01 - - let position: CGPoint - if let point = transformProperties.position?.value.pointValue { - position = point - } else if - let xPos = transformProperties.positionX?.value.cgFloatValue, - let yPos = transformProperties.positionY?.value.cgFloatValue - { - position = CGPoint(x: xPos, y: yPos) - } else { - position = .zero - } - - localTransform = CATransform3D.makeTransform( - anchor: transformProperties.anchor.value.pointValue, - position: position, - scale: transformProperties.scale.value.sizeValue, - rotation: transformProperties.rotation.value.cgFloatValue, - skew: nil, - skewAxis: nil) - - if let parentNode = parentNode as? LayerTransformNode { - globalTransform = CATransform3DConcat(localTransform, parentNode.globalTransform) - } else { - globalTransform = localTransform - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift deleted file mode 100644 index 1cb911da..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/LayerContainers/Utility/TextLayer.swift +++ /dev/null @@ -1,321 +0,0 @@ -// -// TextLayer.swift -// Pods -// -// Created by Brandon Withrow on 8/3/20. -// - -import CoreGraphics -import CoreText -import Foundation -import QuartzCore -/// Needed for NSMutableParagraphStyle... -#if os(OSX) -import AppKit -#else -import UIKit -#endif - -// MARK: - TextLayer - -final class TextLayer: CALayer { - - // MARK: Public - - public var text: String? { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var font: CTFont? { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var alignment: NSTextAlignment = .left { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var lineHeight: CGFloat = 0 { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var tracking: CGFloat = 0 { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var fillColor: CGColor? { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var strokeColor: CGColor? { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var strokeWidth: CGFloat = 0 { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public var strokeOnTop: Bool = false { - didSet { - setNeedsLayout() - setNeedsDisplay() - } - } - - public var preferredSize: CGSize? { - didSet { - needsContentUpdate = true - setNeedsLayout() - setNeedsDisplay() - } - } - - public func sizeToFit() { - updateTextContent() - bounds = drawingRect - anchorPoint = drawingAnchor - setNeedsLayout() - setNeedsDisplay() - } - - // MARK: Internal - - override func action(forKey _: String) -> CAAction? { - nil - } - - override func draw(in ctx: CGContext) { - guard let attributedString = attributedString else { return } - updateTextContent() - guard fillFrameSetter != nil || strokeFrameSetter != nil else { return } - - ctx.textMatrix = .identity - ctx.setAllowsAntialiasing(true) - ctx.setAllowsFontSmoothing(true) - ctx.setAllowsFontSubpixelPositioning(true) - ctx.setAllowsFontSubpixelQuantization(true) - - ctx.setShouldAntialias(true) - ctx.setShouldSmoothFonts(true) - ctx.setShouldSubpixelPositionFonts(true) - ctx.setShouldSubpixelQuantizeFonts(true) - - if contentsAreFlipped() { - ctx.translateBy(x: 0, y: drawingRect.height) - ctx.scaleBy(x: 1.0, y: -1.0) - } - - let drawingPath = CGPath(rect: drawingRect, transform: nil) - - let fillFrame: CTFrame? - if let setter = fillFrameSetter { - fillFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, attributedString.length), drawingPath, nil) - } else { - fillFrame = nil - } - - let strokeFrame: CTFrame? - if let setter = strokeFrameSetter { - strokeFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, attributedString.length), drawingPath, nil) - } else { - strokeFrame = nil - } - - if !strokeOnTop, let strokeFrame = strokeFrame { - CTFrameDraw(strokeFrame, ctx) - } - - if let fillFrame = fillFrame { - CTFrameDraw(fillFrame, ctx) - } - - if strokeOnTop, let strokeFrame = strokeFrame { - CTFrameDraw(strokeFrame, ctx) - } - } - - // MARK: Private - - private var drawingRect: CGRect = .zero - private var drawingAnchor: CGPoint = .zero - private var fillFrameSetter: CTFramesetter? - private var attributedString: NSAttributedString? - private var strokeFrameSetter: CTFramesetter? - private var needsContentUpdate: Bool = false - - // Draws Debug colors for the font alignment. - @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) - private func drawDebug(_ ctx: CGContext) { - if let font = font { - let ascent = CTFontGetAscent(font) - let descent = CTFontGetDescent(font) - let capHeight = CTFontGetCapHeight(font) - let leading = CTFontGetLeading(font) - - // Ascent Red - ctx.setFillColor(CGColor(srgbRed: 1, green: 0, blue: 0, alpha: 0.5)) - ctx.fill(CGRect(x: 0, y: 0, width: drawingRect.width, height: ascent)) - - // Descent Blue - ctx.setFillColor(CGColor(srgbRed: 0, green: 0, blue: 1, alpha: 0.5)) - ctx.fill(CGRect(x: 0, y: ascent, width: drawingRect.width, height: descent)) - - // Leading Yellow - ctx.setFillColor(CGColor(srgbRed: 1, green: 1, blue: 0, alpha: 0.5)) - ctx.fill(CGRect(x: 0, y: ascent + descent, width: drawingRect.width, height: leading)) - - // Cap height Green - ctx.setFillColor(CGColor(srgbRed: 0, green: 1, blue: 0, alpha: 0.5)) - ctx.fill(CGRect(x: 0, y: ascent - capHeight, width: drawingRect.width, height: capHeight)) - - if drawingRect.height - ascent + descent + leading > 0 { - // Remainder - ctx.setFillColor(CGColor(srgbRed: 0, green: 1, blue: 1, alpha: 0.5)) - ctx - .fill(CGRect( - x: 0, - y: ascent + descent + leading, - width: drawingRect.width, - height: drawingRect.height - ascent + descent + leading)) - } - } - } - - private func updateTextContent() { - guard needsContentUpdate else { return } - needsContentUpdate = false - guard let font = font, let text = text, text.count > 0, fillColor != nil || strokeColor != nil else { - drawingRect = .zero - drawingAnchor = .zero - attributedString = nil - fillFrameSetter = nil - strokeFrameSetter = nil - return - } - - // Get Font properties - let ascent = CTFontGetAscent(font) - let descent = CTFontGetDescent(font) - let capHeight = CTFontGetCapHeight(font) - let leading = CTFontGetLeading(font) - let minLineHeight = -(ascent + descent + leading) - - // Calculate line spacing - let lineSpacing = max(CGFloat(minLineHeight) + lineHeight, CGFloat(minLineHeight)) - // Build Attributes - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineSpacing = lineSpacing - paragraphStyle.lineHeightMultiple = 1 - paragraphStyle.maximumLineHeight = ascent + descent + leading - paragraphStyle.alignment = alignment - paragraphStyle.lineBreakMode = NSLineBreakMode.byWordWrapping - var attributes: [NSAttributedString.Key: Any] = [ - NSAttributedString.Key.ligature: 0, - NSAttributedString.Key.font: font, - NSAttributedString.Key.kern: tracking, - NSAttributedString.Key.paragraphStyle: paragraphStyle, - ] - - if let fillColor = fillColor { - attributes[NSAttributedString.Key.foregroundColor] = fillColor - } - - let attrString = NSAttributedString(string: text, attributes: attributes) - attributedString = attrString - - if fillColor != nil { - let setter = CTFramesetterCreateWithAttributedString(attrString as CFAttributedString) - fillFrameSetter = setter - } else { - fillFrameSetter = nil - } - - if let strokeColor = strokeColor { - attributes[NSAttributedString.Key.foregroundColor] = nil - attributes[NSAttributedString.Key.strokeWidth] = strokeWidth - attributes[NSAttributedString.Key.strokeColor] = strokeColor - let strokeAttributedString = NSAttributedString(string: text, attributes: attributes) - strokeFrameSetter = CTFramesetterCreateWithAttributedString(strokeAttributedString as CFAttributedString) - } else { - strokeFrameSetter = nil - strokeWidth = 0 - } - - guard let setter = fillFrameSetter ?? strokeFrameSetter else { - return - } - - // Calculate drawing size and anchor offset - let textAnchor: CGPoint - if let preferredSize = preferredSize { - drawingRect = CGRect(origin: .zero, size: preferredSize) - drawingRect.size.height += (ascent - capHeight) - drawingRect.size.height += descent - textAnchor = CGPoint(x: 0, y: ascent - capHeight) - } else { - let size = CTFramesetterSuggestFrameSizeWithConstraints( - setter, - CFRange(location: 0, length: attrString.length), - nil, - CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude), - nil) - switch alignment { - case .left: - textAnchor = CGPoint(x: 0, y: ascent) - case .right: - textAnchor = CGPoint(x: size.width, y: ascent) - case .center: - textAnchor = CGPoint(x: size.width * 0.5, y: ascent) - default: - textAnchor = .zero - } - drawingRect = CGRect( - x: 0, - y: 0, - width: ceil(size.width), - height: ceil(size.height)) - } - - // Now Calculate Anchor - drawingAnchor = CGPoint( - x: textAnchor.x.remap(fromLow: 0, fromHigh: drawingRect.size.width, toLow: 0, toHigh: 1), - y: textAnchor.y.remap(fromLow: 0, fromHigh: drawingRect.size.height, toLow: 0, toHigh: 1)) - - if fillFrameSetter != nil && strokeFrameSetter != nil { - drawingRect.size.width += strokeWidth - drawingRect.size.height += strokeWidth - } - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift deleted file mode 100644 index 45a74c9c..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Animation.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// Animation.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/7/19. -// - -import Foundation - -// MARK: - CoordinateSpace - -public enum CoordinateSpace: Int, Codable { - case type2d - case type3d -} - -// MARK: - Animation - -/** - The `Animation` model is the top level model object in Lottie. - - An `Animation` holds all of the animation data backing a Lottie Animation. - Codable, see JSON schema [here](https://github.com/airbnb/lottie-web/tree/master/docs/json). - */ -public final class Animation: Codable { - - // MARK: Lifecycle - - required public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Animation.CodingKeys.self) - version = try container.decode(String.self, forKey: .version) - type = try container.decodeIfPresent(CoordinateSpace.self, forKey: .type) ?? .type2d - startFrame = try container.decode(AnimationFrameTime.self, forKey: .startFrame) - endFrame = try container.decode(AnimationFrameTime.self, forKey: .endFrame) - framerate = try container.decode(Double.self, forKey: .framerate) - width = try container.decode(Int.self, forKey: .width) - height = try container.decode(Int.self, forKey: .height) - layers = try container.decode([LayerModel].self, ofFamily: LayerType.self, forKey: .layers) - glyphs = try container.decodeIfPresent([Glyph].self, forKey: .glyphs) - fonts = try container.decodeIfPresent(FontList.self, forKey: .fonts) - assetLibrary = try container.decodeIfPresent(AssetLibrary.self, forKey: .assetLibrary) - markers = try container.decodeIfPresent([Marker].self, forKey: .markers) - - if let markers = markers { - var markerMap: [String: Marker] = [:] - for marker in markers { - markerMap[marker.name] = marker - } - self.markerMap = markerMap - } else { - markerMap = nil - } - } - - // MARK: Public - - /// The start time of the composition in frameTime. - public let startFrame: AnimationFrameTime - - /// The end time of the composition in frameTime. - public let endFrame: AnimationFrameTime - - /// The frame rate of the composition. - public let framerate: Double - - /// Return all marker names, in order, or an empty list if none are specified - public var markerNames: [String] { - guard let markers = markers else { return [] } - return markers.map { $0.name } - } - - // MARK: Internal - - enum CodingKeys: String, CodingKey { - case version = "v" - case type = "ddd" - case startFrame = "ip" - case endFrame = "op" - case framerate = "fr" - case width = "w" - case height = "h" - case layers = "layers" - case glyphs = "chars" - case fonts = "fonts" - case assetLibrary = "assets" - case markers = "markers" - } - - /// The version of the JSON Schema. - let version: String - - /// The coordinate space of the composition. - let type: CoordinateSpace - - /// The height of the composition in points. - let width: Int - - /// The width of the composition in points. - let height: Int - - /// The list of animation layers - let layers: [LayerModel] - - /// The list of glyphs used for text rendering - let glyphs: [Glyph]? - - /// The list of fonts used for text rendering - let fonts: FontList? - - /// Asset Library - let assetLibrary: AssetLibrary? - - /// Markers - let markers: [Marker]? - let markerMap: [String: Marker]? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift deleted file mode 100644 index e6bca025..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/Asset.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Asset.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -public class Asset: Codable { - - // MARK: Lifecycle - - required public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Asset.CodingKeys.self) - if let id = try? container.decode(String.self, forKey: .id) { - self.id = id - } else { - id = String(try container.decode(Int.self, forKey: .id)) - } - } - - // MARK: Public - - /// The ID of the asset - public let id: String - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case id - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift deleted file mode 100644 index dd0d6d48..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/AssetLibrary.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// AssetLibrary.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -final class AssetLibrary: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - var containerForKeys = container - - var decodedAssets = [String : Asset]() - - var imageAssets = [String : ImageAsset]() - var precompAssets = [String : PrecompAsset]() - - while !container.isAtEnd { - let keyContainer = try containerForKeys.nestedContainer(keyedBy: PrecompAsset.CodingKeys.self) - if keyContainer.contains(.layers) { - let precompAsset = try container.decode(PrecompAsset.self) - decodedAssets[precompAsset.id] = precompAsset - precompAssets[precompAsset.id] = precompAsset - } else { - let imageAsset = try container.decode(ImageAsset.self) - decodedAssets[imageAsset.id] = imageAsset - imageAssets[imageAsset.id] = imageAsset - } - } - assets = decodedAssets - self.precompAssets = precompAssets - self.imageAssets = imageAssets - } - - // MARK: Internal - - /// The Assets - let assets: [String: Asset] - - let imageAssets: [String: ImageAsset] - let precompAssets: [String: PrecompAsset] - - func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(contentsOf: Array(assets.values)) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift deleted file mode 100644 index 284cfbf0..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/ImageAsset.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// ImageAsset.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -public final class ImageAsset: Asset { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: ImageAsset.CodingKeys.self) - name = try container.decode(String.self, forKey: .name) - directory = try container.decode(String.self, forKey: .directory) - width = try container.decode(Double.self, forKey: .width) - height = try container.decode(Double.self, forKey: .height) - try super.init(from: decoder) - } - - // MARK: Public - - /// Image name - public let name: String - - /// Image Directory - public let directory: String - - /// Image Size - public let width: Double - - public let height: Double - - override public func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(name, forKey: .name) - try container.encode(directory, forKey: .directory) - try container.encode(width, forKey: .width) - try container.encode(height, forKey: .height) - } - - // MARK: Internal - - enum CodingKeys: String, CodingKey { - case name = "p" - case directory = "u" - case width = "w" - case height = "h" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift deleted file mode 100644 index fd76fe93..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Assets/PrecompAsset.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// PrecompAsset.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -final class PrecompAsset: Asset { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: PrecompAsset.CodingKeys.self) - layers = try container.decode([LayerModel].self, ofFamily: LayerType.self, forKey: .layers) - try super.init(from: decoder) - } - - // MARK: Internal - - enum CodingKeys: String, CodingKey { - case layers - } - - /// Layers of the precomp - let layers: [LayerModel] - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(layers, forKey: .layers) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift deleted file mode 100644 index d1747033..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/Bundle.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit -#endif - -extension Bundle { - func getAnimationData(_ name: String, subdirectory: String? = nil) throws -> Data? { - // Check for files in the bundle at the given path - if let url = url(forResource: name, withExtension: "json", subdirectory: subdirectory) { - return try Data(contentsOf: url) - } - - // Check for data assets (not available on macOS) - #if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) - let assetKey = subdirectory != nil ? "\(subdirectory ?? "")/\(name)" : name - return NSDataAsset(name: assetKey, bundle: self)?.data - #else - return nil - #endif - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift deleted file mode 100644 index c0d8d755..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +++ /dev/null @@ -1,44 +0,0 @@ -// From: https://medium.com/@kewindannerfjordremeczki/swift-4-0-decodable-heterogeneous-collections-ecc0e6b468cf - -import Foundation - -// MARK: - ClassFamily - -/// To support a new class family, create an enum that conforms to this protocol and contains the different types. -protocol ClassFamily: Decodable { - /// The discriminator key. - static var discriminator: Discriminator { get } - - /// Returns the class type of the object corresponding to the value. - func getType() -> AnyObject.Type -} - -// MARK: - Discriminator - -/// Discriminator key enum used to retrieve discriminator fields in JSON payloads. -enum Discriminator: String, CodingKey { - case type = "ty" -} - -extension KeyedDecodingContainer { - - /// Decode a heterogeneous list of objects for a given family. - /// - Parameters: - /// - heterogeneousType: The decodable type of the list. - /// - family: The ClassFamily enum for the type family. - /// - key: The CodingKey to look up the list in the current container. - /// - Returns: The resulting list of heterogeneousType elements. - func decode(_: [T].Type, ofFamily family: U.Type, forKey key: K) throws -> [T] { - var container = try nestedUnkeyedContainer(forKey: key) - var list = [T]() - var tmpContainer = container - while !container.isAtEnd { - let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self) - let family: U = try typeContainer.decode(U.self, forKey: U.discriminator) - if let type = family.getType() as? T.Type { - list.append(try tmpContainer.decode(type)) - } - } - return list - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift deleted file mode 100644 index c704f724..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/Keyframe.swift +++ /dev/null @@ -1,145 +0,0 @@ -// -// Keyframe.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/7/19. -// - -import CoreGraphics -import Foundation - -// MARK: - Keyframe - -/** - Keyframe represents a point in time and is the container for datatypes. - Note: This is a parent class and should not be used directly. - */ -final class Keyframe { - - // MARK: Lifecycle - - /// Initialize a value-only keyframe with no time data. - init( - _ value: T, - spatialInTangent: Vector3D? = nil, - spatialOutTangent: Vector3D? = nil) - { - self.value = value - time = 0 - isHold = true - inTangent = nil - outTangent = nil - self.spatialInTangent = spatialInTangent - self.spatialOutTangent = spatialOutTangent - } - - /// Initialize a keyframe - init( - value: T, - time: Double, - isHold: Bool, - inTangent: Vector2D?, - outTangent: Vector2D?, - spatialInTangent: Vector3D? = nil, - spatialOutTangent: Vector3D? = nil) - { - self.value = value - self.time = CGFloat(time) - self.isHold = isHold - self.outTangent = outTangent - self.inTangent = inTangent - self.spatialInTangent = spatialInTangent - self.spatialOutTangent = spatialOutTangent - } - - // MARK: Internal - - /// The value of the keyframe - let value: T - /// The time in frames of the keyframe. - let time: CGFloat - /// A hold keyframe freezes interpolation until the next keyframe that is not a hold. - let isHold: Bool - /// The in tangent for the time interpolation curve. - let inTangent: Vector2D? - /// The out tangent for the time interpolation curve. - let outTangent: Vector2D? - - /// The spacial in tangent of the vector. - let spatialInTangent: Vector3D? - /// The spacial out tangent of the vector. - let spatialOutTangent: Vector3D? -} - -// MARK: - KeyframeData - -/** - A generic class used to parse and remap keyframe json. - - Keyframe json has a couple of different variations and formats depending on the - type of keyframea and also the version of the JSON. By parsing the raw data - we can reconfigure it into a constant format. - */ -final class KeyframeData: Codable { - - // MARK: Lifecycle - - init( - startValue: T?, - endValue: T?, - time: Double?, - hold: Int?, - inTangent: Vector2D?, - outTangent: Vector2D?, - spatialInTangent: Vector3D?, - spatialOutTangent: Vector3D?) - { - self.startValue = startValue - self.endValue = endValue - self.time = time - self.hold = hold - self.inTangent = inTangent - self.outTangent = outTangent - self.spatialInTangent = spatialInTangent - self.spatialOutTangent = spatialOutTangent - } - - // MARK: Internal - - enum CodingKeys: String, CodingKey { - case startValue = "s" - case endValue = "e" - case time = "t" - case hold = "h" - case inTangent = "i" - case outTangent = "o" - case spatialInTangent = "ti" - case spatialOutTangent = "to" - } - - /// The start value of the keyframe - let startValue: T? - /// The End value of the keyframe. Note: Newer versions animation json do not have this field. - let endValue: T? - /// The time in frames of the keyframe. - let time: Double? - /// A hold keyframe freezes interpolation until the next keyframe that is not a hold. - let hold: Int? - - /// The in tangent for the time interpolation curve. - let inTangent: Vector2D? - /// The out tangent for the time interpolation curve. - let outTangent: Vector2D? - - /// The spacial in tangent of the vector. - let spatialInTangent: Vector3D? - /// The spacial out tangent of the vector. - let spatialOutTangent: Vector3D? - - var isHold: Bool { - if let hold = hold { - return hold > 0 - } - return false - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift deleted file mode 100644 index cb0b6189..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Keyframes/KeyframeGroup.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// KeyframeGroup.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/14/19. -// - -import Foundation - -/** - Used for coding/decoding a group of Keyframes by type. - - Keyframe data is wrapped in a dictionary { "k" : KeyframeData }. - The keyframe data can either be an array of keyframes or, if no animation is present, the raw value. - This helper object is needed to properly decode the json. - */ - -final class KeyframeGroup: Codable where T: Codable, T: Interpolatable { - - // MARK: Lifecycle - - init(keyframes: ContiguousArray>) { - self.keyframes = keyframes - } - - init(_ value: T) { - keyframes = [Keyframe(value)] - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: KeyframeWrapperKey.self) - - if let keyframeData: T = try? container.decode(T.self, forKey: .keyframeData) { - /// Try to decode raw value; No keyframe data. - keyframes = [Keyframe(keyframeData)] - } else { - /** - Decode and array of keyframes. - - Body Movin and Lottie deal with keyframes in different ways. - - A keyframe object in Body movin defines a span of time with a START - and an END, from the current keyframe time to the next keyframe time. - - A keyframe object in Lottie defines a singular point in time/space. - This point has an in-tangent and an out-tangent. - - To properly decode this we must iterate through keyframes while holding - reference to the previous keyframe. - */ - - var keyframesContainer = try container.nestedUnkeyedContainer(forKey: .keyframeData) - var keyframes = ContiguousArray>() - var previousKeyframeData: KeyframeData? - while !keyframesContainer.isAtEnd { - // Ensure that Time and Value are present. - - let keyframeData = try keyframesContainer.decode(KeyframeData.self) - - guard - let value: T = keyframeData.startValue ?? previousKeyframeData?.endValue, - let time = keyframeData.time else - { - /// Missing keyframe data. JSON must be corrupt. - throw DecodingError.dataCorruptedError( - forKey: KeyframeWrapperKey.keyframeData, - in: container, - debugDescription: "Missing keyframe data.") - } - - keyframes.append(Keyframe( - value: value, - time: time, - isHold: keyframeData.isHold, - inTangent: previousKeyframeData?.inTangent, - outTangent: keyframeData.outTangent, - spatialInTangent: previousKeyframeData?.spatialInTangent, - spatialOutTangent: keyframeData.spatialOutTangent)) - previousKeyframeData = keyframeData - } - self.keyframes = keyframes - } - } - - // MARK: Internal - - let keyframes: ContiguousArray> - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: KeyframeWrapperKey.self) - - if keyframes.count == 1 { - let keyframe = keyframes[0] - try container.encode(keyframe.value, forKey: .keyframeData) - } else { - var keyframeContainer = container.nestedUnkeyedContainer(forKey: .keyframeData) - - for i in 1..( - startValue: keyframe.value, - endValue: nextKeyframe.value, - time: Double(keyframe.time), - hold: keyframe.isHold ? 1 : nil, - inTangent: nextKeyframe.inTangent, - outTangent: keyframe.outTangent, - spatialInTangent: nil, - spatialOutTangent: nil) - try keyframeContainer.encode(keyframeData) - } - } - } - - // MARK: Private - - private enum KeyframeWrapperKey: String, CodingKey { - case keyframeData = "k" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift deleted file mode 100644 index 92cdcfae..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ImageLayerModel.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ImageLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// A layer that holds an image. -final class ImageLayerModel: LayerModel { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: ImageLayerModel.CodingKeys.self) - referenceID = try container.decode(String.self, forKey: .referenceID) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The reference ID of the image. - let referenceID: String - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(referenceID, forKey: .referenceID) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case referenceID = "refId" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift deleted file mode 100644 index 506e6a11..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/LayerModel.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// Layer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/7/19. -// - -import Foundation - -// MARK: - LayerType + ClassFamily - -/// Used for mapping a heterogeneous list to classes for parsing. -extension LayerType: ClassFamily { - static var discriminator: Discriminator = .type - - func getType() -> AnyObject.Type { - switch self { - case .precomp: - return PreCompLayerModel.self - case .solid: - return SolidLayerModel.self - case .image: - return ImageLayerModel.self - case .null: - return LayerModel.self - case .shape: - return ShapeLayerModel.self - case .text: - return TextLayerModel.self - } - } -} - -// MARK: - LayerType - -public enum LayerType: Int, Codable { - case precomp - case solid - case image - case null - case shape - case text - - public init(from decoder: Decoder) throws { - self = try LayerType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .null - } -} - -// MARK: - MatteType - -public enum MatteType: Int, Codable { - case none - case add - case invert - case unknown -} - -// MARK: - BlendMode - -public enum BlendMode: Int, Codable { - case normal - case multiply - case screen - case overlay - case darken - case lighten - case colorDodge - case colorBurn - case hardLight - case softLight - case difference - case exclusion - case hue - case saturation - case color - case luminosity -} - -// MARK: - LayerModel - -/** - A base top container for shapes, images, and other view objects. - */ -class LayerModel: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: LayerModel.CodingKeys.self) - name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer" - index = try container.decodeIfPresent(Int.self, forKey: .index) ?? 0 - type = try container.decode(LayerType.self, forKey: .type) - coordinateSpace = try container.decodeIfPresent(CoordinateSpace.self, forKey: .coordinateSpace) ?? .type2d - inFrame = try container.decode(Double.self, forKey: .inFrame) - outFrame = try container.decode(Double.self, forKey: .outFrame) - startTime = try container.decode(Double.self, forKey: .startTime) - transform = try container.decode(Transform.self, forKey: .transform) - parent = try container.decodeIfPresent(Int.self, forKey: .parent) - blendMode = try container.decodeIfPresent(BlendMode.self, forKey: .blendMode) ?? .normal - masks = try container.decodeIfPresent([Mask].self, forKey: .masks) - timeStretch = try container.decodeIfPresent(Double.self, forKey: .timeStretch) ?? 1 - matte = try container.decodeIfPresent(MatteType.self, forKey: .matte) - hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false - } - - // MARK: Internal - - /// The readable name of the layer - let name: String - - /// The index of the layer - let index: Int - - /// The type of the layer. - let type: LayerType - - /// The coordinate space - let coordinateSpace: CoordinateSpace - - /// The in time of the layer in frames. - let inFrame: Double - /// The out time of the layer in frames. - let outFrame: Double - - /// The start time of the layer in frames. - let startTime: Double - - /// The transform of the layer - let transform: Transform - - /// The index of the parent layer, if applicable. - let parent: Int? - - /// The blending mode for the layer - let blendMode: BlendMode - - /// An array of masks for the layer. - let masks: [Mask]? - - /// A number that stretches time by a multiplier - let timeStretch: Double - - /// The type of matte if any. - let matte: MatteType? - - let hidden: Bool - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case name = "nm" - case index = "ind" - case type = "ty" - case coordinateSpace = "ddd" - case inFrame = "ip" - case outFrame = "op" - case startTime = "st" - case transform = "ks" - case parent = "parent" - case blendMode = "bm" - case masks = "masksProperties" - case timeStretch = "sr" - case matte = "tt" - case hidden = "hd" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift deleted file mode 100644 index 9eed00c0..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/PreCompLayerModel.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// PreCompLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// A layer that holds another animation composition. -final class PreCompLayerModel: LayerModel { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: PreCompLayerModel.CodingKeys.self) - referenceID = try container.decode(String.self, forKey: .referenceID) - timeRemapping = try container.decodeIfPresent(KeyframeGroup.self, forKey: .timeRemapping) - width = try container.decode(Double.self, forKey: .width) - height = try container.decode(Double.self, forKey: .height) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The reference ID of the precomp. - let referenceID: String - - /// A value that remaps time over time. - let timeRemapping: KeyframeGroup? - - /// Precomp Width - let width: Double - - /// Precomp Height - let height: Double - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(referenceID, forKey: .referenceID) - try container.encode(timeRemapping, forKey: .timeRemapping) - try container.encode(width, forKey: .width) - try container.encode(height, forKey: .height) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case referenceID = "refId" - case timeRemapping = "tm" - case width = "w" - case height = "h" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift deleted file mode 100644 index 04d9c648..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/ShapeLayerModel.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ShapeLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// A layer that holds vector shape objects. -final class ShapeLayerModel: LayerModel { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: ShapeLayerModel.CodingKeys.self) - items = try container.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .items) - try super.init(from: decoder) - } - - // MARK: Internal - - /// A list of shape items. - let items: [ShapeItem] - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(items, forKey: .items) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case items = "shapes" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift deleted file mode 100644 index b6d3669a..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/SolidLayerModel.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// SolidLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// A layer that holds a solid color. -final class SolidLayerModel: LayerModel { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: SolidLayerModel.CodingKeys.self) - colorHex = try container.decode(String.self, forKey: .colorHex) - width = try container.decode(Double.self, forKey: .width) - height = try container.decode(Double.self, forKey: .height) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The color of the solid in Hex // Change to value provider. - let colorHex: String - - /// The Width of the color layer - let width: Double - - /// The height of the color layer - let height: Double - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(colorHex, forKey: .colorHex) - try container.encode(width, forKey: .width) - try container.encode(height, forKey: .height) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case colorHex = "sc" - case width = "sw" - case height = "sh" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift deleted file mode 100644 index 1fac9bfe..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Layers/TextLayerModel.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// TextLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// A layer that holds text. -final class TextLayerModel: LayerModel { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: TextLayerModel.CodingKeys.self) - let textContainer = try container.nestedContainer(keyedBy: TextCodingKeys.self, forKey: .textGroup) - text = try textContainer.decode(KeyframeGroup.self, forKey: .text) - animators = try textContainer.decode([TextAnimator].self, forKey: .animators) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The text for the layer - let text: KeyframeGroup - - /// Text animators - let animators: [TextAnimator] - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - var textContainer = container.nestedContainer(keyedBy: TextCodingKeys.self, forKey: .textGroup) - try textContainer.encode(text, forKey: .text) - try textContainer.encode(animators, forKey: .animators) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case textGroup = "t" - } - - private enum TextCodingKeys: String, CodingKey { - case text = "d" - case animators = "a" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift deleted file mode 100644 index 07299fd6..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/DashPattern.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// DashPattern.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/22/19. -// - -import Foundation - -// MARK: - DashElementType - -enum DashElementType: String, Codable { - case offset = "o" - case dash = "d" - case gap = "g" -} - -// MARK: - DashElement - -final class DashElement: Codable { - - enum CodingKeys: String, CodingKey { - case type = "n" - case value = "v" - } - - let type: DashElementType - let value: KeyframeGroup -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift deleted file mode 100644 index aaf15997..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Marker.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// Marker.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -/// A time marker -final class Marker: Codable { - - enum CodingKeys: String, CodingKey { - case name = "cm" - case frameTime = "tm" - } - - /// The Marker Name - let name: String - - /// The Frame time of the marker - let frameTime: AnimationFrameTime -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift deleted file mode 100644 index 75b0af39..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Mask.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// Mask.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - MaskMode - -enum MaskMode: String, Codable { - case add = "a" - case subtract = "s" - case intersect = "i" - case lighten = "l" - case darken = "d" - case difference = "f" - case none = "n" -} - -// MARK: - Mask - -final class Mask: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Mask.CodingKeys.self) - mode = try container.decodeIfPresent(MaskMode.self, forKey: .mode) ?? .add - opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) - shape = try container.decode(KeyframeGroup.self, forKey: .shape) - inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false - expansion = try container.decodeIfPresent(KeyframeGroup.self, forKey: .expansion) ?? KeyframeGroup(Vector1D(0)) - } - - // MARK: Internal - - enum CodingKeys: String, CodingKey { - case mode = "mode" - case opacity = "o" - case inverted = "inv" - case shape = "pt" - case expansion = "x" - } - - let mode: MaskMode - - let opacity: KeyframeGroup - - let shape: KeyframeGroup - - let inverted: Bool - - let expansion: KeyframeGroup -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift deleted file mode 100644 index 46322d64..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Objects/Transform.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// Transform.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/7/19. -// - -import Foundation - -/// The animatable transform for a layer. Controls position, rotation, scale, and opacity. -final class Transform: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - /** - This manual override of decode is required because we want to throw an error - in the case that there is not position data. - */ - let container = try decoder.container(keyedBy: Transform.CodingKeys.self) - - // AnchorPoint - anchorPoint = try container - .decodeIfPresent(KeyframeGroup.self, forKey: .anchorPoint) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) - - // Position - if container.contains(.positionX), container.contains(.positionY) { - // Position dimensions are split into two keyframe groups - positionX = try container.decode(KeyframeGroup.self, forKey: .positionX) - positionY = try container.decode(KeyframeGroup.self, forKey: .positionY) - position = nil - } else if let positionKeyframes = try? container.decode(KeyframeGroup.self, forKey: .position) { - // Position dimensions are a single keyframe group. - position = positionKeyframes - positionX = nil - positionY = nil - } else if - let positionContainer = try? container.nestedContainer(keyedBy: PositionCodingKeys.self, forKey: .position), - let positionX = try? positionContainer.decode(KeyframeGroup.self, forKey: .positionX), - let positionY = try? positionContainer.decode(KeyframeGroup.self, forKey: .positionY) - { - /// Position keyframes are split and nested. - self.positionX = positionX - self.positionY = positionY - position = nil - } else { - /// Default value. - position = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) - positionX = nil - positionY = nil - } - - // Scale - scale = try container - .decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) - - // Rotation - if let rotationZ = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotationZ) { - rotation = rotationZ - } else { - rotation = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) - } - rotationZ = nil - - // Opacity - opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) - } - - // MARK: Internal - - enum CodingKeys: String, CodingKey { - case anchorPoint = "a" - case position = "p" - case positionX = "px" - case positionY = "py" - case scale = "s" - case rotation = "r" - case rotationZ = "rz" - case opacity = "o" - } - - enum PositionCodingKeys: String, CodingKey { - case split = "s" - case positionX = "x" - case positionY = "y" - } - - /// The anchor point of the transform. - let anchorPoint: KeyframeGroup - - /// The position of the transform. This is nil if the position data was split. - let position: KeyframeGroup? - - /// The positionX of the transform. This is nil if the position property is set. - let positionX: KeyframeGroup? - - /// The positionY of the transform. This is nil if the position property is set. - let positionY: KeyframeGroup? - - /// The scale of the transform - let scale: KeyframeGroup - - /// The rotation of the transform. Note: This is single dimensional rotation. - let rotation: KeyframeGroup - - /// The opacity of the transform. - let opacity: KeyframeGroup - - /// Should always be nil. - let rotationZ: KeyframeGroup? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift deleted file mode 100644 index b207167e..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Ellipse.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// EllipseItem.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - PathDirection - -enum PathDirection: Int, Codable { - case clockwise = 1 - case userSetClockwise = 2 - case counterClockwise = 3 -} - -// MARK: - Ellipse - -/// An item that define an ellipse shape -final class Ellipse: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Ellipse.CodingKeys.self) - direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise - position = try container.decode(KeyframeGroup.self, forKey: .position) - size = try container.decode(KeyframeGroup.self, forKey: .size) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The direction of the ellipse. - let direction: PathDirection - - /// The position of the ellipse - let position: KeyframeGroup - - /// The size of the ellipse - let size: KeyframeGroup - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(direction, forKey: .direction) - try container.encode(position, forKey: .position) - try container.encode(size, forKey: .size) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case direction = "d" - case position = "p" - case size = "s" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift deleted file mode 100644 index c3d558f2..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/FillI.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// FillShape.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - FillRule - -enum FillRule: Int, Codable { - case none - case nonZeroWinding - case evenOdd -} - -// MARK: - Fill - -/// An item that defines a fill render -final class Fill: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Fill.CodingKeys.self) - opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) - color = try container.decode(KeyframeGroup.self, forKey: .color) - fillRule = try container.decodeIfPresent(FillRule.self, forKey: .fillRule) ?? .nonZeroWinding - try super.init(from: decoder) - } - - // MARK: Internal - - /// The opacity of the fill - let opacity: KeyframeGroup - - /// The color keyframes for the fill - let color: KeyframeGroup - - let fillRule: FillRule - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(opacity, forKey: .opacity) - try container.encode(color, forKey: .color) - try container.encode(fillRule, forKey: .fillRule) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case opacity = "o" - case color = "c" - case fillRule = "r" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift deleted file mode 100644 index 9809c454..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientFill.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// GradientFill.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - GradientType - -enum GradientType: Int, Codable { - case none - case linear - case radial -} - -// MARK: - GradientFill - -/// An item that define a gradient fill -final class GradientFill: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: GradientFill.CodingKeys.self) - opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) - startPoint = try container.decode(KeyframeGroup.self, forKey: .startPoint) - endPoint = try container.decode(KeyframeGroup.self, forKey: .endPoint) - gradientType = try container.decode(GradientType.self, forKey: .gradientType) - highlightLength = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightLength) - highlightAngle = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightAngle) - let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) - colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors) - numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The opacity of the fill - let opacity: KeyframeGroup - - /// The start of the gradient - let startPoint: KeyframeGroup - - /// The end of the gradient - let endPoint: KeyframeGroup - - /// The type of gradient - let gradientType: GradientType - - /// Gradient Highlight Length. Only if type is Radial - let highlightLength: KeyframeGroup? - - /// Highlight Angle. Only if type is Radial - let highlightAngle: KeyframeGroup? - - /// The number of color points in the gradient - let numberOfColors: Int - - /// The Colors of the gradient. - let colors: KeyframeGroup<[Double]> - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(opacity, forKey: .opacity) - try container.encode(startPoint, forKey: .startPoint) - try container.encode(endPoint, forKey: .endPoint) - try container.encode(gradientType, forKey: .gradientType) - try container.encodeIfPresent(highlightLength, forKey: .highlightLength) - try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle) - var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) - try colorsContainer.encode(numberOfColors, forKey: .numberOfColors) - try colorsContainer.encode(colors, forKey: .colors) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case opacity = "o" - case startPoint = "s" - case endPoint = "e" - case gradientType = "t" - case highlightLength = "h" - case highlightAngle = "a" - case colors = "g" - } - - private enum GradientDataKeys: String, CodingKey { - case numberOfColors = "p" - case colors = "k" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift deleted file mode 100644 index eb0d6161..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/GradientStroke.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// GradientStroke.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - LineCap - -enum LineCap: Int, Codable { - case none - case butt - case round - case square -} - -// MARK: - LineJoin - -enum LineJoin: Int, Codable { - case none - case miter - case round - case bevel -} - -// MARK: - GradientStroke - -/// An item that define an ellipse shape -final class GradientStroke: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: GradientStroke.CodingKeys.self) - opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) - startPoint = try container.decode(KeyframeGroup.self, forKey: .startPoint) - endPoint = try container.decode(KeyframeGroup.self, forKey: .endPoint) - gradientType = try container.decode(GradientType.self, forKey: .gradientType) - highlightLength = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightLength) - highlightAngle = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightAngle) - width = try container.decode(KeyframeGroup.self, forKey: .width) - lineCap = try container.decodeIfPresent(LineCap.self, forKey: .lineCap) ?? .round - lineJoin = try container.decodeIfPresent(LineJoin.self, forKey: .lineJoin) ?? .round - miterLimit = try container.decodeIfPresent(Double.self, forKey: .miterLimit) ?? 4 - // TODO Decode Color Objects instead of array. - let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) - colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors) - numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors) - dashPattern = try container.decodeIfPresent([DashElement].self, forKey: .dashPattern) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The opacity of the fill - let opacity: KeyframeGroup - - /// The start of the gradient - let startPoint: KeyframeGroup - - /// The end of the gradient - let endPoint: KeyframeGroup - - /// The type of gradient - let gradientType: GradientType - - /// Gradient Highlight Length. Only if type is Radial - let highlightLength: KeyframeGroup? - - /// Highlight Angle. Only if type is Radial - let highlightAngle: KeyframeGroup? - - /// The number of color points in the gradient - let numberOfColors: Int - - /// The Colors of the gradient. - let colors: KeyframeGroup<[Double]> - - /// The width of the stroke - let width: KeyframeGroup - - /// Line Cap - let lineCap: LineCap - - /// Line Join - let lineJoin: LineJoin - - /// Miter Limit - let miterLimit: Double - - /// The dash pattern of the stroke - let dashPattern: [DashElement]? - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(opacity, forKey: .opacity) - try container.encode(startPoint, forKey: .startPoint) - try container.encode(endPoint, forKey: .endPoint) - try container.encode(gradientType, forKey: .gradientType) - try container.encodeIfPresent(highlightLength, forKey: .highlightLength) - try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle) - try container.encode(width, forKey: .width) - try container.encode(lineCap, forKey: .lineCap) - try container.encode(lineJoin, forKey: .lineJoin) - try container.encode(miterLimit, forKey: .miterLimit) - var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) - try colorsContainer.encode(numberOfColors, forKey: .numberOfColors) - try colorsContainer.encode(colors, forKey: .colors) - try container.encodeIfPresent(dashPattern, forKey: .dashPattern) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case opacity = "o" - case startPoint = "s" - case endPoint = "e" - case gradientType = "t" - case highlightLength = "h" - case highlightAngle = "a" - case colors = "g" - case width = "w" - case lineCap = "lc" - case lineJoin = "lj" - case miterLimit = "ml" - case dashPattern = "d" - } - - private enum GradientDataKeys: String, CodingKey { - case numberOfColors = "p" - case colors = "k" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift deleted file mode 100644 index 0055a005..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Group.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// GroupItem.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// An item that define an ellipse shape -final class Group: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Group.CodingKeys.self) - items = try container.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .items) - try super.init(from: decoder) - } - - // MARK: Internal - - /// A list of shape items. - let items: [ShapeItem] - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(items, forKey: .items) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case items = "it" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift deleted file mode 100644 index a0b7dbaf..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Merge.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// Merge.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - MergeMode - -enum MergeMode: Int, Codable { - case none - case merge - case add - case subtract - case intersect - case exclude -} - -// MARK: - Merge - -/// An item that define an ellipse shape -final class Merge: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Merge.CodingKeys.self) - mode = try container.decode(MergeMode.self, forKey: .mode) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The mode of the merge path - let mode: MergeMode - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(mode, forKey: .mode) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case mode = "mm" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift deleted file mode 100644 index 548c09f5..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Rectangle.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Rectangle.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// An item that define an ellipse shape -final class Rectangle: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Rectangle.CodingKeys.self) - direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise - position = try container.decode(KeyframeGroup.self, forKey: .position) - size = try container.decode(KeyframeGroup.self, forKey: .size) - cornerRadius = try container.decode(KeyframeGroup.self, forKey: .cornerRadius) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The direction of the rect. - let direction: PathDirection - - /// The position - let position: KeyframeGroup - - /// The size - let size: KeyframeGroup - - /// The Corner radius of the rectangle - let cornerRadius: KeyframeGroup - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(direction, forKey: .direction) - try container.encode(position, forKey: .position) - try container.encode(size, forKey: .size) - try container.encode(cornerRadius, forKey: .cornerRadius) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case direction = "d" - case position = "p" - case size = "s" - case cornerRadius = "r" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift deleted file mode 100644 index a88a1e7f..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Repeater.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// Repeater.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// An item that define an ellipse shape -final class Repeater: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Repeater.CodingKeys.self) - copies = try container.decodeIfPresent(KeyframeGroup.self, forKey: .copies) ?? KeyframeGroup(Vector1D(0)) - offset = try container.decodeIfPresent(KeyframeGroup.self, forKey: .offset) ?? KeyframeGroup(Vector1D(0)) - let transformContainer = try container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform) - startOpacity = try transformContainer - .decodeIfPresent(KeyframeGroup.self, forKey: .startOpacity) ?? KeyframeGroup(Vector1D(100)) - endOpacity = try transformContainer - .decodeIfPresent(KeyframeGroup.self, forKey: .endOpacity) ?? KeyframeGroup(Vector1D(100)) - rotation = try transformContainer - .decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) - position = try transformContainer - .decodeIfPresent(KeyframeGroup.self, forKey: .position) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) - anchorPoint = try transformContainer - .decodeIfPresent(KeyframeGroup.self, forKey: .anchorPoint) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) - scale = try transformContainer - .decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The number of copies to repeat - let copies: KeyframeGroup - - /// The offset of each copy - let offset: KeyframeGroup - - /// Start Opacity - let startOpacity: KeyframeGroup - - /// End opacity - let endOpacity: KeyframeGroup - - /// The rotation - let rotation: KeyframeGroup - - /// Anchor Point - let anchorPoint: KeyframeGroup - - /// Position - let position: KeyframeGroup - - /// Scale - let scale: KeyframeGroup - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(copies, forKey: .copies) - try container.encode(offset, forKey: .offset) - var transformContainer = container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform) - try transformContainer.encode(startOpacity, forKey: .startOpacity) - try transformContainer.encode(endOpacity, forKey: .endOpacity) - try transformContainer.encode(rotation, forKey: .rotation) - try transformContainer.encode(position, forKey: .position) - try transformContainer.encode(anchorPoint, forKey: .anchorPoint) - try transformContainer.encode(scale, forKey: .scale) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case copies = "c" - case offset = "o" - case transform = "tr" - } - - private enum TransformKeys: String, CodingKey { - case rotation = "r" - case startOpacity = "so" - case endOpacity = "eo" - case anchorPoint = "a" - case position = "p" - case scale = "s" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift deleted file mode 100644 index 6e3ae7ae..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Shape.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// VectorShape.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// An item that define an ellipse shape -final class Shape: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Shape.CodingKeys.self) - path = try container.decode(KeyframeGroup.self, forKey: .path) - direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The Path - let path: KeyframeGroup - - let direction: PathDirection? - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(path, forKey: .path) - try container.encodeIfPresent(direction, forKey: .direction) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case path = "ks" - case direction = "d" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift deleted file mode 100644 index 7084d499..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeItem.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// ShapeItem.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - ShapeType + ClassFamily - -/// Used for mapping a heterogeneous list to classes for parsing. -extension ShapeType: ClassFamily { - - static var discriminator: Discriminator = .type - - func getType() -> AnyObject.Type { - switch self { - case .ellipse: - return Ellipse.self - case .fill: - return Fill.self - case .gradientFill: - return GradientFill.self - case .group: - return Group.self - case .gradientStroke: - return GradientStroke.self - case .merge: - return Merge.self - case .rectangle: - return Rectangle.self - case .repeater: - return Repeater.self - case .shape: - return Shape.self - case .star: - return Star.self - case .stroke: - return Stroke.self - case .trim: - return Trim.self - case .transform: - return ShapeTransform.self - default: - return ShapeItem.self - } - } -} - -// MARK: - ShapeType - -enum ShapeType: String, Codable { - case ellipse = "el" - case fill = "fl" - case gradientFill = "gf" - case group = "gr" - case gradientStroke = "gs" - case merge = "mm" - case rectangle = "rc" - case repeater = "rp" - case round = "rd" - case shape = "sh" - case star = "sr" - case stroke = "st" - case trim = "tm" - case transform = "tr" - case unknown - - public init(from decoder: Decoder) throws { - self = try ShapeType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown - } -} - -// MARK: - ShapeItem - -/// An item belonging to a Shape Layer -class ShapeItem: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: ShapeItem.CodingKeys.self) - name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer" - type = try container.decode(ShapeType.self, forKey: .type) - hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false - } - - // MARK: Internal - - /// The name of the shape - let name: String - - /// The type of shape - let type: ShapeType - - let hidden: Bool - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case name = "nm" - case type = "ty" - case hidden = "hd" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift deleted file mode 100644 index 3374644c..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/ShapeTransform.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// TransformItem.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// An item that define an ellipse shape -final class ShapeTransform: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: ShapeTransform.CodingKeys.self) - anchor = try container - .decodeIfPresent(KeyframeGroup.self, forKey: .anchor) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) - position = try container - .decodeIfPresent(KeyframeGroup.self, forKey: .position) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) - scale = try container - .decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) - rotation = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) - opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) - skew = try container.decodeIfPresent(KeyframeGroup.self, forKey: .skew) ?? KeyframeGroup(Vector1D(0)) - skewAxis = try container.decodeIfPresent(KeyframeGroup.self, forKey: .skewAxis) ?? KeyframeGroup(Vector1D(0)) - try super.init(from: decoder) - } - - // MARK: Internal - - /// Anchor Point - let anchor: KeyframeGroup - - /// Position - let position: KeyframeGroup - - /// Scale - let scale: KeyframeGroup - - /// Rotation - let rotation: KeyframeGroup - - /// opacity - let opacity: KeyframeGroup - - /// Skew - let skew: KeyframeGroup - - /// Skew Axis - let skewAxis: KeyframeGroup - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(anchor, forKey: .anchor) - try container.encode(position, forKey: .position) - try container.encode(scale, forKey: .scale) - try container.encode(rotation, forKey: .rotation) - try container.encode(opacity, forKey: .opacity) - try container.encode(skew, forKey: .skew) - try container.encode(skewAxis, forKey: .skewAxis) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case anchor = "a" - case position = "p" - case scale = "s" - case rotation = "r" - case opacity = "o" - case skew = "sk" - case skewAxis = "sa" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift deleted file mode 100644 index 63dbfd61..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Star.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// Star.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - StarType - -enum StarType: Int, Codable { - case none - case star - case polygon -} - -// MARK: - Star - -/// An item that define an ellipse shape -final class Star: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Star.CodingKeys.self) - direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise - position = try container.decode(KeyframeGroup.self, forKey: .position) - outerRadius = try container.decode(KeyframeGroup.self, forKey: .outerRadius) - outerRoundness = try container.decode(KeyframeGroup.self, forKey: .outerRoundness) - innerRadius = try container.decodeIfPresent(KeyframeGroup.self, forKey: .innerRadius) - innerRoundness = try container.decodeIfPresent(KeyframeGroup.self, forKey: .innerRoundness) - rotation = try container.decode(KeyframeGroup.self, forKey: .rotation) - points = try container.decode(KeyframeGroup.self, forKey: .points) - starType = try container.decode(StarType.self, forKey: .starType) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The direction of the star. - let direction: PathDirection - - /// The position of the star - let position: KeyframeGroup - - /// The outer radius of the star - let outerRadius: KeyframeGroup - - /// The outer roundness of the star - let outerRoundness: KeyframeGroup - - /// The outer radius of the star - let innerRadius: KeyframeGroup? - - /// The outer roundness of the star - let innerRoundness: KeyframeGroup? - - /// The rotation of the star - let rotation: KeyframeGroup - - /// The number of points on the star - let points: KeyframeGroup - - /// The type of star - let starType: StarType - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(direction, forKey: .direction) - try container.encode(position, forKey: .position) - try container.encode(outerRadius, forKey: .outerRadius) - try container.encode(outerRoundness, forKey: .outerRoundness) - try container.encode(innerRadius, forKey: .innerRadius) - try container.encode(innerRoundness, forKey: .innerRoundness) - try container.encode(rotation, forKey: .rotation) - try container.encode(points, forKey: .points) - try container.encode(starType, forKey: .starType) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case direction = "d" - case position = "p" - case outerRadius = "or" - case outerRoundness = "os" - case innerRadius = "ir" - case innerRoundness = "is" - case rotation = "r" - case points = "pt" - case starType = "sy" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift deleted file mode 100644 index c5c79c42..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Stroke.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// Stroke.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -/// An item that define an ellipse shape -final class Stroke: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Stroke.CodingKeys.self) - opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) - color = try container.decode(KeyframeGroup.self, forKey: .color) - width = try container.decode(KeyframeGroup.self, forKey: .width) - lineCap = try container.decodeIfPresent(LineCap.self, forKey: .lineCap) ?? .round - lineJoin = try container.decodeIfPresent(LineJoin.self, forKey: .lineJoin) ?? .round - miterLimit = try container.decodeIfPresent(Double.self, forKey: .miterLimit) ?? 4 - dashPattern = try container.decodeIfPresent([DashElement].self, forKey: .dashPattern) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The opacity of the stroke - let opacity: KeyframeGroup - - /// The Color of the stroke - let color: KeyframeGroup - - /// The width of the stroke - let width: KeyframeGroup - - /// Line Cap - let lineCap: LineCap - - /// Line Join - let lineJoin: LineJoin - - /// Miter Limit - let miterLimit: Double - - /// The dash pattern of the stroke - let dashPattern: [DashElement]? - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(opacity, forKey: .opacity) - try container.encode(color, forKey: .color) - try container.encode(width, forKey: .width) - try container.encode(lineCap, forKey: .lineCap) - try container.encode(lineJoin, forKey: .lineJoin) - try container.encode(miterLimit, forKey: .miterLimit) - try container.encodeIfPresent(dashPattern, forKey: .dashPattern) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case opacity = "o" - case color = "c" - case width = "w" - case lineCap = "lc" - case lineJoin = "lj" - case miterLimit = "ml" - case dashPattern = "d" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift deleted file mode 100644 index bbd9a7bb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/ShapeItems/Trim.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// Trim.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import Foundation - -// MARK: - TrimType - -enum TrimType: Int, Codable { - case simultaneously = 1 - case individually = 2 -} - -// MARK: - Trim - -/// An item that define an ellipse shape -final class Trim: ShapeItem { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Trim.CodingKeys.self) - start = try container.decode(KeyframeGroup.self, forKey: .start) - end = try container.decode(KeyframeGroup.self, forKey: .end) - offset = try container.decode(KeyframeGroup.self, forKey: .offset) - trimType = try container.decode(TrimType.self, forKey: .trimType) - try super.init(from: decoder) - } - - // MARK: Internal - - /// The start of the trim - let start: KeyframeGroup - - /// The end of the trim - let end: KeyframeGroup - - /// The offset of the trim - let offset: KeyframeGroup - - let trimType: TrimType - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(start, forKey: .start) - try container.encode(end, forKey: .end) - try container.encode(offset, forKey: .offset) - try container.encode(trimType, forKey: .trimType) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case start = "s" - case end = "e" - case offset = "o" - case trimType = "m" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift deleted file mode 100644 index 2b5b60f9..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Font.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// Font.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -// MARK: - Font - -final class Font: Codable { - - // MARK: Internal - - let name: String - let familyName: String - let style: String - let ascent: Double - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case name = "fName" - case familyName = "fFamily" - case style = "fStyle" - case ascent = "ascent" - } - -} - -// MARK: - FontList - -/// A list of fonts -final class FontList: Codable { - - enum CodingKeys: String, CodingKey { - case fonts = "list" - } - - let fonts: [Font] -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift deleted file mode 100644 index cf71fcc8..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/Glyph.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// Glyph.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -/// A model that holds a vector character -final class Glyph: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Glyph.CodingKeys.self) - character = try container.decode(String.self, forKey: .character) - fontSize = try container.decode(Double.self, forKey: .fontSize) - fontFamily = try container.decode(String.self, forKey: .fontFamily) - fontStyle = try container.decode(String.self, forKey: .fontStyle) - width = try container.decode(Double.self, forKey: .width) - if - container.contains(.shapeWrapper), - let shapeContainer = try? container.nestedContainer(keyedBy: ShapeKey.self, forKey: .shapeWrapper), - shapeContainer.contains(.shapes) - { - shapes = try shapeContainer.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .shapes) - } else { - shapes = [] - } - } - - // MARK: Internal - - /// The character - let character: String - - /// The font size of the character - let fontSize: Double - - /// The font family of the character - let fontFamily: String - - /// The Style of the character - let fontStyle: String - - /// The Width of the character - let width: Double - - /// The Shape Data of the Character - let shapes: [ShapeItem] - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(character, forKey: .character) - try container.encode(fontSize, forKey: .fontSize) - try container.encode(fontFamily, forKey: .fontFamily) - try container.encode(fontStyle, forKey: .fontStyle) - try container.encode(width, forKey: .width) - - var shapeContainer = container.nestedContainer(keyedBy: ShapeKey.self, forKey: .shapeWrapper) - try shapeContainer.encode(shapes, forKey: .shapes) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case character = "ch" - case fontSize = "size" - case fontFamily = "fFamily" - case fontStyle = "style" - case width = "w" - case shapeWrapper = "data" - } - - private enum ShapeKey: String, CodingKey { - case shapes - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift deleted file mode 100644 index 34801683..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextAnimator.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// TextAnimator.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -final class TextAnimator: Codable { - - // MARK: Lifecycle - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: TextAnimator.CodingKeys.self) - name = try container.decodeIfPresent(String.self, forKey: .name) ?? "" - let animatorContainer = try container.nestedContainer(keyedBy: TextAnimatorKeys.self, forKey: .textAnimator) - fillColor = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .fillColor) - strokeColor = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .strokeColor) - strokeWidth = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .strokeWidth) - tracking = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .tracking) - anchor = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .anchor) - position = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .position) - scale = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .scale) - skew = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .skew) - skewAxis = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .skewAxis) - rotation = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) - opacity = try animatorContainer.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) - - } - - // MARK: Internal - - let name: String - - /// Anchor - let anchor: KeyframeGroup? - - /// Position - let position: KeyframeGroup? - - /// Scale - let scale: KeyframeGroup? - - /// Skew - let skew: KeyframeGroup? - - /// Skew Axis - let skewAxis: KeyframeGroup? - - /// Rotation - let rotation: KeyframeGroup? - - /// Opacity - let opacity: KeyframeGroup? - - /// Stroke Color - let strokeColor: KeyframeGroup? - - /// Fill Color - let fillColor: KeyframeGroup? - - /// Stroke Width - let strokeWidth: KeyframeGroup? - - /// Tracking - let tracking: KeyframeGroup? - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - var animatorContainer = container.nestedContainer(keyedBy: TextAnimatorKeys.self, forKey: .textAnimator) - try animatorContainer.encodeIfPresent(fillColor, forKey: .fillColor) - try animatorContainer.encodeIfPresent(strokeColor, forKey: .strokeColor) - try animatorContainer.encodeIfPresent(strokeWidth, forKey: .strokeWidth) - try animatorContainer.encodeIfPresent(tracking, forKey: .tracking) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { -// case textSelector = "s" TODO - case textAnimator = "a" - case name = "nm" - } - - private enum TextSelectorKeys: String, CodingKey { - case start = "s" - case end = "e" - case offset = "o" - } - - private enum TextAnimatorKeys: String, CodingKey { - case fillColor = "fc" - case strokeColor = "sc" - case strokeWidth = "sw" - case tracking = "t" - case anchor = "a" - case position = "p" - case scale = "s" - case skew = "sk" - case skewAxis = "sa" - case rotation = "r" - case opacity = "o" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift deleted file mode 100644 index a983e206..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Model/Text/TextDocument.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// TextDocument.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/9/19. -// - -import Foundation - -// MARK: - TextJustification - -enum TextJustification: Int, Codable { - case left - case right - case center -} - -// MARK: - TextDocument - -final class TextDocument: Codable { - - // MARK: Internal - - /// The Text - let text: String - - /// The Font size - let fontSize: Double - - /// The Font Family - let fontFamily: String - - /// Justification - let justification: TextJustification - - /// Tracking - let tracking: Int - - /// Line Height - let lineHeight: Double - - /// Baseline - let baseline: Double? - - /// Fill Color data - let fillColorData: Color? - - /// Scroke Color data - let strokeColorData: Color? - - /// Stroke Width - let strokeWidth: Double? - - /// Stroke Over Fill - let strokeOverFill: Bool? - - let textFramePosition: Vector3D? - - let textFrameSize: Vector3D? - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case text = "t" - case fontSize = "s" - case fontFamily = "f" - case justification = "j" - case tracking = "tr" - case lineHeight = "lh" - case baseline = "ls" - case fillColorData = "fc" - case strokeColorData = "sc" - case strokeWidth = "sw" - case strokeOverFill = "of" - case textFramePosition = "ps" - case textFrameSize = "sz" - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift deleted file mode 100644 index 2df59082..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Extensions/ItemsExtension.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// ItemsExtension.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/18/19. -// - -import Foundation - -// MARK: - NodeTree - -final class NodeTree { - var rootNode: AnimatorNode? = nil - var transform: ShapeTransform? = nil - var renderContainers: [ShapeContainerLayer] = [] - var paths: [PathOutputNode] = [] - var childrenNodes: [AnimatorNode] = [] -} - -extension Array where Element == ShapeItem { - func initializeNodeTree() -> NodeTree { - - let nodeTree = NodeTree() - - for item in self { - guard item.hidden == false, item.type != .unknown else { continue } - if let fill = item as? Fill { - let node = FillNode(parentNode: nodeTree.rootNode, fill: fill) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let stroke = item as? Stroke { - let node = StrokeNode(parentNode: nodeTree.rootNode, stroke: stroke) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let gradientFill = item as? GradientFill { - let node = GradientFillNode(parentNode: nodeTree.rootNode, gradientFill: gradientFill) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let gradientStroke = item as? GradientStroke { - let node = GradientStrokeNode(parentNode: nodeTree.rootNode, gradientStroke: gradientStroke) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let ellipse = item as? Ellipse { - let node = EllipseNode(parentNode: nodeTree.rootNode, ellipse: ellipse) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let rect = item as? Rectangle { - let node = RectangleNode(parentNode: nodeTree.rootNode, rectangle: rect) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let star = item as? Star { - switch star.starType { - case .none: - continue - case .polygon: - let node = PolygonNode(parentNode: nodeTree.rootNode, star: star) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - case .star: - let node = StarNode(parentNode: nodeTree.rootNode, star: star) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } - } else if let shape = item as? Shape { - let node = ShapeNode(parentNode: nodeTree.rootNode, shape: shape) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let trim = item as? Trim { - let node = TrimPathNode(parentNode: nodeTree.rootNode, trim: trim, upstreamPaths: nodeTree.paths) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - } else if let xform = item as? ShapeTransform { - nodeTree.transform = xform - continue - } else if let group = item as? Group { - - let tree = group.items.initializeNodeTree() - let node = GroupNode(name: group.name, parentNode: nodeTree.rootNode, tree: tree) - nodeTree.rootNode = node - nodeTree.childrenNodes.append(node) - /// Now add all child paths to current tree - nodeTree.paths.append(contentsOf: tree.paths) - nodeTree.renderContainers.append(node.container) - } - - if let pathNode = nodeTree.rootNode as? PathNode { - //// Add path container to the node tree - nodeTree.paths.append(pathNode.pathOutput) - } - - if let renderNode = nodeTree.rootNode as? RenderNode { - nodeTree.renderContainers.append(ShapeRenderLayer(renderer: renderNode.renderer)) - } - } - return nodeTree - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift deleted file mode 100644 index 14e00178..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/NodeProperty.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// NodeProperty.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -/// A node property that holds a reference to a T ValueProvider and a T ValueContainer. -class NodeProperty: AnyNodeProperty { - - // MARK: Lifecycle - - init(provider: AnyValueProvider) { - valueProvider = provider - typedContainer = ValueContainer(provider.value(frame: 0) as! T) - typedContainer.setNeedsUpdate() - } - - // MARK: Internal - - var valueProvider: AnyValueProvider - - var valueType: Any.Type { T.self } - - var value: T { - typedContainer.outputValue - } - - var valueContainer: AnyValueContainer { - typedContainer - } - - func needsUpdate(frame: CGFloat) -> Bool { - valueContainer.needsUpdate || valueProvider.hasUpdate(frame: frame) - } - - func setProvider(provider: AnyValueProvider) { - guard provider.valueType == valueType else { return } - valueProvider = provider - valueContainer.setNeedsUpdate() - } - - func update(frame: CGFloat) { - typedContainer.setValue(valueProvider.value(frame: frame), forFrame: frame) - } - - // MARK: Fileprivate - - fileprivate var typedContainer: ValueContainer -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift deleted file mode 100644 index 4f2d5adb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// AnyNodeProperty.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -// MARK: - AnyNodeProperty - -/// A property of a node. The node property holds a provider and a container -protocol AnyNodeProperty { - - /// Returns true if the property needs to recompute its stored value - func needsUpdate(frame: CGFloat) -> Bool - - /// Updates the property for the frame - func update(frame: CGFloat) - - /// The stored value container for the property - var valueContainer: AnyValueContainer { get } - - /// The value provider for the property - var valueProvider: AnyValueProvider { get } - - /// The Type of the value provider - var valueType: Any.Type { get } - - /// Sets the value provider for the property. - func setProvider(provider: AnyValueProvider) -} - -extension AnyNodeProperty { - - /// Returns the most recently computed value for the keypath, returns nil if property wasn't found - func getValueOfType() -> T? { - valueContainer.value as? T - } - - /// Returns the most recently computed value for the keypath, returns nil if property wasn't found - func getValue() -> Any? { - valueContainer.value - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift deleted file mode 100644 index 769d51cb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// AnyValueContainer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -/// The container for the value of a property. -protocol AnyValueContainer: AnyObject { - - /// The stored value of the container - var value: Any { get } - - /// Notifies the provider that it should update its container - func setNeedsUpdate() - - /// When true the container needs to have its value updated by its provider - var needsUpdate: Bool { get } - - /// The frame time of the last provided update - var lastUpdateFrame: CGFloat { get } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift deleted file mode 100644 index b46f524f..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// KeypathSettable.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -import QuartzCore - -/// Protocol that provides keypath search functionality. Returns all node properties associated with a keypath. -protocol KeypathSearchable { - - /// The name of the Keypath - var keypathName: String { get } - - /// A list of properties belonging to the keypath. - var keypathProperties: [String: AnyNodeProperty] { get } - - /// Children Keypaths - var childKeypaths: [KeypathSearchable] { get } - - var keypathLayer: CALayer? { get } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift deleted file mode 100644 index 07b101f7..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// NodePropertyMap.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/21/19. -// - -import Foundation -import QuartzCore - -// MARK: - NodePropertyMap - -protocol NodePropertyMap { - var properties: [AnyNodeProperty] { get } -} - -extension NodePropertyMap { - - var childKeypaths: [KeypathSearchable] { - [] - } - - var keypathLayer: CALayer? { - nil - } - - /// Checks if the node's local contents need to be rebuilt. - func needsLocalUpdate(frame: CGFloat) -> Bool { - for property in properties { - if property.needsUpdate(frame: frame) { - return true - } - } - return false - } - - /// Rebuilds only the local nodes that have an update for the frame - func updateNodeProperties(frame: CGFloat) { - properties.forEach { property in - property.update(frame: frame) - } - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift deleted file mode 100644 index bac72ab7..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueContainer.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// ValueContainer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -/// A container for a node value that is Typed to T. -class ValueContainer: AnyValueContainer { - - // MARK: Lifecycle - - init(_ value: T) { - outputValue = value - } - - // MARK: Internal - - private(set) var lastUpdateFrame = CGFloat.infinity - - fileprivate(set) var needsUpdate: Bool = true - - var value: Any { - outputValue as Any - } - - var outputValue: T { - didSet { - needsUpdate = false - } - } - - func setValue(_ value: Any, forFrame: CGFloat) { - if let typedValue = value as? T { - needsUpdate = false - lastUpdateFrame = forFrame - outputValue = typedValue - } - } - - func setNeedsUpdate() { - needsUpdate = true - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift deleted file mode 100644 index 1790cc2b..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// KeyframeGroupInterpolator.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/22/19. -// - -import CoreGraphics -import Foundation - -/// A value provider that produces an array of values from an array of Keyframe Interpolators -final class GroupInterpolator: AnyValueProvider where ValueType: Interpolatable { - - // MARK: Lifecycle - - /// Initialize with an array of array of keyframes. - init(keyframeGroups: ContiguousArray>>) { - keyframeInterpolators = ContiguousArray(keyframeGroups.map({ KeyframeInterpolator(keyframes: $0) })) - } - - // MARK: Internal - - let keyframeInterpolators: ContiguousArray> - - var valueType: Any.Type { - [ValueType].self - } - - func hasUpdate(frame: CGFloat) -> Bool { - let updated = keyframeInterpolators.first(where: { $0.hasUpdate(frame: frame) }) - return updated != nil - } - - func value(frame: CGFloat) -> Any { - let output = keyframeInterpolators.map({ $0.value(frame: frame) as! ValueType }) - return output - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift deleted file mode 100644 index 5b974fbb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift +++ /dev/null @@ -1,256 +0,0 @@ -// -// KeyframeInterpolator.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/15/19. -// - -import CoreGraphics -import Foundation - -// MARK: - KeyframeInterpolator - -/// A value provider that produces a value at Time from a group of keyframes -final class KeyframeInterpolator: AnyValueProvider where ValueType: Interpolatable { - - // MARK: Lifecycle - - init(keyframes: ContiguousArray>) { - self.keyframes = keyframes - } - - // MARK: Internal - - let keyframes: ContiguousArray> - - var valueType: Any.Type { - ValueType.self - } - - /** - Returns true to trigger a frame update for this interpolator. - - An interpolator will be asked if it needs to update every frame. - If the interpolator needs updating it will be asked to compute its value for - the given frame. - - Cases a keyframe should not be updated: - - If time is in span and leading keyframe is hold - - If time is after the last keyframe. - - If time is before the first keyframe - - Cases for updating a keyframe: - - If time is in the span, and is not a hold - - If time is outside of the span, and there are more keyframes - - If a value delegate is set - - If leading and trailing are both nil. - */ - func hasUpdate(frame: CGFloat) -> Bool { - if lastUpdatedFrame == nil { - return true - } - - if - let leading = leadingKeyframe, - trailingKeyframe == nil, - leading.time < frame - { - /// Frame is after bounds of keyframes - return false - } - if - let trailing = trailingKeyframe, - leadingKeyframe == nil, - frame < trailing.time - { - /// Frame is before bounds of keyframes - return false - } - if - let leading = leadingKeyframe, - let trailing = trailingKeyframe, - leading.isHold, - leading.time < frame, - frame < trailing.time - { - return false - } - return true - } - - @discardableResult - func value(frame: CGFloat) -> Any { - // First set the keyframe span for the frame. - updateSpanIndices(frame: frame) - lastUpdatedFrame = frame - // If only one keyframe return its value - let progress: CGFloat - let value: ValueType - - if - let leading = leadingKeyframe, - let trailing = trailingKeyframe - { - /// We have leading and trailing keyframe. - progress = leading.interpolatedProgress(trailing, keyTime: frame) - value = leading.interpolate(trailing, progress: progress) - } else if let leading = leadingKeyframe { - progress = 0 - value = leading.value - } else if let trailing = trailingKeyframe { - progress = 1 - value = trailing.value - } else { - /// Satisfy the compiler. - progress = 0 - value = keyframes[0].value - } - return value - } - - // MARK: Fileprivate - - fileprivate var lastUpdatedFrame: CGFloat? - - fileprivate var leadingIndex: Int? = nil - fileprivate var trailingIndex: Int? = nil - fileprivate var leadingKeyframe: Keyframe? = nil - fileprivate var trailingKeyframe: Keyframe? = nil - - /// Finds the appropriate Leading and Trailing keyframe index for the given time. - fileprivate func updateSpanIndices(frame: CGFloat) { - guard keyframes.count > 0 else { - leadingIndex = nil - trailingIndex = nil - leadingKeyframe = nil - trailingKeyframe = nil - return - } - - /** - This function searches through the array to find the span of two keyframes - that contain the current time. - - We could use Array.first(where:) but that would search through the entire array - each frame. - Instead we track the last used index and search either forwards or - backwards from there. This reduces the iterations and complexity from - - O(n), where n is the length of the sequence to - O(n), where n is the number of items after or before the last used index. - - */ - - if keyframes.count == 1 { - /// Only one keyframe. Set it as first and move on. - leadingIndex = 0 - trailingIndex = nil - leadingKeyframe = keyframes[0] - trailingKeyframe = nil - return - } - - /// Sets the initial keyframes. This is often only needed for the first check. - if - leadingIndex == nil && - trailingIndex == nil - { - if frame < keyframes[0].time { - /// Time is before the first keyframe. Set it as the trailing. - trailingIndex = 0 - } else { - /// Time is after the first keyframe. Set the keyframe and the trailing. - leadingIndex = 0 - trailingIndex = 1 - } - } - - if - let currentTrailing = trailingIndex, - keyframes[currentTrailing].time <= frame - { - /// Time is after the current span. Iterate forward. - var newLeading = currentTrailing - var keyframeFound: Bool = false - while !keyframeFound { - - leadingIndex = newLeading - trailingIndex = keyframes.validIndex(newLeading + 1) - - guard let trailing = trailingIndex else { - /// We have reached the end of our keyframes. Time is after the last keyframe. - keyframeFound = true - continue - } - if frame < keyframes[trailing].time { - /// Keyframe in current span. - keyframeFound = true - continue - } - /// Advance the array. - newLeading = trailing - } - - } else if - let currentLeading = leadingIndex, - frame < keyframes[currentLeading].time - { - - /// Time is before the current span. Iterate backwards - var newTrailing = currentLeading - - var keyframeFound: Bool = false - while !keyframeFound { - - leadingIndex = keyframes.validIndex(newTrailing - 1) - trailingIndex = newTrailing - - guard let leading = leadingIndex else { - /// We have reached the end of our keyframes. Time is after the last keyframe. - keyframeFound = true - continue - } - if keyframes[leading].time <= frame { - /// Keyframe in current span. - keyframeFound = true - continue - } - /// Step back - newTrailing = leading - } - } - if let keyFrame = leadingIndex { - leadingKeyframe = keyframes[keyFrame] - } else { - leadingKeyframe = nil - } - - if let keyFrame = trailingIndex { - trailingKeyframe = keyframes[keyFrame] - } else { - trailingKeyframe = nil - } - } -} - -extension Array { - - fileprivate func validIndex(_ index: Int) -> Int? { - if 0 <= index, index < endIndex { - return index - } - return nil - } - -} - -extension ContiguousArray { - - fileprivate func validIndex(_ index: Int) -> Int? { - if 0 <= index, index < endIndex { - return index - } - return nil - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift deleted file mode 100644 index 45447492..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// SingleValueProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import Foundation -import QuartzCore - -/// Returns a value for every frame. -final class SingleValueProvider: AnyValueProvider { - - // MARK: Lifecycle - - init(_ value: ValueType) { - self.value = value - } - - // MARK: Internal - - var value: ValueType { - didSet { - hasUpdate = true - } - } - - var valueType: Any.Type { - ValueType.self - } - - func hasUpdate(frame _: CGFloat) -> Bool { - hasUpdate - } - - func value(frame _: CGFloat) -> Any { - hasUpdate = false - return value - } - - // MARK: Private - - private var hasUpdate: Bool = true -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift deleted file mode 100644 index d6d2c580..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +++ /dev/null @@ -1,281 +0,0 @@ -// -// TrimPathNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/23/19. -// - -import Foundation -import QuartzCore - -// MARK: - TrimPathProperties - -final class TrimPathProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(trim: Trim) { - keypathName = trim.name - start = NodeProperty(provider: KeyframeInterpolator(keyframes: trim.start.keyframes)) - end = NodeProperty(provider: KeyframeInterpolator(keyframes: trim.end.keyframes)) - offset = NodeProperty(provider: KeyframeInterpolator(keyframes: trim.offset.keyframes)) - type = trim.trimType - keypathProperties = [ - "Start" : start, - "End" : end, - "Offset" : offset, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - let keypathName: String - - let start: NodeProperty - let end: NodeProperty - let offset: NodeProperty - let type: TrimType -} - -// MARK: - TrimPathNode - -final class TrimPathNode: AnimatorNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, trim: Trim, upstreamPaths: [PathOutputNode]) { - outputNode = PassThroughOutputNode(parent: parentNode?.outputNode) - self.parentNode = parentNode - properties = TrimPathProperties(trim: trim) - self.upstreamPaths = upstreamPaths - } - - // MARK: Internal - - let properties: TrimPathProperties - - let parentNode: AnimatorNode? - let outputNode: NodeOutput - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - var isEnabled: Bool = true - - // MARK: Animator Node - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - func forceUpstreamOutputUpdates() -> Bool { - hasLocalUpdates || hasUpstreamUpdates - } - - func rebuildOutputs(frame: CGFloat) { - /// Make sure there is a trim. - let startValue = properties.start.value.cgFloatValue * 0.01 - let endValue = properties.end.value.cgFloatValue * 0.01 - let start = min(startValue, endValue) - let end = max(startValue, endValue) - - let offset = properties.offset.value.cgFloatValue.truncatingRemainder(dividingBy: 360) / 360 - - /// No need to trim, it's a full path - if start == 0, end == 1 { - return - } - - /// All paths are empty. - if start == end { - for pathContainer in upstreamPaths { - pathContainer.removePaths(updateFrame: frame) - } - return - } - - if properties.type == .simultaneously { - /// Just trim each path - for pathContainer in upstreamPaths { - let pathObjects = pathContainer.removePaths(updateFrame: frame) - for path in pathObjects { - // We are treating each compount path as an individual path. Its subpaths are treated as a whole. - pathContainer.appendPath( - path.trim(fromPosition: start, toPosition: end, offset: offset, trimSimultaneously: false), - updateFrame: frame) - } - } - return - } - - /// Individual path trimming. - - /// Brace yourself for the below code. - - /// Normalize lengths with offset. - var startPosition = (start + offset).truncatingRemainder(dividingBy: 1) - var endPosition = (end + offset).truncatingRemainder(dividingBy: 1) - - if startPosition < 0 { - startPosition = 1 + startPosition - } - - if endPosition < 0 { - endPosition = 1 + endPosition - } - if startPosition == 1 { - startPosition = 0 - } - if endPosition == 0 { - endPosition = 1 - } - - /// First get the total length of all paths. - var totalLength: CGFloat = 0 - upstreamPaths.forEach({ totalLength = totalLength + $0.totalLength }) - - /// Now determine the start and end cut lengths - let startLength = startPosition * totalLength - let endLength = endPosition * totalLength - var pathStart: CGFloat = 0 - - /// Now loop through all path containers - for pathContainer in upstreamPaths { - - let pathEnd = pathStart + pathContainer.totalLength - - if - !startLength.isInRange(pathStart, pathEnd) && - endLength.isInRange(pathStart, pathEnd) - { - // pathStart|=======E----------------------|pathEnd - // Cut path components, removing after end. - - let pathCutLength = endLength - pathStart - let subpaths = pathContainer.removePaths(updateFrame: frame) - var subpathStart: CGFloat = 0 - for path in subpaths { - let subpathEnd = subpathStart + path.length - if pathCutLength < subpathEnd { - /// This is the subpath that needs to be cut. - let cutLength = pathCutLength - subpathStart - let newPath = path.trim(fromPosition: 0, toPosition: cutLength / path.length, offset: 0, trimSimultaneously: false) - pathContainer.appendPath(newPath, updateFrame: frame) - break - } else { - /// Add to container and move on - pathContainer.appendPath(path, updateFrame: frame) - } - if pathCutLength == subpathEnd { - /// Right on the end. The next subpath is not included. Break. - break - } - subpathStart = subpathEnd - } - - } else if - !endLength.isInRange(pathStart, pathEnd) && - startLength.isInRange(pathStart, pathEnd) - { - // pathStart|-------S======================|pathEnd - // - - // Cut path components, removing before beginning. - let pathCutLength = startLength - pathStart - // Clear paths from container - let subpaths = pathContainer.removePaths(updateFrame: frame) - var subpathStart: CGFloat = 0 - for path in subpaths { - let subpathEnd = subpathStart + path.length - - if subpathStart < pathCutLength, pathCutLength < subpathEnd { - /// This is the subpath that needs to be cut. - let cutLength = pathCutLength - subpathStart - let newPath = path.trim(fromPosition: cutLength / path.length, toPosition: 1, offset: 0, trimSimultaneously: false) - pathContainer.appendPath(newPath, updateFrame: frame) - } else if pathCutLength <= subpathStart { - pathContainer.appendPath(path, updateFrame: frame) - } - subpathStart = subpathEnd - } - } else if - endLength.isInRange(pathStart, pathEnd) && - startLength.isInRange(pathStart, pathEnd) - { - // pathStart|-------S============E---------|endLength - // pathStart|=====E----------------S=======|endLength - // trim from path beginning to endLength. - - // Cut path components, removing before beginnings. - let startCutLength = startLength - pathStart - let endCutLength = endLength - pathStart - // Clear paths from container - let subpaths = pathContainer.removePaths(updateFrame: frame) - var subpathStart: CGFloat = 0 - for path in subpaths { - - let subpathEnd = subpathStart + path.length - - if - !startCutLength.isInRange(subpathStart, subpathEnd) && - !endCutLength.isInRange(subpathStart, subpathEnd) - { - // The whole path is included. Add - // S|==============================|E - pathContainer.appendPath(path, updateFrame: frame) - - } else if - startCutLength.isInRange(subpathStart, subpathEnd) && - !endCutLength.isInRange(subpathStart, subpathEnd) - { - /// The start of the path needs to be trimmed - // |-------S======================|E - let cutLength = startCutLength - subpathStart - let newPath = path.trim(fromPosition: cutLength / path.length, toPosition: 1, offset: 0, trimSimultaneously: false) - pathContainer.appendPath(newPath, updateFrame: frame) - } else if - !startCutLength.isInRange(subpathStart, subpathEnd) && - endCutLength.isInRange(subpathStart, subpathEnd) - { - // S|=======E----------------------| - let cutLength = endCutLength - subpathStart - let newPath = path.trim(fromPosition: 0, toPosition: cutLength / path.length, offset: 0, trimSimultaneously: false) - pathContainer.appendPath(newPath, updateFrame: frame) - break - } else if - startCutLength.isInRange(subpathStart, subpathEnd) && - endCutLength.isInRange(subpathStart, subpathEnd) - { - // |-------S============E---------| - let cutFromLength = startCutLength - subpathStart - let cutToLength = endCutLength - subpathStart - let newPath = path.trim( - fromPosition: cutFromLength / path.length, - toPosition: cutToLength / path.length, - offset: 0, - trimSimultaneously: false) - pathContainer.appendPath(newPath, updateFrame: frame) - break - } - - subpathStart = subpathEnd - } - } else if - (endLength <= pathStart && pathEnd <= startLength) || - (startLength <= pathStart && endLength <= pathStart) || - (pathEnd <= startLength && pathEnd <= endLength) - { - /// The Path needs to be cleared - pathContainer.removePaths(updateFrame: frame) - } - - pathStart = pathEnd - } - - } - - // MARK: Fileprivate - - fileprivate let upstreamPaths: [PathOutputNode] -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift deleted file mode 100644 index c44ef41f..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// TransformNodeOutput.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -class GroupOutputNode: NodeOutput { - - // MARK: Lifecycle - - init(parent: NodeOutput?, rootNode: NodeOutput?) { - self.parent = parent - self.rootNode = rootNode - } - - // MARK: Internal - - let parent: NodeOutput? - let rootNode: NodeOutput? - var isEnabled: Bool = true - - private(set) var outputPath: CGPath? = nil - private(set) var transform: CATransform3D = CATransform3DIdentity - - func setTransform(_ xform: CATransform3D, forFrame _: CGFloat) { - transform = xform - outputPath = nil - } - - func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { - guard isEnabled else { - let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false - outputPath = parent?.outputPath - return upstreamUpdates - } - - let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false - if upstreamUpdates { - outputPath = nil - } - let rootUpdates = rootNode?.hasOutputUpdates(forFrame) ?? false - if rootUpdates { - outputPath = nil - } - - var localUpdates: Bool = false - if outputPath == nil { - localUpdates = true - - let newPath = CGMutablePath() - if let parentNode = parent, let parentPath = parentNode.outputPath { - /// First add parent path. - newPath.addPath(parentPath) - } - var xform = CATransform3DGetAffineTransform(transform) - if - let rootNode = rootNode, - let rootPath = rootNode.outputPath, - let xformedPath = rootPath.copy(using: &xform) - { - /// Now add root path. Note root path is transformed. - newPath.addPath(xformedPath) - } - - outputPath = newPath - } - - return upstreamUpdates || localUpdates - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift deleted file mode 100644 index 332a2d96..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// PassThroughOutputNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -class PassThroughOutputNode: NodeOutput { - - // MARK: Lifecycle - - init(parent: NodeOutput?) { - self.parent = parent - } - - // MARK: Internal - - let parent: NodeOutput? - - var hasUpdate: Bool = false - var isEnabled: Bool = true - - var outputPath: CGPath? { - if let parent = parent { - return parent.outputPath - } - return nil - } - - func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { - /// Changes to this node do not affect downstream nodes. - let parentUpdate = parent?.hasOutputUpdates(forFrame) ?? false - /// Changes to upstream nodes do, however, affect this nodes state. - hasUpdate = hasUpdate || parentUpdate - return parentUpdate - } - - func hasRenderUpdates(_ forFrame: CGFloat) -> Bool { - /// Return true if there are upstream updates or if this node has updates - let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false - hasUpdate = hasUpdate || upstreamUpdates - return hasUpdate - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift deleted file mode 100644 index 5ddb0058..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// PathNodeOutput.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -/// A node that has an output of a BezierPath -class PathOutputNode: NodeOutput { - - // MARK: Lifecycle - - init(parent: NodeOutput?) { - self.parent = parent - } - - // MARK: Internal - - let parent: NodeOutput? - - fileprivate(set) var outputPath: CGPath? = nil - - var lastUpdateFrame: CGFloat? = nil - var lastPathBuildFrame: CGFloat? = nil - var isEnabled: Bool = true - fileprivate(set) var totalLength: CGFloat = 0 - fileprivate(set) var pathObjects: [CompoundBezierPath] = [] - - func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { - guard isEnabled else { - let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false - outputPath = parent?.outputPath - return upstreamUpdates - } - - /// Ask if parent was updated - let upstreamUpdates = parent?.hasOutputUpdates(forFrame) ?? false - - /// If parent was updated and the path hasn't been built for this frame, clear the path. - if upstreamUpdates && lastPathBuildFrame != forFrame { - outputPath = nil - } - - if outputPath == nil { - /// If the path is clear, build the new path. - lastPathBuildFrame = forFrame - let newPath = CGMutablePath() - if let parentNode = parent, let parentPath = parentNode.outputPath { - newPath.addPath(parentPath) - } - for path in pathObjects { - for subPath in path.paths { - newPath.addPath(subPath.cgPath()) - } - } - outputPath = newPath - } - - /// Return true if there were upstream updates or if this node was updated. - return upstreamUpdates || (lastUpdateFrame == forFrame) - } - - @discardableResult - func removePaths(updateFrame: CGFloat?) -> [CompoundBezierPath] { - lastUpdateFrame = updateFrame - let returnPaths = pathObjects - outputPath = nil - totalLength = 0 - pathObjects = [] - return returnPaths - } - - func setPath(_ path: BezierPath, updateFrame: CGFloat) { - lastUpdateFrame = updateFrame - outputPath = nil - totalLength = path.length - pathObjects = [CompoundBezierPath(path: path)] - } - - func appendPath(_ path: CompoundBezierPath, updateFrame: CGFloat) { - lastUpdateFrame = updateFrame - outputPath = nil - totalLength = totalLength + path.length - pathObjects.append(path) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift deleted file mode 100644 index 0ddcafaa..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// FillRenderer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -extension FillRule { - var cgFillRule: CGPathFillRule { - switch self { - case .evenOdd: - return .evenOdd - default: - return .winding - } - } - - var caFillRule: CAShapeLayerFillRule { - switch self { - case .evenOdd: - return CAShapeLayerFillRule.evenOdd - default: - return CAShapeLayerFillRule.nonZero - } - } -} - -// MARK: - FillRenderer - -/// A rendered for a Path Fill -final class FillRenderer: PassThroughOutputNode, Renderable { - - let shouldRenderInContext: Bool = false - - var color: CGColor? { - didSet { - hasUpdate = true - } - } - - var opacity: CGFloat = 0 { - didSet { - hasUpdate = true - } - } - - var fillRule: FillRule = .none { - didSet { - hasUpdate = true - } - } - - func updateShapeLayer(layer: CAShapeLayer) { - layer.fillColor = color - layer.opacity = Float(opacity) - layer.fillRule = fillRule.caFillRule - hasUpdate = false - } - - func render(_ inContext: CGContext) { - guard inContext.path != nil && inContext.path!.isEmpty == false else { - return - } - guard let color = color else { return } - hasUpdate = false - inContext.setAlpha(opacity * 0.01) - inContext.setFillColor(color) - inContext.fillPath(using: fillRule.cgFillRule) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift deleted file mode 100644 index f1f317cb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +++ /dev/null @@ -1,149 +0,0 @@ -// -// GradientFillRenderer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import Foundation -import QuartzCore - -/// A rendered for a Path Fill -final class GradientFillRenderer: PassThroughOutputNode, Renderable { - - var shouldRenderInContext: Bool = true - - var start: CGPoint = .zero { - didSet { - hasUpdate = true - } - } - - var numberOfColors: Int = 0 { - didSet { - hasUpdate = true - } - } - - var colors: [CGFloat] = [] { - didSet { - hasUpdate = true - } - } - - var end: CGPoint = .zero { - didSet { - hasUpdate = true - } - } - - var opacity: CGFloat = 0 { - didSet { - hasUpdate = true - } - } - - var type: GradientType = .none { - didSet { - hasUpdate = true - } - } - - func updateShapeLayer(layer _: CAShapeLayer) { - // Not applicable - } - - func render(_ inContext: CGContext) { - guard inContext.path != nil && inContext.path!.isEmpty == false else { - return - } - hasUpdate = false - var alphaColors = [CGColor]() - var alphaLocations = [CGFloat]() - - var gradientColors = [CGColor]() - var colorLocations = [CGFloat]() - let colorSpace = CGColorSpaceCreateDeviceRGB() - let maskColorSpace = CGColorSpaceCreateDeviceGray() - for i in 0.. ix, let color = CGColor( - colorSpace: colorSpace, - components: [colors[ix + 1], colors[ix + 2], colors[ix + 3], 1]) - { - gradientColors.append(color) - colorLocations.append(colors[ix]) - } - } - - var drawMask = false - for i in stride(from: numberOfColors * 4, to: colors.endIndex, by: 2) { - let alpha = colors[i + 1] - if alpha < 1 { - drawMask = true - } - if let color = CGColor(colorSpace: maskColorSpace, components: [alpha, 1]) { - alphaLocations.append(colors[i]) - alphaColors.append(color) - } - } - - inContext.setAlpha(opacity) - inContext.clip() - - /// First draw a mask is necessary. - if drawMask { - guard - let maskGradient = CGGradient( - colorsSpace: maskColorSpace, - colors: alphaColors as CFArray, - locations: alphaLocations), - let maskContext = CGContext( - data: nil, - width: inContext.width, - height: inContext.height, - bitsPerComponent: 8, - bytesPerRow: inContext.width, - space: maskColorSpace, - bitmapInfo: 0) else { return } - let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(maskContext.height)) - maskContext.concatenate(flipVertical) - maskContext.concatenate(inContext.ctm) - if type == .linear { - maskContext.drawLinearGradient( - maskGradient, - start: start, - end: end, - options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) - } else { - maskContext.drawRadialGradient( - maskGradient, - startCenter: start, - startRadius: 0, - endCenter: start, - endRadius: start.distanceTo(end), - options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) - } - /// Clips the gradient - if let alphaMask = maskContext.makeImage() { - inContext.clip(to: inContext.boundingBoxOfClipPath, mask: alphaMask) - } - } - - /// Now draw the gradient - guard let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors as CFArray, locations: colorLocations) - else { return } - if type == .linear { - inContext.drawLinearGradient(gradient, start: start, end: end, options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) - } else { - inContext.drawRadialGradient( - gradient, - startCenter: start, - startRadius: 0, - endCenter: start, - endRadius: start.distanceTo(end), - options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift deleted file mode 100644 index 9e1a0ccb..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// GradientStrokeRenderer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import Foundation -import QuartzCore - -// MARK: - Renderer - -final class GradientStrokeRenderer: PassThroughOutputNode, Renderable { - - // MARK: Lifecycle - - override init(parent: NodeOutput?) { - strokeRender = StrokeRenderer(parent: nil) - gradientRender = GradientFillRenderer(parent: nil) - strokeRender.color = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 1, 1, 1]) - super.init(parent: parent) - } - - // MARK: Internal - - var shouldRenderInContext: Bool = true - - let strokeRender: StrokeRenderer - let gradientRender: GradientFillRenderer - - override func hasOutputUpdates(_ forFrame: CGFloat) -> Bool { - let updates = super.hasOutputUpdates(forFrame) - return updates || strokeRender.hasUpdate || gradientRender.hasUpdate - } - - func updateShapeLayer(layer _: CAShapeLayer) { - /// Not Applicable - } - - func render(_ inContext: CGContext) { - guard inContext.path != nil && inContext.path!.isEmpty == false else { - return - } - - strokeRender.hasUpdate = false - hasUpdate = false - gradientRender.hasUpdate = false - - strokeRender.setupForStroke(inContext) - - inContext.replacePathWithStrokedPath() - - /// Now draw the gradient. - gradientRender.render(inContext) - - } - - func renderBoundsFor(_ boundingBox: CGRect) -> CGRect { - strokeRender.renderBoundsFor(boundingBox) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift deleted file mode 100644 index b283cb67..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// StrokeRenderer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import Foundation -import QuartzCore - -extension LineJoin { - var cgLineJoin: CGLineJoin { - switch self { - case .bevel: - return .bevel - case .none: - return .miter - case .miter: - return .miter - case .round: - return .round - } - } - - var caLineJoin: CAShapeLayerLineJoin { - switch self { - case .none: - return CAShapeLayerLineJoin.miter - case .miter: - return CAShapeLayerLineJoin.miter - case .round: - return CAShapeLayerLineJoin.round - case .bevel: - return CAShapeLayerLineJoin.bevel - } - } -} - -extension LineCap { - var cgLineCap: CGLineCap { - switch self { - case .none: - return .butt - case .butt: - return .butt - case .round: - return .round - case .square: - return .square - } - } - - var caLineCap: CAShapeLayerLineCap { - switch self { - case .none: - return CAShapeLayerLineCap.butt - case .butt: - return CAShapeLayerLineCap.butt - case .round: - return CAShapeLayerLineCap.round - case .square: - return CAShapeLayerLineCap.square - } - } -} - -// MARK: - StrokeRenderer - -/// A rendered that renders a stroke on a path. -final class StrokeRenderer: PassThroughOutputNode, Renderable { - - var shouldRenderInContext: Bool = false - - var color: CGColor? { - didSet { - hasUpdate = true - } - } - - var opacity: CGFloat = 0 { - didSet { - hasUpdate = true - } - } - - var width: CGFloat = 0 { - didSet { - hasUpdate = true - } - } - - var miterLimit: CGFloat = 0 { - didSet { - hasUpdate = true - } - } - - var lineCap: LineCap = .none { - didSet { - hasUpdate = true - } - } - - var lineJoin: LineJoin = .none { - didSet { - hasUpdate = true - } - } - - var dashPhase: CGFloat? { - didSet { - hasUpdate = true - } - } - - var dashLengths: [CGFloat]? { - didSet { - hasUpdate = true - } - } - - func renderBoundsFor(_ boundingBox: CGRect) -> CGRect { - boundingBox.insetBy(dx: -width, dy: -width) - } - - func setupForStroke(_ inContext: CGContext) { - inContext.setLineWidth(width) - inContext.setMiterLimit(miterLimit) - inContext.setLineCap(lineCap.cgLineCap) - inContext.setLineJoin(lineJoin.cgLineJoin) - if let dashPhase = dashPhase, let lengths = dashLengths { - inContext.setLineDash(phase: dashPhase, lengths: lengths) - } else { - inContext.setLineDash(phase: 0, lengths: []) - } - } - - func render(_ inContext: CGContext) { - guard inContext.path != nil && inContext.path!.isEmpty == false else { - return - } - guard let color = color else { return } - hasUpdate = false - setupForStroke(inContext) - inContext.setAlpha(opacity) - inContext.setStrokeColor(color) - inContext.strokePath() - } - - func updateShapeLayer(layer: CAShapeLayer) { - layer.strokeColor = color - layer.opacity = Float(opacity) - layer.lineWidth = width - layer.lineJoin = lineJoin.caLineJoin - layer.lineCap = lineCap.caLineCap - layer.lineDashPhase = dashPhase ?? 0 - layer.fillColor = nil - if let dashPattern = dashLengths { - layer.lineDashPattern = dashPattern.map({ NSNumber(value: Double($0)) }) - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift deleted file mode 100644 index 64b2226b..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// EllipseNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/17/19. -// - -import Foundation -import QuartzCore - -// MARK: - EllipseNodeProperties - -final class EllipseNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(ellipse: Ellipse) { - keypathName = ellipse.name - direction = ellipse.direction - position = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.position.keyframes)) - size = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.size.keyframes)) - keypathProperties = [ - "Position" : position, - "Size" : size, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - let direction: PathDirection - let position: NodeProperty - let size: NodeProperty - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] -} - -// MARK: - EllipseNode - -final class EllipseNode: AnimatorNode, PathNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, ellipse: Ellipse) { - pathOutput = PathOutputNode(parent: parentNode?.outputNode) - properties = EllipseNodeProperties(ellipse: ellipse) - self.parentNode = parentNode - } - - // MARK: Internal - - static let ControlPointConstant: CGFloat = 0.55228 - - let pathOutput: PathOutputNode - - let properties: EllipseNodeProperties - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - // MARK: Animator Node - - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - var isEnabled: Bool = true { - didSet{ - pathOutput.isEnabled = isEnabled - } - } - - func rebuildOutputs(frame: CGFloat) { - let ellipseSize = properties.size.value.sizeValue - let center = properties.position.value.pointValue - - // Unfortunately we HAVE to manually build out the ellipse. - // Every Apple method constructs an ellipse from the 3 o-clock position - // After effects constructs from the Noon position. - // After effects does clockwise, but also has a flag for reversed. - - var half = ellipseSize * 0.5 - if properties.direction == .counterClockwise { - half.width = half.width * -1 - } - - let q1 = CGPoint(x: center.x, y: center.y - half.height) - let q2 = CGPoint(x: center.x + half.width, y: center.y) - let q3 = CGPoint(x: center.x, y: center.y + half.height) - let q4 = CGPoint(x: center.x - half.width, y: center.y) - - let cp = half * EllipseNode.ControlPointConstant - - var path = BezierPath(startPoint: CurveVertex( - point: q1, - inTangentRelative: CGPoint(x: -cp.width, y: 0), - outTangentRelative: CGPoint(x: cp.width, y: 0))) - path.addVertex(CurveVertex( - point: q2, - inTangentRelative: CGPoint(x: 0, y: -cp.height), - outTangentRelative: CGPoint(x: 0, y: cp.height))) - - path.addVertex(CurveVertex( - point: q3, - inTangentRelative: CGPoint(x: cp.width, y: 0), - outTangentRelative: CGPoint(x: -cp.width, y: 0))) - - path.addVertex(CurveVertex( - point: q4, - inTangentRelative: CGPoint(x: 0, y: cp.height), - outTangentRelative: CGPoint(x: 0, y: -cp.height))) - - path.addVertex(CurveVertex( - point: q1, - inTangentRelative: CGPoint(x: -cp.width, y: 0), - outTangentRelative: CGPoint(x: cp.width, y: 0))) - path.close() - pathOutput.setPath(path, updateFrame: frame) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift deleted file mode 100644 index dc2d94cd..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +++ /dev/null @@ -1,152 +0,0 @@ -// -// PolygonNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/21/19. -// - -import Foundation -import QuartzCore - -// MARK: - PolygonNodeProperties - -final class PolygonNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(star: Star) { - keypathName = star.name - direction = star.direction - position = NodeProperty(provider: KeyframeInterpolator(keyframes: star.position.keyframes)) - outerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRadius.keyframes)) - outerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRoundness.keyframes)) - rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes)) - points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes)) - keypathProperties = [ - "Position" : position, - "Outer Radius" : outerRadius, - "Outer Roundedness" : outerRoundedness, - "Rotation" : rotation, - "Points" : points, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - var childKeypaths: [KeypathSearchable] = [] - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - - let direction: PathDirection - let position: NodeProperty - let outerRadius: NodeProperty - let outerRoundedness: NodeProperty - let rotation: NodeProperty - let points: NodeProperty -} - -// MARK: - PolygonNode - -final class PolygonNode: AnimatorNode, PathNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, star: Star) { - pathOutput = PathOutputNode(parent: parentNode?.outputNode) - properties = PolygonNodeProperties(star: star) - self.parentNode = parentNode - } - - // MARK: Internal - - /// Magic number needed for constructing path. - static let PolygonConstant: CGFloat = 0.25 - - let properties: PolygonNodeProperties - - let pathOutput: PathOutputNode - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - // MARK: Animator Node - - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - var isEnabled: Bool = true { - didSet{ - pathOutput.isEnabled = isEnabled - } - } - - func rebuildOutputs(frame: CGFloat) { - let outerRadius = properties.outerRadius.value.cgFloatValue - let outerRoundedness = properties.outerRoundedness.value.cgFloatValue * 0.01 - let numberOfPoints = properties.points.value.cgFloatValue - let rotation = properties.rotation.value.cgFloatValue - let position = properties.position.value.pointValue - - var currentAngle = (rotation - 90).toRadians() - let anglePerPoint = ((2 * CGFloat.pi) / numberOfPoints) - - var point = CGPoint( - x: outerRadius * cos(currentAngle), - y: outerRadius * sin(currentAngle)) - var vertices = [CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero)] - - var previousPoint = point - currentAngle += anglePerPoint; - for _ in 0.. - let size: NodeProperty - let cornerRadius: NodeProperty - -} - -// MARK: - RectangleNode - -final class RectangleNode: AnimatorNode, PathNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, rectangle: Rectangle) { - properties = RectNodeProperties(rectangle: rectangle) - pathOutput = PathOutputNode(parent: parentNode?.outputNode) - self.parentNode = parentNode - } - - // MARK: Internal - - let properties: RectNodeProperties - - let pathOutput: PathOutputNode - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - // MARK: Animator Node - - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - var isEnabled: Bool = true { - didSet{ - pathOutput.isEnabled = isEnabled - } - } - - func rebuildOutputs(frame: CGFloat) { - - let size = properties.size.value.sizeValue * 0.5 - let radius = min(min(properties.cornerRadius.value.cgFloatValue, size.width) , size.height) - let position = properties.position.value.pointValue - var bezierPath = BezierPath() - let points: [CurveVertex] - - if radius <= 0 { - /// No Corners - points = [ - /// Lead In - CurveVertex( - point: CGPoint(x: size.width, y: -size.height), - inTangentRelative: .zero, - outTangentRelative: .zero) - .translated(position), - /// Corner 1 - CurveVertex( - point: CGPoint(x: size.width, y: size.height), - inTangentRelative: .zero, - outTangentRelative: .zero) - .translated(position), - /// Corner 2 - CurveVertex( - point: CGPoint(x: -size.width, y: size.height), - inTangentRelative: .zero, - outTangentRelative: .zero) - .translated(position), - /// Corner 3 - CurveVertex( - point: CGPoint(x: -size.width, y: -size.height), - inTangentRelative: .zero, - outTangentRelative: .zero) - .translated(position), - /// Corner 4 - CurveVertex( - point: CGPoint(x: size.width, y: -size.height), - inTangentRelative: .zero, - outTangentRelative: .zero) - .translated(position), - ] - } else { - let controlPoint = radius * EllipseNode.ControlPointConstant - points = [ - /// Lead In - CurveVertex( - CGPoint(x: radius, y: 0), - CGPoint(x: radius, y: 0), - CGPoint(x: radius, y: 0)) - .translated(CGPoint(x: -radius, y: radius)) - .translated(CGPoint(x: size.width, y: -size.height)) - .translated(position), - /// Corner 1 - CurveVertex( - CGPoint(x: radius, y: 0), // In tangent - CGPoint(x: radius, y: 0), // Point - CGPoint(x: radius, y: controlPoint)) - .translated(CGPoint(x: -radius, y: -radius)) - .translated(CGPoint(x: size.width, y: size.height)) - .translated(position), - CurveVertex( - CGPoint(x: controlPoint, y: radius), // In tangent - CGPoint(x: 0, y: radius), // Point - CGPoint(x: 0, y: radius)) // Out Tangent - .translated(CGPoint(x: -radius, y: -radius)) - .translated(CGPoint(x: size.width, y: size.height)) - .translated(position), - /// Corner 2 - CurveVertex( - CGPoint(x: 0, y: radius), // In tangent - CGPoint(x: 0, y: radius), // Point - CGPoint(x: -controlPoint, y: radius))// Out tangent - .translated(CGPoint(x: radius, y: -radius)) - .translated(CGPoint(x: -size.width, y: size.height)) - .translated(position), - CurveVertex( - CGPoint(x: -radius, y: controlPoint), // In tangent - CGPoint(x: -radius, y: 0), // Point - CGPoint(x: -radius, y: 0)) // Out tangent - .translated(CGPoint(x: radius, y: -radius)) - .translated(CGPoint(x: -size.width, y: size.height)) - .translated(position), - /// Corner 3 - CurveVertex( - CGPoint(x: -radius, y: 0), // In tangent - CGPoint(x: -radius, y: 0), // Point - CGPoint(x: -radius, y: -controlPoint)) // Out tangent - .translated(CGPoint(x: radius, y: radius)) - .translated(CGPoint(x: -size.width, y: -size.height)) - .translated(position), - CurveVertex( - CGPoint(x: -controlPoint, y: -radius), // In tangent - CGPoint(x: 0, y: -radius), // Point - CGPoint(x: 0, y: -radius)) // Out tangent - .translated(CGPoint(x: radius, y: radius)) - .translated(CGPoint(x: -size.width, y: -size.height)) - .translated(position), - /// Corner 4 - CurveVertex( - CGPoint(x: 0, y: -radius), // In tangent - CGPoint(x: 0, y: -radius), // Point - CGPoint(x: controlPoint, y: -radius)) // Out tangent - .translated(CGPoint(x: -radius, y: radius)) - .translated(CGPoint(x: size.width, y: -size.height)) - .translated(position), - CurveVertex( - CGPoint(x: radius, y: -controlPoint), // In tangent - CGPoint(x: radius, y: 0), // Point - CGPoint(x: radius, y: 0)) // Out tangent - .translated(CGPoint(x: -radius, y: radius)) - .translated(CGPoint(x: size.width, y: -size.height)) - .translated(position), - ] - } - let reversed = properties.direction == .counterClockwise - let pathPoints = reversed ? points.reversed() : points - for point in pathPoints { - bezierPath.addVertex(reversed ? point.reversed() : point) - } - bezierPath.close() - pathOutput.setPath(bezierPath, updateFrame: frame) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift deleted file mode 100644 index f807bf88..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// PathNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/16/19. -// - -import CoreGraphics -import Foundation - -// MARK: - ShapeNodeProperties - -final class ShapeNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(shape: Shape) { - keypathName = shape.name - path = NodeProperty(provider: KeyframeInterpolator(keyframes: shape.path.keyframes)) - keypathProperties = [ - "Path" : path, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - let path: NodeProperty - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - -} - -// MARK: - ShapeNode - -final class ShapeNode: AnimatorNode, PathNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, shape: Shape) { - pathOutput = PathOutputNode(parent: parentNode?.outputNode) - properties = ShapeNodeProperties(shape: shape) - self.parentNode = parentNode - } - - // MARK: Internal - - let properties: ShapeNodeProperties - - let pathOutput: PathOutputNode - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - // MARK: Animator Node - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - var isEnabled: Bool = true { - didSet{ - pathOutput.isEnabled = isEnabled - } - } - - func rebuildOutputs(frame: CGFloat) { - pathOutput.setPath(properties.path.value, updateFrame: frame) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift deleted file mode 100644 index 61dd394d..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +++ /dev/null @@ -1,201 +0,0 @@ -// -// StarNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/21/19. -// - -import Foundation -import QuartzCore - -// MARK: - StarNodeProperties - -final class StarNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(star: Star) { - keypathName = star.name - direction = star.direction - position = NodeProperty(provider: KeyframeInterpolator(keyframes: star.position.keyframes)) - outerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRadius.keyframes)) - outerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRoundness.keyframes)) - if let innerRadiusKeyframes = star.innerRadius?.keyframes { - innerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: innerRadiusKeyframes)) - } else { - innerRadius = NodeProperty(provider: SingleValueProvider(Vector1D(0))) - } - if let innderRoundedness = star.innerRoundness?.keyframes { - innerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: innderRoundedness)) - } else { - innerRoundedness = NodeProperty(provider: SingleValueProvider(Vector1D(0))) - } - rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes)) - points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes)) - keypathProperties = [ - "Position" : position, - "Outer Radius" : outerRadius, - "Outer Roundedness" : outerRoundedness, - "Inner Radius" : innerRadius, - "Inner Roundedness" : innerRoundedness, - "Rotation" : rotation, - "Points" : points, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - - let direction: PathDirection - let position: NodeProperty - let outerRadius: NodeProperty - let outerRoundedness: NodeProperty - let innerRadius: NodeProperty - let innerRoundedness: NodeProperty - let rotation: NodeProperty - let points: NodeProperty -} - -// MARK: - StarNode - -final class StarNode: AnimatorNode, PathNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, star: Star) { - pathOutput = PathOutputNode(parent: parentNode?.outputNode) - properties = StarNodeProperties(star: star) - self.parentNode = parentNode - } - - // MARK: Internal - - /// Magic number needed for building path data - static let PolystarConstant: CGFloat = 0.47829 - - let properties: StarNodeProperties - - let pathOutput: PathOutputNode - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - // MARK: Animator Node - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - var isEnabled: Bool = true { - didSet{ - pathOutput.isEnabled = isEnabled - } - } - - func rebuildOutputs(frame: CGFloat) { - let outerRadius = properties.outerRadius.value.cgFloatValue - let innerRadius = properties.innerRadius.value.cgFloatValue - let outerRoundedness = properties.outerRoundedness.value.cgFloatValue * 0.01 - let innerRoundedness = properties.innerRoundedness.value.cgFloatValue * 0.01 - let numberOfPoints = properties.points.value.cgFloatValue - let rotation = properties.rotation.value.cgFloatValue - let position = properties.position.value.pointValue - - var currentAngle = (rotation - 90).toRadians() - let anglePerPoint = (2 * CGFloat.pi) / numberOfPoints - let halfAnglePerPoint = anglePerPoint / 2.0 - let partialPointAmount = numberOfPoints - floor(numberOfPoints) - - var point: CGPoint = .zero - - var partialPointRadius: CGFloat = 0 - if partialPointAmount != 0 { - currentAngle += halfAnglePerPoint * (1 - partialPointAmount) - partialPointRadius = innerRadius + partialPointAmount * (outerRadius - innerRadius) - point.x = (partialPointRadius * cos(currentAngle)) - point.y = (partialPointRadius * sin(currentAngle)) - currentAngle += anglePerPoint * partialPointAmount / 2 - } else { - point.x = (outerRadius * cos(currentAngle)) - point.y = (outerRadius * sin(currentAngle)) - currentAngle += halfAnglePerPoint - } - - var vertices = [CurveVertex]() - vertices.append(CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero)) - - var previousPoint = point - var longSegment = false - let numPoints = Int(ceil(numberOfPoints) * 2) - for i in 0.. - let position: NodeProperty - let scale: NodeProperty - let rotation: NodeProperty - let opacity: NodeProperty - let skew: NodeProperty - let skewAxis: NodeProperty - - var caTransform: CATransform3D { - CATransform3D.makeTransform( - anchor: anchor.value.pointValue, - position: position.value.pointValue, - scale: scale.value.sizeValue, - rotation: rotation.value.cgFloatValue, - skew: skew.value.cgFloatValue, - skewAxis: skewAxis.value.cgFloatValue) - } -} - -// MARK: - GroupNode - -final class GroupNode: AnimatorNode { - - // MARK: Lifecycle - - // MARK: Initializer - init(name: String, parentNode: AnimatorNode?, tree: NodeTree) { - self.parentNode = parentNode - keypathName = name - rootNode = tree.rootNode - properties = GroupNodeProperties(transform: tree.transform) - groupOutput = GroupOutputNode(parent: parentNode?.outputNode, rootNode: rootNode?.outputNode) - var childKeypaths: [KeypathSearchable] = tree.childrenNodes - childKeypaths.append(properties) - self.childKeypaths = childKeypaths - - for childContainer in tree.renderContainers { - container.insertRenderLayer(childContainer) - } - } - - // MARK: Internal - - // MARK: Properties - let groupOutput: GroupOutputNode - - let properties: GroupNodeProperties - - let rootNode: AnimatorNode? - - var container = ShapeContainerLayer() - - // MARK: Keypath Searchable - - let keypathName: String - - let childKeypaths: [KeypathSearchable] - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - var keypathLayer: CALayer? { - container - } - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - properties - } - - var outputNode: NodeOutput { - groupOutput - } - - var isEnabled: Bool = true { - didSet { - container.isHidden = !isEnabled - } - } - - func performAdditionalLocalUpdates(frame: CGFloat, forceLocalUpdate: Bool) -> Bool { - rootNode?.updateContents(frame, forceLocalUpdate: forceLocalUpdate) ?? false - } - - func performAdditionalOutputUpdates(_ frame: CGFloat, forceOutputUpdate: Bool) { - rootNode?.updateOutputs(frame, forceOutputUpdate: forceOutputUpdate) - } - - func rebuildOutputs(frame: CGFloat) { - container.opacity = Float(properties.opacity.value.cgFloatValue) * 0.01 - container.transform = properties.caTransform - groupOutput.setTransform(container.transform, forFrame: frame) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift deleted file mode 100644 index e75b04c4..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// FillNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/17/19. -// - -import CoreGraphics -import Foundation - -// MARK: - FillNodeProperties - -final class FillNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(fill: Fill) { - keypathName = fill.name - color = NodeProperty(provider: KeyframeInterpolator(keyframes: fill.color.keyframes)) - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: fill.opacity.keyframes)) - type = fill.fillRule - keypathProperties = [ - "Opacity" : opacity, - "Color" : color, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - let opacity: NodeProperty - let color: NodeProperty - let type: FillRule - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - -} - -// MARK: - FillNode - -final class FillNode: AnimatorNode, RenderNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, fill: Fill) { - fillRender = FillRenderer(parent: parentNode?.outputNode) - fillProperties = FillNodeProperties(fill: fill) - self.parentNode = parentNode - } - - // MARK: Internal - - let fillRender: FillRenderer - - let fillProperties: FillNodeProperties - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - var renderer: NodeOutput & Renderable { - fillRender - } - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - fillProperties - } - - var isEnabled: Bool = true { - didSet { - fillRender.isEnabled = isEnabled - } - } - - func localUpdatesPermeateDownstream() -> Bool { - false - } - - func rebuildOutputs(frame _: CGFloat) { - fillRender.color = fillProperties.color.value.cgColorValue - fillRender.opacity = fillProperties.opacity.value.cgFloatValue * 0.01 - fillRender.fillRule = fillProperties.type - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift deleted file mode 100644 index 570d8ca8..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// GradientFillNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/22/19. -// - -import Foundation -import QuartzCore - -// MARK: - GradientFillProperties - -final class GradientFillProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(gradientfill: GradientFill) { - keypathName = gradientfill.name - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.opacity.keyframes)) - startPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.startPoint.keyframes)) - endPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.endPoint.keyframes)) - colors = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientfill.colors.keyframes)) - gradientType = gradientfill.gradientType - numberOfColors = gradientfill.numberOfColors - keypathProperties = [ - "Opacity" : opacity, - "Start Point" : startPoint, - "End Point" : endPoint, - "Colors" : colors, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - let opacity: NodeProperty - let startPoint: NodeProperty - let endPoint: NodeProperty - let colors: NodeProperty<[Double]> - - let gradientType: GradientType - let numberOfColors: Int - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - -} - -// MARK: - GradientFillNode - -final class GradientFillNode: AnimatorNode, RenderNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, gradientFill: GradientFill) { - fillRender = GradientFillRenderer(parent: parentNode?.outputNode) - fillProperties = GradientFillProperties(gradientfill: gradientFill) - self.parentNode = parentNode - } - - // MARK: Internal - - let fillRender: GradientFillRenderer - - let fillProperties: GradientFillProperties - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - var renderer: NodeOutput & Renderable { - fillRender - } - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - fillProperties - } - - var isEnabled: Bool = true { - didSet { - fillRender.isEnabled = isEnabled - } - } - - func localUpdatesPermeateDownstream() -> Bool { - false - } - - func rebuildOutputs(frame _: CGFloat) { - fillRender.start = fillProperties.startPoint.value.pointValue - fillRender.end = fillProperties.endPoint.value.pointValue - fillRender.opacity = fillProperties.opacity.value.cgFloatValue * 0.01 - fillRender.colors = fillProperties.colors.value.map { CGFloat($0) } - fillRender.type = fillProperties.gradientType - fillRender.numberOfColors = fillProperties.numberOfColors - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift deleted file mode 100644 index 8d607d25..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +++ /dev/null @@ -1,151 +0,0 @@ -// -// GradientStrokeNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/23/19. -// - -import CoreGraphics -import Foundation - -// MARK: - GradientStrokeProperties - -final class GradientStrokeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(gradientStroke: GradientStroke) { - keypathName = gradientStroke.name - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.opacity.keyframes)) - startPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.startPoint.keyframes)) - endPoint = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.endPoint.keyframes)) - colors = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.colors.keyframes)) - gradientType = gradientStroke.gradientType - numberOfColors = gradientStroke.numberOfColors - width = NodeProperty(provider: KeyframeInterpolator(keyframes: gradientStroke.width.keyframes)) - miterLimit = CGFloat(gradientStroke.miterLimit) - lineCap = gradientStroke.lineCap - lineJoin = gradientStroke.lineJoin - - if let dashes = gradientStroke.dashPattern { - var dashPatterns = ContiguousArray>>() - var dashPhase = ContiguousArray>() - for dash in dashes { - if dash.type == .offset { - dashPhase = dash.value.keyframes - } else { - dashPatterns.append(dash.value.keyframes) - } - } - dashPattern = NodeProperty(provider: GroupInterpolator(keyframeGroups: dashPatterns)) - self.dashPhase = NodeProperty(provider: KeyframeInterpolator(keyframes: dashPhase)) - } else { - dashPattern = NodeProperty(provider: SingleValueProvider([Vector1D]())) - dashPhase = NodeProperty(provider: SingleValueProvider(Vector1D(0))) - } - keypathProperties = [ - "Opacity" : opacity, - "Start Point" : startPoint, - "End Point" : endPoint, - "Colors" : colors, - "Stroke Width" : width, - "Dashes" : dashPattern, - "Dash Phase" : dashPhase, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - var keypathName: String - - let opacity: NodeProperty - let startPoint: NodeProperty - let endPoint: NodeProperty - let colors: NodeProperty<[Double]> - let width: NodeProperty - - let dashPattern: NodeProperty<[Vector1D]> - let dashPhase: NodeProperty - - let lineCap: LineCap - let lineJoin: LineJoin - let miterLimit: CGFloat - let gradientType: GradientType - let numberOfColors: Int - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - -} - -// MARK: - GradientStrokeNode - -final class GradientStrokeNode: AnimatorNode, RenderNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, gradientStroke: GradientStroke) { - strokeRender = GradientStrokeRenderer(parent: parentNode?.outputNode) - strokeProperties = GradientStrokeProperties(gradientStroke: gradientStroke) - self.parentNode = parentNode - } - - // MARK: Internal - - let strokeRender: GradientStrokeRenderer - - let strokeProperties: GradientStrokeProperties - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - var renderer: NodeOutput & Renderable { - strokeRender - } - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - strokeProperties - } - - var isEnabled: Bool = true { - didSet { - strokeRender.isEnabled = isEnabled - } - } - - func localUpdatesPermeateDownstream() -> Bool { - false - } - - func rebuildOutputs(frame _: CGFloat) { - /// Update gradient properties - strokeRender.gradientRender.start = strokeProperties.startPoint.value.pointValue - strokeRender.gradientRender.end = strokeProperties.endPoint.value.pointValue - strokeRender.gradientRender.opacity = strokeProperties.opacity.value.cgFloatValue - strokeRender.gradientRender.colors = strokeProperties.colors.value.map { CGFloat($0) } - strokeRender.gradientRender.type = strokeProperties.gradientType - strokeRender.gradientRender.numberOfColors = strokeProperties.numberOfColors - - /// Now update stroke properties - strokeRender.strokeRender.opacity = strokeProperties.opacity.value.cgFloatValue - strokeRender.strokeRender.width = strokeProperties.width.value.cgFloatValue - strokeRender.strokeRender.miterLimit = strokeProperties.miterLimit - strokeRender.strokeRender.lineCap = strokeProperties.lineCap - strokeRender.strokeRender.lineJoin = strokeProperties.lineJoin - - /// Get dash lengths - let dashLengths = strokeProperties.dashPattern.value.map { $0.cgFloatValue } - if dashLengths.count > 0 { - strokeRender.strokeRender.dashPhase = strokeProperties.dashPhase.value.cgFloatValue - strokeRender.strokeRender.dashLengths = dashLengths - } else { - strokeRender.strokeRender.dashLengths = nil - strokeRender.strokeRender.dashPhase = nil - } - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift deleted file mode 100644 index ae7fcc12..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// StrokeNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/22/19. -// - -import Foundation -import QuartzCore - -// MARK: - StrokeNodeProperties - -final class StrokeNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(stroke: Stroke) { - keypathName = stroke.name - color = NodeProperty(provider: KeyframeInterpolator(keyframes: stroke.color.keyframes)) - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: stroke.opacity.keyframes)) - width = NodeProperty(provider: KeyframeInterpolator(keyframes: stroke.width.keyframes)) - miterLimit = CGFloat(stroke.miterLimit) - lineCap = stroke.lineCap - lineJoin = stroke.lineJoin - - if let dashes = stroke.dashPattern { - var dashPatterns = ContiguousArray>>() - var dashPhase = ContiguousArray>() - for dash in dashes { - if dash.type == .offset { - dashPhase = dash.value.keyframes - } else { - dashPatterns.append(dash.value.keyframes) - } - } - dashPattern = NodeProperty(provider: GroupInterpolator(keyframeGroups: dashPatterns)) - if dashPhase.count == 0 { - self.dashPhase = NodeProperty(provider: SingleValueProvider(Vector1D(0))) - } else { - self.dashPhase = NodeProperty(provider: KeyframeInterpolator(keyframes: dashPhase)) - } - } else { - dashPattern = NodeProperty(provider: SingleValueProvider([Vector1D]())) - dashPhase = NodeProperty(provider: SingleValueProvider(Vector1D(0))) - } - keypathProperties = [ - "Opacity" : opacity, - "Color" : color, - "Stroke Width" : width, - "Dashes" : dashPattern, - "Dash Phase" : dashPhase, - ] - properties = Array(keypathProperties.values) - } - - // MARK: Internal - - let keypathName: String - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - - let opacity: NodeProperty - let color: NodeProperty - let width: NodeProperty - - let dashPattern: NodeProperty<[Vector1D]> - let dashPhase: NodeProperty - - let lineCap: LineCap - let lineJoin: LineJoin - let miterLimit: CGFloat - -} - -// MARK: - StrokeNode - -/// Node that manages stroking a path -final class StrokeNode: AnimatorNode, RenderNode { - - // MARK: Lifecycle - - init(parentNode: AnimatorNode?, stroke: Stroke) { - strokeRender = StrokeRenderer(parent: parentNode?.outputNode) - strokeProperties = StrokeNodeProperties(stroke: stroke) - self.parentNode = parentNode - } - - // MARK: Internal - - let strokeRender: StrokeRenderer - - let strokeProperties: StrokeNodeProperties - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - - var renderer: NodeOutput & Renderable { - strokeRender - } - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - strokeProperties - } - - var isEnabled: Bool = true { - didSet { - strokeRender.isEnabled = isEnabled - } - } - - func localUpdatesPermeateDownstream() -> Bool { - false - } - - func rebuildOutputs(frame _: CGFloat) { - strokeRender.color = strokeProperties.color.value.cgColorValue - strokeRender.opacity = strokeProperties.opacity.value.cgFloatValue * 0.01 - strokeRender.width = strokeProperties.width.value.cgFloatValue - strokeRender.miterLimit = strokeProperties.miterLimit - strokeRender.lineCap = strokeProperties.lineCap - strokeRender.lineJoin = strokeProperties.lineJoin - - /// Get dash lengths - let dashLengths = strokeProperties.dashPattern.value.map { $0.cgFloatValue } - if dashLengths.count > 0 { - strokeRender.dashPhase = strokeProperties.dashPhase.value.cgFloatValue - strokeRender.dashLengths = dashLengths - } else { - strokeRender.dashLengths = nil - strokeRender.dashPhase = nil - } - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift deleted file mode 100644 index 6156a8b0..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +++ /dev/null @@ -1,270 +0,0 @@ -// -// TextAnimatorNode.swift -// lottie-ios-iOS -// -// Created by Brandon Withrow on 2/19/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -// MARK: - TextAnimatorNodeProperties - -final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable { - - // MARK: Lifecycle - - init(textAnimator: TextAnimator) { - keypathName = textAnimator.name - var properties = [String : AnyNodeProperty]() - - if let keyframeGroup = textAnimator.anchor { - anchor = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Anchor"] = anchor - } else { - anchor = nil - } - - if let keyframeGroup = textAnimator.position { - position = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Position"] = position - } else { - position = nil - } - - if let keyframeGroup = textAnimator.scale { - scale = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Scale"] = scale - } else { - scale = nil - } - - if let keyframeGroup = textAnimator.skew { - skew = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Skew"] = skew - } else { - skew = nil - } - - if let keyframeGroup = textAnimator.skewAxis { - skewAxis = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Skew Axis"] = skewAxis - } else { - skewAxis = nil - } - - if let keyframeGroup = textAnimator.rotation { - rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Rotation"] = rotation - } else { - rotation = nil - } - - if let keyframeGroup = textAnimator.opacity { - opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Opacity"] = opacity - } else { - opacity = nil - } - - if let keyframeGroup = textAnimator.strokeColor { - strokeColor = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Stroke Color"] = strokeColor - } else { - strokeColor = nil - } - - if let keyframeGroup = textAnimator.fillColor { - fillColor = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Fill Color"] = fillColor - } else { - fillColor = nil - } - - if let keyframeGroup = textAnimator.strokeWidth { - strokeWidth = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Stroke Width"] = strokeWidth - } else { - strokeWidth = nil - } - - if let keyframeGroup = textAnimator.tracking { - tracking = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes)) - properties["Tracking"] = tracking - } else { - tracking = nil - } - - keypathProperties = properties - - self.properties = Array(keypathProperties.values) - } - - // MARK: Internal - - let keypathName: String - - let anchor: NodeProperty? - let position: NodeProperty? - let scale: NodeProperty? - let skew: NodeProperty? - let skewAxis: NodeProperty? - let rotation: NodeProperty? - let opacity: NodeProperty? - let strokeColor: NodeProperty? - let fillColor: NodeProperty? - let strokeWidth: NodeProperty? - let tracking: NodeProperty? - - let keypathProperties: [String: AnyNodeProperty] - let properties: [AnyNodeProperty] - - var caTransform: CATransform3D { - CATransform3D.makeTransform( - anchor: anchor?.value.pointValue ?? .zero, - position: position?.value.pointValue ?? .zero, - scale: scale?.value.sizeValue ?? CGSize(width: 100, height: 100), - rotation: rotation?.value.cgFloatValue ?? 0, - skew: skew?.value.cgFloatValue, - skewAxis: skewAxis?.value.cgFloatValue) - } -} - -// MARK: - TextOutputNode - -final class TextOutputNode: NodeOutput { - - // MARK: Lifecycle - - init(parent: TextOutputNode?) { - parentTextNode = parent - } - - // MARK: Internal - - var parentTextNode: TextOutputNode? - var isEnabled: Bool = true - - var outputPath: CGPath? - - var parent: NodeOutput? { - parentTextNode - } - - var xform: CATransform3D { - get { - _xform ?? parentTextNode?.xform ?? CATransform3DIdentity - } - set { - _xform = newValue - } - } - - var opacity: CGFloat { - get { - _opacity ?? parentTextNode?.opacity ?? 1 - } - set { - _opacity = newValue - } - } - - var strokeColor: CGColor? { - get { - _strokeColor ?? parentTextNode?.strokeColor - } - set { - _strokeColor = newValue - } - } - - var fillColor: CGColor? { - get { - _fillColor ?? parentTextNode?.fillColor - } - set { - _fillColor = newValue - } - } - - var tracking: CGFloat { - get { - _tracking ?? parentTextNode?.tracking ?? 0 - } - set { - _tracking = newValue - } - } - - var strokeWidth: CGFloat { - get { - _strokeWidth ?? parentTextNode?.strokeWidth ?? 0 - } - set { - _strokeWidth = newValue - } - } - - func hasOutputUpdates(_: CGFloat) -> Bool { - // TODO Fix This - true - } - - // MARK: Fileprivate - - fileprivate var _xform: CATransform3D? - fileprivate var _opacity: CGFloat? - fileprivate var _strokeColor: CGColor? - fileprivate var _fillColor: CGColor? - fileprivate var _tracking: CGFloat? - fileprivate var _strokeWidth: CGFloat? -} - -// MARK: - TextAnimatorNode - -class TextAnimatorNode: AnimatorNode { - - // MARK: Lifecycle - - init(parentNode: TextAnimatorNode?, textAnimator: TextAnimator) { - textOutputNode = TextOutputNode(parent: parentNode?.textOutputNode) - textAnimatorProperties = TextAnimatorNodeProperties(textAnimator: textAnimator) - self.parentNode = parentNode - } - - // MARK: Internal - - let textOutputNode: TextOutputNode - - let textAnimatorProperties: TextAnimatorNodeProperties - - let parentNode: AnimatorNode? - var hasLocalUpdates: Bool = false - var hasUpstreamUpdates: Bool = false - var lastUpdateFrame: CGFloat? = nil - var isEnabled: Bool = true - - var outputNode: NodeOutput { - textOutputNode - } - - // MARK: Animator Node Protocol - - var propertyMap: NodePropertyMap & KeypathSearchable { - textAnimatorProperties - } - - func localUpdatesPermeateDownstream() -> Bool { - true - } - - func rebuildOutputs(frame _: CGFloat) { - textOutputNode.xform = textAnimatorProperties.caTransform - textOutputNode.opacity = (textAnimatorProperties.opacity?.value.cgFloatValue ?? 100) * 0.01 - textOutputNode.strokeColor = textAnimatorProperties.strokeColor?.value.cgColorValue - textOutputNode.fillColor = textAnimatorProperties.fillColor?.value.cgColorValue - textOutputNode.tracking = textAnimatorProperties.tracking?.value.cgFloatValue ?? 1 - textOutputNode.strokeWidth = textAnimatorProperties.strokeWidth?.value.cgFloatValue ?? 0 - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift deleted file mode 100644 index d9b15a59..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/AnimatorNode.swift +++ /dev/null @@ -1,204 +0,0 @@ -// -// AnimatorNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/15/19. -// - -import Foundation -import QuartzCore - -// MARK: - NodeOutput - -/** - Defines the basic outputs of an animator node. - - */ -protocol NodeOutput { - - /// The parent node. - var parent: NodeOutput? { get } - - /// Returns true if there are any updates upstream. OutputPath must be built before returning. - func hasOutputUpdates(_ forFrame: CGFloat) -> Bool - - var outputPath: CGPath? { get } - - var isEnabled: Bool { get set } -} - -// MARK: - AnimatorNode - -/** - The Animator Node is the base node in the render system tree. - - It defines a single node that has an output path and option input node. - At animation time the root animation node is asked to update its contents for - the current frame. - The node reaches up its chain of nodes until the first node that does not need - updating is found. Then each node updates its contents down the render pipeline. - Each node adds its local path to its input path and passes it forward. - - An animator node holds a group of interpolators. These interpolators determine - if the node needs an update for the current frame. - - */ -protocol AnimatorNode: AnyObject, KeypathSearchable { - - /** - The available properties of the Node. - - These properties are automatically updated each frame. - These properties are also settable and gettable through the dynamic - property system. - - */ - var propertyMap: NodePropertyMap & KeypathSearchable { get } - - /// The upstream input node - var parentNode: AnimatorNode? { get } - - /// The output of the node. - var outputNode: NodeOutput { get } - - /// Update the outputs of the node. Called if local contents were update or if outputsNeedUpdate returns true. - func rebuildOutputs(frame: CGFloat) - - /// Setters for marking current node state. - var isEnabled: Bool { get set } - var hasLocalUpdates: Bool { get set } - var hasUpstreamUpdates: Bool { get set } - var lastUpdateFrame: CGFloat? { get set } - - // MARK: Optional - - /// Marks if updates to this node affect nodes downstream. - func localUpdatesPermeateDownstream() -> Bool - func forceUpstreamOutputUpdates() -> Bool - - /// Called at the end of this nodes update cycle. Always called. Optional. - func performAdditionalLocalUpdates(frame: CGFloat, forceLocalUpdate: Bool) -> Bool - func performAdditionalOutputUpdates(_ frame: CGFloat, forceOutputUpdate: Bool) - - /// The default simply returns `hasLocalUpdates` - func shouldRebuildOutputs(frame: CGFloat) -> Bool -} - -/// Basic Node Logic -extension AnimatorNode { - - func shouldRebuildOutputs(frame _: CGFloat) -> Bool { - hasLocalUpdates - } - - func localUpdatesPermeateDownstream() -> Bool { - /// Optional override - true - } - - func forceUpstreamOutputUpdates() -> Bool { - /// Optional - false - } - - func performAdditionalLocalUpdates(frame _: CGFloat, forceLocalUpdate: Bool) -> Bool { - /// Optional - forceLocalUpdate - } - - func performAdditionalOutputUpdates(_: CGFloat, forceOutputUpdate _: Bool) { - /// Optional - } - - @discardableResult - func updateOutputs(_ frame: CGFloat, forceOutputUpdate: Bool) -> Bool { - guard isEnabled else { - // Disabled node, pass through. - lastUpdateFrame = frame - return parentNode?.updateOutputs(frame, forceOutputUpdate: forceOutputUpdate) ?? false - } - - if forceOutputUpdate == false && lastUpdateFrame != nil && lastUpdateFrame! == frame { - /// This node has already updated for this frame. Go ahead and return the results. - return hasUpstreamUpdates || hasLocalUpdates - } - - /// Ask if this node should force output updates upstream. - let forceUpstreamUpdates = forceOutputUpdate || forceUpstreamOutputUpdates() - - /// Perform upstream output updates. Optionally mark upstream updates if any. - hasUpstreamUpdates = ( - parentNode? - .updateOutputs(frame, forceOutputUpdate: forceUpstreamUpdates) ?? false || hasUpstreamUpdates) - - /// Perform additional local output updates - performAdditionalOutputUpdates(frame, forceOutputUpdate: forceUpstreamUpdates) - - /// If there are local updates, or if updates have been force, rebuild outputs - if forceUpstreamUpdates || shouldRebuildOutputs(frame: frame) { - lastUpdateFrame = frame - rebuildOutputs(frame: frame) - } - return hasUpstreamUpdates || hasLocalUpdates - } - - /// Rebuilds the content of this node, and upstream nodes if necessary. - @discardableResult - func updateContents(_ frame: CGFloat, forceLocalUpdate: Bool) -> Bool { - guard isEnabled else { - // Disabled node, pass through. - return parentNode?.updateContents(frame, forceLocalUpdate: forceLocalUpdate) ?? false - } - - if forceLocalUpdate == false && lastUpdateFrame != nil && lastUpdateFrame! == frame { - /// This node has already updated for this frame. Go ahead and return the results. - return localUpdatesPermeateDownstream() ? hasUpstreamUpdates || hasLocalUpdates : hasUpstreamUpdates - } - - /// Are there local updates? If so mark the node. - hasLocalUpdates = forceLocalUpdate ? forceLocalUpdate : propertyMap.needsLocalUpdate(frame: frame) - - /// Were there upstream updates? If so mark the node - hasUpstreamUpdates = parentNode?.updateContents(frame, forceLocalUpdate: forceLocalUpdate) ?? false - - /// Perform property updates if necessary. - if hasLocalUpdates { - /// Rebuild local properties - propertyMap.updateNodeProperties(frame: frame) - } - - /// Ask the node to perform any other updates it might have. - hasUpstreamUpdates = performAdditionalLocalUpdates(frame: frame, forceLocalUpdate: forceLocalUpdate) || hasUpstreamUpdates - - /// If the node can update nodes downstream, notify them, otherwise pass on any upstream updates downstream. - return localUpdatesPermeateDownstream() ? hasUpstreamUpdates || hasLocalUpdates : hasUpstreamUpdates - } - - func updateTree(_ frame: CGFloat, forceUpdates: Bool = false) { - updateContents(frame, forceLocalUpdate: forceUpdates) - updateOutputs(frame, forceOutputUpdate: forceUpdates) - } - -} - -extension AnimatorNode { - /// Default implementation for Keypath searchable. - /// Forward all calls to the propertyMap. - - var keypathName: String { - propertyMap.keypathName - } - - var keypathProperties: [String: AnyNodeProperty] { - propertyMap.keypathProperties - } - - var childKeypaths: [KeypathSearchable] { - propertyMap.childKeypaths - } - - var keypathLayer: CALayer? { - nil - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift deleted file mode 100644 index 7eaa6cf4..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/PathNode.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// PathNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/17/19. -// - -import Foundation - -// MARK: - PathNode - -protocol PathNode { - var pathOutput: PathOutputNode { get } -} - -extension PathNode where Self: AnimatorNode { - - var outputNode: NodeOutput { - pathOutput - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift deleted file mode 100644 index ca4e6eaf..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/Protocols/RenderNode.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// RenderNode.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/17/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -// MARK: - RenderNode - -/// A protocol that defines a node that holds render instructions -protocol RenderNode { - var renderer: Renderable & NodeOutput { get } -} - -// MARK: - Renderable - -/// A protocol that defines anything with render instructions -protocol Renderable { - - /// The last frame in which this node was updated. - var hasUpdate: Bool { get } - - func hasRenderUpdates(_ forFrame: CGFloat) -> Bool - - /** - Determines if the renderer requires a custom context for drawing. - If yes the shape layer will perform a custom drawing pass. - If no the shape layer will be a standard CAShapeLayer - */ - var shouldRenderInContext: Bool { get } - - /// Passes in the CAShapeLayer to update - func updateShapeLayer(layer: CAShapeLayer) - - /// Asks the renderer what the renderable bounds is for the given box. - func renderBoundsFor(_ boundingBox: CGRect) -> CGRect - - /// Renders the shape in a custom context - func render(_ inContext: CGContext) -} - -extension RenderNode where Self: AnimatorNode { - - var outputNode: NodeOutput { - renderer - } - -} - -extension Renderable { - - func renderBoundsFor(_ boundingBox: CGRect) -> CGRect { - /// Optional - boundingBox - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift deleted file mode 100644 index 146d2b1a..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// ShapeContainerLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import Foundation -import QuartzCore - -/** - The base layer that holds Shapes and Shape Renderers - */ -class ShapeContainerLayer: CALayer { - - // MARK: Lifecycle - - override init() { - super.init() - actions = [ - "position" : NSNull(), - "bounds" : NSNull(), - "anchorPoint" : NSNull(), - "transform" : NSNull(), - "opacity" : NSNull(), - "hidden" : NSNull(), - ] - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override init(layer: Any) { - guard let layer = layer as? ShapeContainerLayer else { - fatalError("init(layer:) wrong class.") - } - super.init(layer: layer) - } - - // MARK: Internal - - private(set) var renderLayers: [ShapeContainerLayer] = [] - - var renderScale: CGFloat = 1 { - didSet { - updateRenderScale() - } - } - - func insertRenderLayer(_ layer: ShapeContainerLayer) { - renderLayers.append(layer) - insertSublayer(layer, at: 0) - } - - func markRenderUpdates(forFrame: CGFloat) { - if hasRenderUpdate(forFrame: forFrame) { - rebuildContents(forFrame: forFrame) - } - guard isHidden == false else { return } - renderLayers.forEach { $0.markRenderUpdates(forFrame: forFrame) } - } - - func hasRenderUpdate(forFrame _: CGFloat) -> Bool { - false - } - - func rebuildContents(forFrame _: CGFloat) { - /// Override - } - - func updateRenderScale() { - contentsScale = renderScale - renderLayers.forEach( { $0.renderScale = renderScale } ) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift deleted file mode 100644 index 19ffed34..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// RenderLayer.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/18/19. -// - -import Foundation -import QuartzCore - -/** - The layer responsible for rendering shape objects - */ -final class ShapeRenderLayer: ShapeContainerLayer { - - // MARK: Lifecycle - - init(renderer: Renderable & NodeOutput) { - self.renderer = renderer - super.init() - anchorPoint = .zero - actions = [ - "position" : NSNull(), - "bounds" : NSNull(), - "anchorPoint" : NSNull(), - "path" : NSNull(), - "transform" : NSNull(), - "opacity" : NSNull(), - "hidden" : NSNull(), - ] - shapeLayer.actions = [ - "position" : NSNull(), - "bounds" : NSNull(), - "anchorPoint" : NSNull(), - "path" : NSNull(), - "fillColor" : NSNull(), - "strokeColor" : NSNull(), - "lineWidth" : NSNull(), - "miterLimit" : NSNull(), - "lineDashPhase" : NSNull(), - "hidden" : NSNull(), - ] - addSublayer(shapeLayer) - } - - override init(layer: Any) { - guard let layer = layer as? ShapeRenderLayer else { - fatalError("init(layer:) wrong class.") - } - renderer = layer.renderer - super.init(layer: layer) - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Internal - - fileprivate(set) var renderer: Renderable & NodeOutput - - let shapeLayer = CAShapeLayer() - - override func hasRenderUpdate(forFrame: CGFloat) -> Bool { - isHidden = !renderer.isEnabled - guard isHidden == false else { return false } - return renderer.hasRenderUpdates(forFrame) - } - - override func rebuildContents(forFrame _: CGFloat) { - - if renderer.shouldRenderInContext { - if let newPath = renderer.outputPath { - bounds = renderer.renderBoundsFor(newPath.boundingBox) - } else { - bounds = .zero - } - position = bounds.origin - setNeedsDisplay() - } else { - shapeLayer.path = renderer.outputPath - renderer.updateShapeLayer(layer: shapeLayer) - } - } - - override func draw(in ctx: CGContext) { - if let path = renderer.outputPath { - if !path.isEmpty { - ctx.addPath(path) - } - } - renderer.render(ctx) - } - - override func updateRenderScale() { - super.updateRenderScale() - shapeLayer.contentsScale = renderScale - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift deleted file mode 100644 index 3e8a5114..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// AnimatorNodeDebugging.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/18/19. -// - -import Foundation - -extension AnimatorNode { - - func printNodeTree() { - parentNode?.printNodeTree() - print(String(describing: type(of: self))) - - if let group = self as? GroupNode { - print("* |Children") - group.rootNode?.printNodeTree() - print("*") - } else { - print("|") - } - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift deleted file mode 100644 index 25ace05d..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Debugging/LayerDebugging.swift +++ /dev/null @@ -1,226 +0,0 @@ -// -// LayerDebugging.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/24/19. -// - -import Foundation -import QuartzCore - -// MARK: - LayerDebugStyle - -struct LayerDebugStyle { - let anchorColor: CGColor - let boundsColor: CGColor - let anchorWidth: CGFloat - let boundsWidth: CGFloat -} - -// MARK: - LayerDebugging - -protocol LayerDebugging { - var debugStyle: LayerDebugStyle { get } -} - -// MARK: - CustomLayerDebugging - -protocol CustomLayerDebugging { - func layerForDebugging() -> CALayer -} - -// MARK: - DebugLayer - -class DebugLayer: CALayer { - init(style: LayerDebugStyle) { - super.init() - zPosition = 1000 - bounds = CGRect(x: 0, y: 0, width: style.anchorWidth, height: style.anchorWidth) - backgroundColor = style.anchorColor - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension CALayer { - - public func logLayerTree(withIndent: Int = 0) { - var string = "" - for _ in 0...withIndent { - string = string + " " - } - string = string + "|_" + String(describing: self) - print(string) - if let sublayers = sublayers { - for sublayer in sublayers { - sublayer.logLayerTree(withIndent: withIndent + 1) - } - } - } - -} - -// MARK: - CompositionLayer + CustomLayerDebugging - -extension CompositionLayer: CustomLayerDebugging { - func layerForDebugging() -> CALayer { - contentsLayer - } -} - -extension CALayer { - - func setDebuggingState(visible: Bool) { - - var sublayers = self.sublayers - if let cust = self as? CustomLayerDebugging { - sublayers = cust.layerForDebugging().sublayers - } - - if let sublayers = sublayers { - for i in 0.. LayerDebugStyle { - let colorSpace = CGColorSpaceCreateDeviceRGB() - - let anchorColor = CGColor(colorSpace: colorSpace, components: [1, 0, 0, 1])! - let boundsColor = CGColor(colorSpace: colorSpace, components: [1, 1, 0, 1])! - return LayerDebugStyle( - anchorColor: anchorColor, - boundsColor: boundsColor, - anchorWidth: 10, - boundsWidth: 2) - } - - static func topLayerStyle() -> LayerDebugStyle { - let colorSpace = CGColorSpaceCreateDeviceRGB() - let anchorColor = CGColor(colorSpace: colorSpace, components: [1, 0.5, 0, 0])! - let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! - - return LayerDebugStyle( - anchorColor: anchorColor, - boundsColor: boundsColor, - anchorWidth: 10, - boundsWidth: 2) - } - - static func nullLayerStyle() -> LayerDebugStyle { - let colorSpace = CGColorSpaceCreateDeviceRGB() - let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 0, 1, 0])! - let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! - - return LayerDebugStyle( - anchorColor: anchorColor, - boundsColor: boundsColor, - anchorWidth: 10, - boundsWidth: 2) - } - - static func shapeLayerStyle() -> LayerDebugStyle { - let colorSpace = CGColorSpaceCreateDeviceRGB() - let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 0])! - let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! - - return LayerDebugStyle( - anchorColor: anchorColor, - boundsColor: boundsColor, - anchorWidth: 10, - boundsWidth: 2) - } - - static func shapeRenderLayerStyle() -> LayerDebugStyle { - let colorSpace = CGColorSpaceCreateDeviceRGB() - let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 1, 1, 0])! - let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])! - - return LayerDebugStyle( - anchorColor: anchorColor, - boundsColor: boundsColor, - anchorWidth: 10, - boundsWidth: 2) - } -} - -extension Array where Element == LayerModel { - - var parents: [Int] { - var array = [Int]() - for layer in self { - if let parent = layer.parent { - array.append(parent) - } else { - array.append(-1) - } - } - return array - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift deleted file mode 100644 index 32a09e73..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +++ /dev/null @@ -1,268 +0,0 @@ -// -// KeypathSearchableExtension.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -import QuartzCore - -extension KeypathSearchable { - - func animatorNodes(for keyPath: AnimationKeypath) -> [AnimatorNode]? { - // Make sure there is a current key path. - guard let currentKey = keyPath.currentKey else { return nil } - - // Now try popping the keypath for wildcard / child search - guard let nextKeypath = keyPath.popKey(keypathName) else { - // We may be on the final keypath. Check for match. - if - let node = self as? AnimatorNode, - currentKey.equalsKeypath(keypathName) - { - // This is the final keypath and matches self. Return.s - return [node] - } - /// Nope. Stop Search - return nil - } - - var results: [AnimatorNode] = [] - - if - let node = self as? AnimatorNode, - nextKeypath.currentKey == nil - { - // Keypath matched self and was the final keypath. - results.append(node) - } - - for childNode in childKeypaths { - // Check if the child has any nodes matching the next keypath. - if let foundNodes = childNode.animatorNodes(for: nextKeypath) { - results.append(contentsOf: foundNodes) - } - - // In this case the current key is fuzzy, and both child and self match the next keyname. Keep digging! - if - currentKey.keyPathType == .fuzzyWildcard, - let nextKeypath = keyPath.nextKeypath, - nextKeypath.equalsKeypath(childNode.keypathName), - let foundNodes = childNode.animatorNodes(for: keyPath) - { - results.append(contentsOf: foundNodes) - } - } - - guard results.count > 0 else { - return nil - } - - return results - } - - func nodeProperties(for keyPath: AnimationKeypath) -> [AnyNodeProperty]? { - guard let nextKeypath = keyPath.popKey(keypathName) else { - /// Nope. Stop Search - return nil - } - - /// Keypath matches in some way. Continue the search. - var results: [AnyNodeProperty] = [] - - /// Check if we have a property keypath yet - if - let propertyKey = nextKeypath.propertyKey, - let property = keypathProperties[propertyKey] - { - /// We found a property! - results.append(property) - } - - if nextKeypath.nextKeypath != nil { - /// Now check child keypaths. - for child in childKeypaths { - if let childProperties = child.nodeProperties(for: nextKeypath) { - results.append(contentsOf: childProperties) - } - } - } - - guard results.count > 0 else { - return nil - } - - return results - } - - func layer(for keyPath: AnimationKeypath) -> CALayer? { - if keyPath.nextKeypath == nil, let layerKey = keyPath.currentKey, layerKey.equalsKeypath(keypathName) { - /// We found our layer! - return keypathLayer - } - guard let nextKeypath = keyPath.popKey(keypathName) else { - /// Nope. Stop Search - return nil - } - - if nextKeypath.nextKeypath != nil { - /// Now check child keypaths. - for child in childKeypaths { - if let layer = child.layer(for: keyPath) { - return layer - } - } - } - return nil - } - - func logKeypaths(for keyPath: AnimationKeypath?) { - let newKeypath: AnimationKeypath - if let previousKeypath = keyPath { - newKeypath = previousKeypath.appendingKey(keypathName) - } else { - newKeypath = AnimationKeypath(keys: [keypathName]) - } - print(newKeypath.fullPath) - for key in keypathProperties.keys { - print(newKeypath.appendingKey(key).fullPath) - } - for child in childKeypaths { - child.logKeypaths(for: newKeypath) - } - } -} - -extension AnimationKeypath { - var currentKey: String? { - keys.first - } - - var nextKeypath: String? { - guard keys.count > 1 else { - return nil - } - return keys[1] - } - - var propertyKey: String? { - if nextKeypath == nil { - /// There are no more keypaths. This is a property key. - return currentKey - } - if keys.count == 2, currentKey?.keyPathType == .fuzzyWildcard { - /// The next keypath is the last and the current is a fuzzy key. - return nextKeypath - } - return nil - } - - var fullPath: String { - keys.joined(separator: ".") - } - - // Pops the top keypath from the stack if the keyname matches. - func popKey(_ keyname: String) -> AnimationKeypath? { - guard - let currentKey = currentKey, - currentKey.equalsKeypath(keyname), - keys.count > 1 else - { - // Current key either doesnt match or we are on the last key. - return nil - } - - // Pop the keypath from the stack and return the new stack. - let newKeys: [String] - - if currentKey.keyPathType == .fuzzyWildcard { - /// Dont remove if current key is a fuzzy wildcard, and if the next keypath doesnt equal keypathname - if - let nextKeypath = nextKeypath, - nextKeypath.equalsKeypath(keyname) - { - /// Remove next two keypaths. This keypath breaks the wildcard. - var oldKeys = keys - oldKeys.remove(at: 0) - oldKeys.remove(at: 0) - newKeys = oldKeys - } else { - newKeys = keys - } - } else { - var oldKeys = keys - oldKeys.remove(at: 0) - newKeys = oldKeys - } - - return AnimationKeypath(keys: newKeys) - } - - func appendingKey(_ key: String) -> AnimationKeypath { - var newKeys = keys - newKeys.append(key) - return AnimationKeypath(keys: newKeys) - } -} - -extension String { - var keyPathType: KeyType { - switch self { - case "*": - return .wildcard - case "**": - return .fuzzyWildcard - default: - return .specific - } - } - - func equalsKeypath(_ keyname: String) -> Bool { - if keyPathType == .wildcard || keyPathType == .fuzzyWildcard { - return true - } - if self == keyname { - return true - } - if let index = firstIndex(of: "*") { - // Wildcard search. - let prefix = String(self.prefix(upTo: index)) - let suffix = String(self.suffix(from: self.index(after: index))) - - if prefix.count > 0 { - // Match prefix. - if keyname.count < prefix.count { - return false - } - let testPrefix = String(keyname.prefix(upTo: keyname.index(keyname.startIndex, offsetBy: prefix.count))) - if testPrefix != prefix { - // Prefix doesnt match - return false - } - } - if suffix.count > 0 { - // Match suffix. - if keyname.count < suffix.count { - // Suffix doesnt match - return false - } - let index = keyname.index(keyname.endIndex, offsetBy: -suffix.count) - let testSuffix = String(keyname.suffix(from: index)) - if testSuffix != suffix { - return false - } - } - return true - } - return false - } -} - -// MARK: - KeyType - -enum KeyType { - case specific - case wildcard - case fuzzyWildcard -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift deleted file mode 100644 index 015a10ce..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/CGFloatExtensions.swift +++ /dev/null @@ -1,152 +0,0 @@ -// -// CGFloatExtensions.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/14/19. -// - -import Foundation -import QuartzCore - -extension CGFloat { - - // MARK: Internal - - var squared: CGFloat { - self * self - } - - var cubed: CGFloat { - self * self * self - } - - var cubicRoot: CGFloat { - CGFloat(pow(Double(self), 1.0 / 3.0)) - } - - func isInRangeOrEqual(_ from: CGFloat, _ to: CGFloat) -> Bool { - from <= self && self <= to - } - - func isInRange(_ from: CGFloat, _ to: CGFloat) -> Bool { - from < self && self < to - } - - func cubicBezierInterpolate(_ P0: CGPoint, _ P1: CGPoint, _ P2: CGPoint, _ P3: CGPoint) -> CGFloat { - var t: CGFloat - if self == P0.x { - // Handle corner cases explicitly to prevent rounding errors - t = 0 - } else if self == P3.x { - t = 1 - } else { - // Calculate t - let a = -P0.x + 3 * P1.x - 3 * P2.x + P3.x; - let b = 3 * P0.x - 6 * P1.x + 3 * P2.x; - let c = -3 * P0.x + 3 * P1.x; - let d = P0.x - self; - let tTemp = CGFloat.SolveCubic(a, b, c, d); - if tTemp == -1 { - return -1; - } - t = tTemp - } - - // Calculate y from t - return (1 - t).cubed * P0.y + 3 * t * (1 - t).squared * P1.y + 3 * t.squared * (1 - t) * P2.y + t.cubed * P3.y; - } - - func cubicBezier(_ t: CGFloat, _ c1: CGFloat, _ c2: CGFloat, _ end: CGFloat) -> CGFloat { - let t_ = (1.0 - t) - let tt_ = t_ * t_ - let ttt_ = t_ * t_ * t_ - let tt = t * t - let ttt = t * t * t - - return self * ttt_ - + 3.0 * c1 * tt_ * t - + 3.0 * c2 * t_ * tt - + end * ttt; - } - - // MARK: Fileprivate - - fileprivate static func SolveQuadratic(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat) -> CGFloat { - var result = (-b + sqrt(b.squared - 4 * a * c)) / (2 * a); - guard !result.isInRangeOrEqual(0, 1) else { - return result - } - - result = (-b - sqrt(b.squared - 4 * a * c)) / (2 * a); - guard !result.isInRangeOrEqual(0, 1) else { - return result - } - - return -1; - } - - fileprivate static func SolveCubic(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat, _ d: CGFloat) -> CGFloat { - if a == 0 { - return SolveQuadratic(b, c, d) - } - if d == 0 { - return 0 - } - let a = a - var b = b - var c = c - var d = d - b /= a - c /= a - d /= a - var q = (3.0 * c - b.squared) / 9.0 - let r = (-27.0 * d + b * (9.0 * c - 2.0 * b.squared)) / 54.0 - let disc = q.cubed + r.squared - let term1 = b / 3.0 - - if disc > 0 { - var s = r + sqrt(disc) - s = (s < 0) ? -((-s).cubicRoot) : s.cubicRoot - var t = r - sqrt(disc) - t = (t < 0) ? -((-t).cubicRoot) : t.cubicRoot - - let result = -term1 + s + t; - if result.isInRangeOrEqual(0, 1) { - return result - } - } else if disc == 0 { - let r13 = (r < 0) ? -((-r).cubicRoot) : r.cubicRoot; - - var result = -term1 + 2.0 * r13; - if result.isInRangeOrEqual(0, 1) { - return result - } - - result = -(r13 + term1); - if result.isInRangeOrEqual(0, 1) { - return result - } - - } else { - q = -q; - var dum1 = q * q * q; - dum1 = acos(r / sqrt(dum1)); - let r13 = 2.0 * sqrt(q); - - var result = -term1 + r13 * cos(dum1 / 3.0); - if result.isInRangeOrEqual(0, 1) { - return result - } - result = -term1 + r13 * cos((dum1 + 2.0 * .pi) / 3.0); - if result.isInRangeOrEqual(0, 1) { - return result - } - result = -term1 + r13 * cos((dum1 + 4.0 * .pi) / 3.0); - if result.isInRangeOrEqual(0, 1) { - return result - } - } - - return -1; - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift deleted file mode 100644 index 8dea758e..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/MathKit.swift +++ /dev/null @@ -1,579 +0,0 @@ -// -// MathKit.swift -// UIToolBox -// -// Created by Brandon Withrow on 10/10/18. -// -// From https://github.com/buba447/UIToolBox - -import CoreGraphics -import Foundation - -extension Int { - var cgFloat: CGFloat { - CGFloat(self) - } -} - -extension Double { - var cgFloat: CGFloat { - CGFloat(self) - } -} - -// MARK: - CGFloat + Interpolatable - -extension CGFloat: Interpolatable { - - /** - Interpolates the receiver to the given number by Amount. - - Parameter toNumber: The number to interpolate to. - - Parameter amount: The amount to interpolate from 0-1 - - ``` - let number = 5 - let interpolated = number.interpolateTo(10, amount: 0.5) - print(interpolated) - // Result: 7.5 - ``` - - 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. - */ - func interpolateTo(_ to: CGFloat, amount: CGFloat) -> CGFloat { - self + ((to - self) * CGFloat(amount)) - } - - func interpolateTo(_ to: CGFloat, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> CGFloat { - interpolateTo(to, amount: amount) - } - - func remap(fromLow: CGFloat, fromHigh: CGFloat, toLow: CGFloat, toHigh: CGFloat) -> CGFloat { - guard (fromHigh - fromLow) != 0 else { - // Would produce NAN - return 0 - } - return toLow + (self - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) - } - - /** - Returns a value that is clamped between the two numbers - - 1. The order of arguments does not matter. - */ - func clamp(_ a: CGFloat, _ b: CGFloat) -> CGFloat { - CGFloat(Double(self).clamp(Double(a), Double(b))) - } - - /** - Returns the difference between the receiver and the given number. - - Parameter absolute: If *true* (Default) the returned value will always be positive. - */ - func diff(_ a: CGFloat, absolute: Bool = true) -> CGFloat { - absolute ? abs(a - self) : a - self - } - - func toRadians() -> CGFloat { self * .pi / 180 } - func toDegrees() -> CGFloat { self * 180 / .pi } - -} - -// MARK: - Double + Interpolatable - -extension Double: Interpolatable { - - /** - Interpolates the receiver to the given number by Amount. - - Parameter toNumber: The number to interpolate to. - - Parameter amount: The amount to interpolate from 0-1 - - ``` - let number = 5 - let interpolated = number.interpolateTo(10, amount: 0.5) - print(interpolated) - // Result: 7.5 - ``` - - 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. - */ - func interpolateTo(_ to: Double, amount: CGFloat) -> Double { - self + ((to - self) * Double(amount)) - } - - func interpolateTo(_ to: Double, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> Double { - interpolateTo(to, amount: amount) - } - - func remap(fromLow: Double, fromHigh: Double, toLow: Double, toHigh: Double) -> Double { - toLow + (self - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) - } - - /** - Returns a value that is clamped between the two numbers - - 1. The order of arguments does not matter. - */ - func clamp(_ a: Double, _ b: Double) -> Double { - let minValue = a <= b ? a : b - let maxValue = a <= b ? b : a - return max(min(self, maxValue), minValue) - } - -} - -extension CGRect { - - // MARK: Lifecycle - - /// Initializes a new CGRect with a center point and size. - init(center: CGPoint, size: CGSize) { - self.init( - x: center.x - (size.width * 0.5), - y: center.y - (size.height * 0.5), - width: size.width, - height: size.height) - } - - // MARK: Internal - - /// Returns the total area of the rect. - var area: CGFloat { - width * height - } - - /// The center point of the rect. Settable. - var center: CGPoint { - get { - CGPoint(x: midX, y: midY) - } - set { - origin = CGPoint( - x: newValue.x - (size.width * 0.5), - y: newValue.y - (size.height * 0.5)) - } - } - - /// The top left point of the rect. Settable. - var topLeft: CGPoint { - get { - CGPoint(x: minX, y: minY) - } - set { - origin = CGPoint( - x: newValue.x, - y: newValue.y) - } - } - - /// The bottom left point of the rect. Settable. - var bottomLeft: CGPoint { - get { - CGPoint(x: minX, y: maxY) - } - set { - origin = CGPoint( - x: newValue.x, - y: newValue.y - size.height) - } - } - - /// The top right point of the rect. Settable. - var topRight: CGPoint { - get { - CGPoint(x: maxX, y: minY) - } - set { - origin = CGPoint( - x: newValue.x - size.width, - y: newValue.y) - } - } - - /// The bottom right point of the rect. Settable. - var bottomRight: CGPoint { - get { - CGPoint(x: maxX, y: maxY) - } - set { - origin = CGPoint( - x: newValue.x - size.width, - y: newValue.y - size.height) - } - } - - /** - Interpolates the receiver to the given rect by Amount. - - Parameter to: The rect to interpolate to. - - Parameter amount: The amount to interpolate from 0-1 - - ``` - let rect = CGRect(x:0, y:0, width: 50, height: 50) - let interpolated = rect.interpolateTo(CGRect(x:100, y:100, width: 100, height: 100), amount: 0.5) - print(interpolated) - // Result: (x: 50, y: 50, width: 75, height: 75) - ``` - - 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. - */ - func interpolateTo(_ to: CGRect, amount: CGFloat) -> CGRect { - CGRect( - x: origin.x.interpolateTo(to.origin.x, amount: amount), - y: origin.y.interpolateTo(to.origin.y, amount: amount), - width: width.interpolateTo(to.width, amount: amount), - height: height.interpolateTo(to.height, amount: amount)) - } - -} - -extension CGSize { - - /// Operator convenience to add sizes with + - static func +(left: CGSize, right: CGSize) -> CGSize { - left.add(right) - } - - /// Operator convenience to subtract sizes with - - static func -(left: CGSize, right: CGSize) -> CGSize { - left.subtract(right) - } - - /// Operator convenience to multiply sizes with * - static func *(left: CGSize, right: CGFloat) -> CGSize { - CGSize(width: left.width * right, height: left.height * right) - } - - /** - Interpolates the receiver to the given size by Amount. - - Parameter to: The size to interpolate to. - - Parameter amount: The amount to interpolate from 0-1 - - ``` - let size = CGSize(width: 50, height: 50) - let interpolated = rect.interpolateTo(CGSize(width: 100, height: 100), amount: 0.5) - print(interpolated) - // Result: (width: 75, height: 75) - ``` - - 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. - */ - func interpolateTo(_ to: CGSize, amount: CGFloat) -> CGSize { - CGSize( - width: width.interpolateTo(to.width, amount: amount), - height: height.interpolateTo(to.height, amount: amount)) - } - - /// Returns the scale float that will fit the receive inside of the given size. - func scaleThatFits(_ size: CGSize) -> CGFloat { - CGFloat.minimum(width / size.width, height / size.height) - } - - /// Adds receiver size to give size. - func add(_ size: CGSize) -> CGSize { - CGSize(width: width + size.width, height: height + size.height) - } - - /// Subtracts given size from receiver size. - func subtract(_ size: CGSize) -> CGSize { - CGSize(width: width - size.width, height: height - size.height) - } - - /// Multiplies receiver size by the given size. - func multiply(_ size: CGSize) -> CGSize { - CGSize(width: width * size.width, height: height * size.height) - } -} - -// MARK: - CGLine - -/// A struct that defines a line segment with two CGPoints -struct CGLine { - - // MARK: Lifecycle - - /// Initializes a line segment with start and end points - init(start: CGPoint, end: CGPoint) { - self.start = start - self.end = end - } - - // MARK: Internal - - /// The Start of the line segment. - var start: CGPoint - /// The End of the line segment. - var end: CGPoint - - /// The length of the line segment. - var length: CGFloat { - end.distanceTo(start) - } - - /// Returns a line segment that is normalized to a length of 1 - func normalize() -> CGLine { - let len = length - guard len > 0 else { - return self - } - let relativeEnd = end - start - let relativeVector = CGPoint(x: relativeEnd.x / len, y: relativeEnd.y / len) - let absoluteVector = relativeVector + start - return CGLine(start: start, end: absoluteVector) - } - - /// Trims a line segment to the given length - func trimmedToLength(_ toLength: CGFloat) -> CGLine { - let len = length - guard len > 0 else { - return self - } - let relativeEnd = end - start - let relativeVector = CGPoint(x: relativeEnd.x / len, y: relativeEnd.y / len) - let sizedVector = CGPoint(x: relativeVector.x * toLength, y: relativeVector.y * toLength) - let absoluteVector = sizedVector + start - return CGLine(start: start, end: absoluteVector) - } - - /// Flips a line vertically and horizontally from the start point. - func flipped() -> CGLine { - let relativeEnd = end - start - let flippedEnd = CGPoint(x: relativeEnd.x * -1, y: relativeEnd.y * -1) - return CGLine(start: start, end: flippedEnd + start) - } - - /// Move the line to the new start point. - func transpose(_ toPoint: CGPoint) -> CGLine { - let diff = toPoint - start - let newEnd = end + diff - return CGLine(start: toPoint, end: newEnd) - } - -} - -infix operator +| -infix operator +- - -// MARK: - CGPoint + Interpolatable - -extension CGPoint: Interpolatable { - - /// Returns the length between the receiver and *CGPoint.zero* - var vectorLength: CGFloat { - distanceTo(.zero) - } - - var isZero: Bool { - x == 0 && y == 0 - } - - /// Operator convenience to divide points with / - static func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint { - CGPoint(x: lhs.x / CGFloat(rhs), y: lhs.y / CGFloat(rhs)) - } - - /// Operator convenience to multiply points with * - static func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint { - CGPoint(x: lhs.x * CGFloat(rhs), y: lhs.y * CGFloat(rhs)) - } - - /// Operator convenience to add points with + - static func +(left: CGPoint, right: CGPoint) -> CGPoint { - left.add(right) - } - - /// Operator convenience to subtract points with - - static func -(left: CGPoint, right: CGPoint) -> CGPoint { - left.subtract(right) - } - - static func +|(left: CGPoint, right: CGFloat) -> CGPoint { - CGPoint(x: left.x, y: left.y + right) - } - - static func +-(left: CGPoint, right: CGFloat) -> CGPoint { - CGPoint(x: left.x + right, y: left.y) - } - - /// Returns the distance between the receiver and the given point. - func distanceTo(_ a: CGPoint) -> CGFloat { - let xDist = a.x - x - let yDist = a.y - y - return CGFloat(sqrt((xDist * xDist) + (yDist * yDist))) - } - - func rounded(decimal: CGFloat) -> CGPoint { - CGPoint(x: round(decimal * x) / decimal, y: round(decimal * y) / decimal) - } - - /** - Interpolates the receiver to the given Point by Amount. - - Parameter to: The Point to interpolate to. - - Parameter amount: The amount to interpolate from 0-1 - - ``` - let point = CGPoint(width: 50, height: 50) - let interpolated = rect.interpolateTo(CGPoint(width: 100, height: 100), amount: 0.5) - print(interpolated) - // Result: (x: 75, y: 75) - ``` - - 1. The amount can be greater than one and less than zero. The interpolation will not be clipped. - */ - - func interpolate(_ to: CGPoint, amount: CGFloat) -> CGPoint { - CGPoint( - x: x.interpolateTo(to.x, amount: amount), - y: y.interpolateTo(to.y, amount: amount)) - } - - func interpolate( - _ to: CGPoint, - outTangent: CGPoint, - inTangent: CGPoint, - amount: CGFloat, - maxIterations: Int = 3, - samples: Int = 20, - accuracy: CGFloat = 1) - -> CGPoint - { - if amount == 0 { - return self - } - if amount == 1 { - return to - } - - if - colinear(outTangent, inTangent) == true, - outTangent.colinear(inTangent, to) == true - { - return interpolate(to, amount: amount) - } - - let step = 1 / CGFloat(samples) - - var points: [(point: CGPoint, distance: CGFloat)] = [(point: self, distance: 0)] - var totalLength: CGFloat = 0 - - var previousPoint = self - var previousAmount = CGFloat(0) - - var closestPoint: Int = 0 - - while previousAmount < 1 { - - previousAmount = previousAmount + step - - if previousAmount < amount { - closestPoint = closestPoint + 1 - } - - let newPoint = pointOnPath(to, outTangent: outTangent, inTangent: inTangent, amount: previousAmount) - let distance = previousPoint.distanceTo(newPoint) - totalLength = totalLength + distance - points.append((point: newPoint, distance: totalLength)) - previousPoint = newPoint - } - - let accurateDistance = amount * totalLength - var point = points[closestPoint] - - var foundPoint: Bool = false - - var pointAmount = CGFloat(closestPoint) * step - var nextPointAmount: CGFloat = pointAmount + step - - var refineIterations = 0 - while foundPoint == false { - refineIterations = refineIterations + 1 - /// First see if the next point is still less than the projected length. - let nextPoint = points[closestPoint + 1] - if nextPoint.distance < accurateDistance { - point = nextPoint - closestPoint = closestPoint + 1 - pointAmount = CGFloat(closestPoint) * step - nextPointAmount = pointAmount + step - if closestPoint == points.count { - foundPoint = true - } - continue - } - if accurateDistance < point.distance { - closestPoint = closestPoint - 1 - if closestPoint < 0 { - foundPoint = true - continue - } - point = points[closestPoint] - pointAmount = CGFloat(closestPoint) * step - nextPointAmount = pointAmount + step - continue - } - - /// Now we are certain the point is the closest point under the distance - let pointDiff = nextPoint.distance - point.distance - let proposedPointAmount = ((accurateDistance - point.distance) / pointDiff) - .remap(fromLow: 0, fromHigh: 1, toLow: pointAmount, toHigh: nextPointAmount) - - let newPoint = pointOnPath(to, outTangent: outTangent, inTangent: inTangent, amount: proposedPointAmount) - let newDistance = point.distance + point.point.distanceTo(newPoint) - pointAmount = proposedPointAmount - point = (point: newPoint, distance: newDistance) - if - accurateDistance - newDistance <= accuracy || - newDistance - accurateDistance <= accuracy - { - foundPoint = true - } - - if refineIterations == maxIterations { - foundPoint = true - } - } - return point.point - } - - func pointOnPath(_ to: CGPoint, outTangent: CGPoint, inTangent: CGPoint, amount: CGFloat) -> CGPoint { - let a = interpolate(outTangent, amount: amount) - let b = outTangent.interpolate(inTangent, amount: amount) - let c = inTangent.interpolate(to, amount: amount) - let d = a.interpolate(b, amount: amount) - let e = b.interpolate(c, amount: amount) - let f = d.interpolate(e, amount: amount) - return f - } - - func colinear(_ a: CGPoint, _ b: CGPoint) -> Bool { - let area = x * (a.y - b.y) + a.x * (b.y - y) + b.x * (y - a.y); - let accuracy: CGFloat = 0.05 - if area < accuracy && area > -accuracy { - return true - } - return false - } - - func interpolateTo(_ to: CGPoint, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> CGPoint { - guard - let outTan = spatialOutTangent, - let inTan = spatialInTangent else - { - return interpolate(to, amount: amount) - } - let cp1 = self + outTan - let cp2 = to + inTan - - return interpolate(to, outTangent: cp1, inTangent: cp2, amount: amount) - } - - /// Subtracts the given point from the receiving point. - func subtract(_ point: CGPoint) -> CGPoint { - CGPoint( - x: x - point.x, - y: y - point.y) - } - - /// Adds the given point from the receiving point. - func add(_ point: CGPoint) -> CGPoint { - CGPoint( - x: x + point.x, - y: y + point.y) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift deleted file mode 100644 index 800d4d68..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Extensions/StringExtensions.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// StringExtensions.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import CoreGraphics -import Foundation - -extension String { - - func hexColorComponents() -> (red: CGFloat, green: CGFloat, blue: CGFloat) { - - var cString: String = trimmingCharacters(in: .whitespacesAndNewlines).uppercased() - - if cString.hasPrefix("#") { - cString.remove(at: cString.startIndex) - } - - if (cString.count) != 6 { - return (red: 0, green: 0, blue: 0) - } - - var rgbValue: UInt64 = 0 - Scanner(string: cString).scanHexInt64(&rgbValue) - - return ( - red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, - green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, - blue: CGFloat(rgbValue & 0x0000FF) / 255.0) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift deleted file mode 100644 index cb99d13b..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Helpers/AnimationContext.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// AnimationContext.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/1/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -/// A completion block for animations. `true` is passed in if the animation completed playing. -public typealias LottieCompletionBlock = (Bool) -> Void - -// MARK: - AnimationContext - -struct AnimationContext { - - init( - playFrom: CGFloat, - playTo: CGFloat, - closure: LottieCompletionBlock?) - { - self.playTo = playTo - self.playFrom = playFrom - self.closure = AnimationCompletionDelegate(completionBlock: closure) - } - - var playFrom: CGFloat - var playTo: CGFloat - var closure: AnimationCompletionDelegate - -} - -// MARK: - AnimationContextState - -enum AnimationContextState { - case playing - case cancelled - case complete -} - -// MARK: - AnimationCompletionDelegate - -class AnimationCompletionDelegate: NSObject, CAAnimationDelegate { - - // MARK: Lifecycle - - init(completionBlock: LottieCompletionBlock?) { - self.completionBlock = completionBlock - super.init() - } - - // MARK: Public - - public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { - guard ignoreDelegate == false else { return } - animationState = flag ? .complete : .cancelled - if let animationLayer = animationLayer, let key = animationKey { - animationLayer.removeAnimation(forKey: key) - if flag { - animationLayer.currentFrame = (anim as! CABasicAnimation).toValue as! CGFloat - } - } - if let completionBlock = completionBlock { - completionBlock(flag) - } - } - - // MARK: Internal - - var animationLayer: AnimationContainer? - var animationKey: String? - var ignoreDelegate: Bool = false - var animationState: AnimationContextState = .playing - - let completionBlock: LottieCompletionBlock? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift deleted file mode 100644 index 1929b99c..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/Interpolatable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Interpolatable.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/14/19. -// - -import CoreGraphics -import Foundation - -protocol Interpolatable { - - func interpolateTo( - _ to: Self, - amount: CGFloat, - spatialOutTangent: CGPoint?, - spatialInTangent: CGPoint?) -> Self - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift deleted file mode 100644 index fc48c329..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +++ /dev/null @@ -1,217 +0,0 @@ -// -// InterpolatableExtensions.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/14/19. -// - -import CoreGraphics -import Foundation - -// MARK: - Vector1D + Interpolatable - -extension Vector1D: Interpolatable { - func interpolateTo(_ to: Vector1D, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> Vector1D { - value.interpolateTo(to.value, amount: amount).vectorValue - } -} - -// MARK: - Vector2D + Interpolatable - -extension Vector2D: Interpolatable { - func interpolateTo(_ to: Vector2D, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> Vector2D { - pointValue.interpolateTo( - to.pointValue, - amount: CGFloat(amount), - spatialOutTangent: spatialOutTangent, - spatialInTangent: spatialInTangent).vector2dValue - } - -} - -// MARK: - Vector3D + Interpolatable - -extension Vector3D: Interpolatable { - func interpolateTo(_ to: Vector3D, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> Vector3D { - if spatialInTangent != nil || spatialOutTangent != nil { - // TODO Support third dimension spatial interpolation - let point = pointValue.interpolateTo( - to.pointValue, - amount: amount, - spatialOutTangent: spatialOutTangent, - spatialInTangent: spatialInTangent) - return Vector3D( - x: point.x, - y: point.y, - z: CGFloat(z.interpolateTo(to.z, amount: amount))) - } - return Vector3D( - x: x.interpolateTo(to.x, amount: amount), - y: y.interpolateTo(to.y, amount: amount), - z: z.interpolateTo(to.z, amount: amount)) - } -} - -// MARK: - Color + Interpolatable - -extension Color: Interpolatable { - - // MARK: Lifecycle - - /// Initialize a new color with Hue Saturation and Value - init(h: Double, s: Double, v: Double, a: Double) { - - let i = floor(h * 6) - let f = h * 6 - i - let p = v * (1 - s); - let q = v * (1 - f * s) - let t = v * (1 - (1 - f) * s) - - switch i.truncatingRemainder(dividingBy: 6) { - case 0: - r = v - g = t - b = p - case 1: - r = q - g = v - b = p - case 2: - r = p - g = v - b = t - case 3: - r = p - g = q - b = v - case 4: - r = t - g = p - b = v - case 5: - r = v - g = p - b = q - default: - r = 0 - g = 0 - b = 0 - } - self.a = a - } - - init(y: Double, u: Double, v: Double, a: Double) { - // From https://www.fourcc.org/fccyvrgb.php - r = y + 1.403 * v - g = y - 0.344 * u - b = y + 1.770 * u - self.a = a - } - - // MARK: Internal - - /// Hue Saturation Value of the color. - var hsva: (h: Double, s: Double, v: Double, a: Double) { - let maxValue = max(r, g, b) - let minValue = min(r, g, b) - - var h: Double, s: Double, v: Double = maxValue - - let d = maxValue - minValue - s = maxValue == 0 ? 0 : d / maxValue; - - if maxValue == minValue { - h = 0; // achromatic - } else { - switch maxValue { - case r: h = (g - b) / d + (g < b ? 6 : 0) - case g: h = (b - r) / d + 2 - case b: h = (r - g) / d + 4 - default: h = maxValue - } - h = h / 6 - } - return (h: h, s: s, v: v, a: a) - } - - var yuv: (y: Double, u: Double, v: Double, a: Double) { - /// From https://www.fourcc.org/fccyvrgb.php - let y = 0.299 * r + 0.587 * g + 0.114 * b - let u = -0.14713 * r - 0.28886 * g + 0.436 * b - let v = 0.615 * r - 0.51499 * g - 0.10001 * b - return (y: y, u: u, v: v, a: a) - } - - func interpolateTo(_ to: Color, amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> Color { - Color( - r: r.interpolateTo(to.r, amount: amount), - g: g.interpolateTo(to.g, amount: amount), - b: b.interpolateTo(to.b, amount: amount), - a: a.interpolateTo(to.a, amount: amount)) - } -} - -// MARK: - CurveVertex + Interpolatable - -extension CurveVertex: Interpolatable { - func interpolateTo( - _ to: CurveVertex, - amount: CGFloat, - spatialOutTangent _: CGPoint?, - spatialInTangent _: CGPoint?) - -> CurveVertex - { - CurveVertex( - point: point.interpolate(to.point, amount: amount), - inTangent: inTangent.interpolate(to.inTangent, amount: amount), - outTangent: outTangent.interpolate(to.outTangent, amount: amount)) - } -} - -// MARK: - BezierPath + Interpolatable - -extension BezierPath: Interpolatable { - func interpolateTo(_ to: BezierPath, amount: CGFloat, spatialOutTangent: CGPoint?, spatialInTangent: CGPoint?) -> BezierPath { - var newPath = BezierPath() - for i in 0.. TextDocument - { - if amount == 1 { - return to - } - return self - } -} - -// MARK: - Array + Interpolatable - -extension Array: Interpolatable where Element == Double { - func interpolateTo(_ to: [Element], amount: CGFloat, spatialOutTangent _: CGPoint?, spatialInTangent _: CGPoint?) -> [Element] { - var returnArray = [Double]() - for i in 0.. CGFloat { - let startTime = time - let endTime = to.time - if keyTime <= startTime { - return 0 - } - if endTime <= keyTime { - return 1 - } - - if isHold { - return 0 - } - - let outTanPoint = outTangent?.pointValue ?? .zero - let inTanPoint = to.inTangent?.pointValue ?? CGPoint(x: 1, y: 1) - var progress: CGFloat = keyTime.remap(fromLow: startTime, fromHigh: endTime, toLow: 0, toHigh: 1) - if !outTanPoint.isZero || !inTanPoint.equalTo(CGPoint(x: 1, y: 1)) { - /// Cubic interpolation - progress = progress.cubicBezierInterpolate(.zero, outTanPoint, inTanPoint, CGPoint(x: 1, y: 1)) - } - return progress - } - - /// Interpolates the keyframes' by a progress from 0-1 - func interpolate(_ to: Keyframe, progress: CGFloat) -> T { - value.interpolateTo( - to.value, - amount: progress, - spatialOutTangent: spatialOutTangent?.pointValue, - spatialInTangent: to.spatialInTangent?.pointValue) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift deleted file mode 100644 index 5e5d743a..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/BezierPath.swift +++ /dev/null @@ -1,423 +0,0 @@ -// -// Shape.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/8/19. -// - -import CoreGraphics -import Foundation - -// MARK: - BezierPath - -/// A container that holds instructions for creating a single, unbroken Bezier Path. -struct BezierPath { - - // MARK: Lifecycle - - /// Initializes a new Bezier Path. - init(startPoint: CurveVertex) { - elements = [PathElement(vertex: startPoint)] - length = 0 - closed = false - } - - init() { - elements = [] - length = 0 - closed = false - } - - // MARK: Internal - - /// The elements of the path - fileprivate(set) var elements: [PathElement] - - /// If the path is closed or not. - fileprivate(set) var closed: Bool - - /// The total length of the path. - fileprivate(set) var length: CGFloat - - mutating func moveToStartPoint(_ vertex: CurveVertex) { - elements = [PathElement(vertex: vertex)] - length = 0 - } - - mutating func addVertex(_ vertex: CurveVertex) { - guard let previous = elements.last else { - addElement(PathElement(vertex: vertex)) - return - } - addElement(previous.pathElementTo(vertex)) - } - - mutating func addCurve(toPoint: CGPoint, outTangent: CGPoint, inTangent: CGPoint) { - guard let previous = elements.last else { return } - let newVertex = CurveVertex(inTangent, toPoint, toPoint) - updateVertex( - CurveVertex(previous.vertex.inTangent, previous.vertex.point, outTangent), - atIndex: elements.endIndex - 1, - remeasure: false) - addVertex(newVertex) - } - - mutating func addLine(toPoint: CGPoint) { - guard let previous = elements.last else { return } - let newVertex = CurveVertex(point: toPoint, inTangentRelative: .zero, outTangentRelative: .zero) - updateVertex( - CurveVertex(previous.vertex.inTangent, previous.vertex.point, previous.vertex.point), - atIndex: elements.endIndex - 1, - remeasure: false) - addVertex(newVertex) - } - - mutating func close() { - closed = true - } - - mutating func addElement(_ pathElement: PathElement) { - elements.append(pathElement) - length = length + pathElement.length - } - - mutating func updateVertex(_ vertex: CurveVertex, atIndex: Int, remeasure: Bool) { - if remeasure { - var newElement: PathElement - if atIndex > 0 { - let previousElement = elements[atIndex - 1] - newElement = previousElement.pathElementTo(vertex) - } else { - newElement = PathElement(vertex: vertex) - } - elements[atIndex] = newElement - - if atIndex + 1 < elements.count{ - let nextElement = elements[atIndex + 1] - elements[atIndex + 1] = newElement.pathElementTo(nextElement.vertex) - } - - } else { - let oldElement = elements[atIndex] - elements[atIndex] = oldElement.updateVertex(newVertex: vertex) - } - } - - /** - Trims a path fromLength toLength with an offset. - - Length and offset are defined in the length coordinate space. - If any argument is outside the range of this path, then it will be looped over the path from finish to start. - - Cutting the curve when fromLength is less than toLength - x x x x - ~~~~~~~~~~~~~~~ooooooooooooooooooooooooooooooooooooooooooooooooo------------------- - |Offset |fromLength toLength| | - - Cutting the curve when from Length is greater than toLength - x x x x x - oooooooooooooooooo--------------------~~~~~~~~~~~~~~~~ooooooooooooooooooooooooooooo - | toLength| |Offset |fromLength | - - */ - func trim(fromLength: CGFloat, toLength: CGFloat, offsetLength: CGFloat) -> [BezierPath] { - guard elements.count > 1 else { - return [] - } - - if fromLength == toLength { - return [] - } - - /// Normalize lengths to the curve length. - var start = (fromLength + offsetLength).truncatingRemainder(dividingBy: length) - var end = (toLength + offsetLength).truncatingRemainder(dividingBy: length) - - if start < 0 { - start = length + start - } - - if end < 0 { - end = length + end - } - - if start == length { - start = 0 - } - if end == 0 { - end = length - } - - if - start == 0 && end == length || - start == end || - start == length && end == 0 - { - /// The trim encompasses the entire path. Return. - return [self] - } - - if start > end { - // Start is greater than end. Two paths are returned. - return trimPathAtLengths(positions: [(start: 0, end: end), (start: start, end: length)]) - } - - return trimPathAtLengths(positions: [(start: start, end: end)]) - } - - // MARK: Fileprivate - - fileprivate func trimPathAtLengths(positions: [(start: CGFloat, end: CGFloat)]) -> [BezierPath] { - guard positions.count > 0 else { - return [] - } - var remainingPositions = positions - - var trim = remainingPositions.remove(at: 0) - - var paths = [BezierPath]() - - var runningLength: CGFloat = 0 - var finishedTrimming: Bool = false - var pathElements = elements - - var currentPath = BezierPath() - var i: Int = 0 - - while !finishedTrimming { - if pathElements.count <= i { - /// Do this for rounding errors - paths.append(currentPath) - finishedTrimming = true - continue - } - /// Loop through and add elements within start->end range. - /// Get current element - let element = pathElements[i] - - /// Calculate new running length. - let newLength = runningLength + element.length - - if newLength < trim.start { - /// Element is not included in the trim, continue. - runningLength = newLength - i = i + 1 - /// Increment index, we are done with this element. - continue - } - - if newLength == trim.start { - /// Current element IS the start element. - /// For start we want to add a zero length element. - currentPath.moveToStartPoint(element.vertex) - runningLength = newLength - i = i + 1 - /// Increment index, we are done with this element. - continue - } - - if runningLength < trim.start, trim.start < newLength, currentPath.elements.count == 0 { - /// The start of the trim is between this element and the previous, trim. - /// Get previous element. - let previousElement = pathElements[i - 1] - /// Trim it - let trimLength = trim.start - runningLength - let trimResults = element.splitElementAtPosition(fromElement: previousElement, atLength: trimLength) - /// Add the right span start. - currentPath.moveToStartPoint(trimResults.rightSpan.start.vertex) - - pathElements[i] = trimResults.rightSpan.end - pathElements[i - 1] = trimResults.rightSpan.start - runningLength = runningLength + trimResults.leftSpan.end.length - /// Dont increment index or the current length, the end of this path can be within this span. - continue - } - - if trim.start < newLength, newLength < trim.end { - /// Element lies within the trim span. - currentPath.addElement(element) - runningLength = newLength - i = i + 1 - continue - } - - if newLength == trim.end { - /// Element is the end element. - /// The element could have a new length if it's added right after the start node. - currentPath.addElement(element) - /// We are done with this span. - runningLength = newLength - i = i + 1 - /// Allow the path to be finalized. - /// Fall through to finalize path and move to next position - } - - if runningLength < trim.end, trim.end < newLength { - /// New element must be cut for end. - /// Get previous element. - let previousElement = pathElements[i - 1] - /// Trim it - let trimLength = trim.end - runningLength - let trimResults = element.splitElementAtPosition(fromElement: previousElement, atLength: trimLength) - /// Add the left span end. - - currentPath.updateVertex(trimResults.leftSpan.start.vertex, atIndex: currentPath.elements.count - 1, remeasure: false) - currentPath.addElement(trimResults.leftSpan.end) - - pathElements[i] = trimResults.rightSpan.end - pathElements[i - 1] = trimResults.rightSpan.start - runningLength = runningLength + trimResults.leftSpan.end.length - /// Dont increment index or the current length, the start of the next path can be within this span. - /// We are done with this span. - /// Allow the path to be finalized. - /// Fall through to finalize path and move to next position - } - - paths.append(currentPath) - currentPath = BezierPath() - if remainingPositions.count > 0 { - trim = remainingPositions.remove(at: 0) - } else { - finishedTrimming = true - } - } - return paths - } - -} - -// MARK: Codable - -extension BezierPath: Codable { - - // MARK: Lifecycle - - init(from decoder: Decoder) throws { - let container: KeyedDecodingContainer - - if let keyedContainer = try? decoder.container(keyedBy: BezierPath.CodingKeys.self) { - container = keyedContainer - } else { - var unkeyedContainer = try decoder.unkeyedContainer() - container = try unkeyedContainer.nestedContainer(keyedBy: BezierPath.CodingKeys.self) - } - - closed = try container.decodeIfPresent(Bool.self, forKey: .closed) ?? true - - var vertexContainer = try container.nestedUnkeyedContainer(forKey: .vertices) - var inPointsContainer = try container.nestedUnkeyedContainer(forKey: .inPoints) - var outPointsContainer = try container.nestedUnkeyedContainer(forKey: .outPoints) - - guard vertexContainer.count == inPointsContainer.count, inPointsContainer.count == outPointsContainer.count else { - /// Will throw an error if vertex, inpoints, and outpoints are not the same length. - /// This error is to be expected. - throw DecodingError.dataCorruptedError( - forKey: CodingKeys.vertices, - in: container, - debugDescription: "Vertex data does not match In Tangents and Out Tangents") - } - - guard let count = vertexContainer.count, count > 0 else { - length = 0 - elements = [] - return - } - - var decodedElements = [PathElement]() - - /// Create first point - let firstVertex = CurveVertex( - point: try vertexContainer.decode(CGPoint.self), - inTangentRelative: try inPointsContainer.decode(CGPoint.self), - outTangentRelative: try outPointsContainer.decode(CGPoint.self)) - var previousElement = PathElement(vertex: firstVertex) - decodedElements.append(previousElement) - - var totalLength: CGFloat = 0 - while !vertexContainer.isAtEnd { - /// Get the next vertex data. - let vertex = CurveVertex( - point: try vertexContainer.decode(CGPoint.self), - inTangentRelative: try inPointsContainer.decode(CGPoint.self), - outTangentRelative: try outPointsContainer.decode(CGPoint.self)) - let pathElement = previousElement.pathElementTo(vertex) - decodedElements.append(pathElement) - previousElement = pathElement - totalLength = totalLength + pathElement.length - } - if closed { - let closeElement = previousElement.pathElementTo(firstVertex) - decodedElements.append(closeElement) - totalLength = totalLength + closeElement.length - } - length = totalLength - elements = decodedElements - } - - // MARK: Internal - - /** - The BezierPath container is encoded and decoded from the JSON format - that defines points for a lottie animation. - - { - "c" = Bool - "i" = [[Double]], - "o" = [[Double]], - "v" = [[Double]] - } - - */ - - enum CodingKeys: String, CodingKey { - case closed = "c" - case inPoints = "i" - case outPoints = "o" - case vertices = "v" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: BezierPath.CodingKeys.self) - try container.encode(closed, forKey: .closed) - - var vertexContainer = container.nestedUnkeyedContainer(forKey: .vertices) - var inPointsContainer = container.nestedUnkeyedContainer(forKey: .inPoints) - var outPointsContainer = container.nestedUnkeyedContainer(forKey: .outPoints) - - /// If closed path, ignore the final element. - let finalIndex = closed ? elements.endIndex - 1 : elements.endIndex - for i in 0.. CGPath { - let cgPath = CGMutablePath() - - var previousElement: PathElement? - for element in elements { - if let previous = previousElement { - if previous.vertex.outTangentRelative.isZero && element.vertex.inTangentRelative.isZero { - cgPath.addLine(to: element.vertex.point) - } else { - cgPath.addCurve(to: element.vertex.point, control1: previous.vertex.outTangent, control2: element.vertex.inTangent) - } - } else { - cgPath.move(to: element.vertex.point) - } - previousElement = element - } - if closed { - cgPath.closeSubpath() - } - return cgPath - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift deleted file mode 100644 index 7001252b..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/ColorExtension.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// Color.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/14/19. -// - -import CoreGraphics -import Foundation - -// MARK: - Color + Codable - -extension Color: Codable { - - // MARK: Lifecycle - - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - - var r1: Double - if !container.isAtEnd { - r1 = try container.decode(Double.self) - } else { - r1 = 0 - } - - var g1: Double - if !container.isAtEnd { - g1 = try container.decode(Double.self) - } else { - g1 = 0 - } - - var b1: Double - if !container.isAtEnd { - b1 = try container.decode(Double.self) - } else { - b1 = 0 - } - - var a1: Double - if !container.isAtEnd { - a1 = try container.decode(Double.self) - } else { - a1 = 1 - } - if r1 > 1, g1 > 1, b1 > 1, a1 > 1 { - r1 = r1 / 255 - g1 = g1 / 255 - b1 = b1 / 255 - a1 = a1 / 255 - } - r = r1 - g = g1 - b = b1 - a = a1 - } - - // MARK: Public - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(r) - try container.encode(g) - try container.encode(b) - try container.encode(a) - } - -} - -extension Color { - - static var clearColor: CGColor { - CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 0])! - } - - var cgColorValue: CGColor { - // TODO: Fix color spaces - let colorspace = CGColorSpaceCreateDeviceRGB() - return CGColor(colorSpace: colorspace, components: [CGFloat(r), CGFloat(g), CGFloat(b), CGFloat(a)]) ?? Color.clearColor - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift deleted file mode 100644 index f425bf30..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CompoundBezierPath.swift +++ /dev/null @@ -1,169 +0,0 @@ -// -// CompoundBezierPath.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/14/19. -// - -import CoreGraphics -import Foundation - -/** - A collection of BezierPath objects that can be trimmed and added. - - */ -struct CompoundBezierPath { - - // MARK: Lifecycle - - init() { - paths = [] - length = 0 - } - - init(path: BezierPath) { - paths = [path] - length = path.length - } - - init(paths: [BezierPath], length: CGFloat) { - self.paths = paths - self.length = length - } - - init(paths: [BezierPath]) { - self.paths = paths - var l: CGFloat = 0 - for path in paths { - l = l + path.length - } - length = l - } - - // MARK: Internal - - let paths: [BezierPath] - - let length: CGFloat - - func addPath(path: BezierPath) -> CompoundBezierPath { - var newPaths = paths - newPaths.append(path) - return CompoundBezierPath(paths: newPaths, length: length + path.length) - } - - func combine(_ compoundBezier: CompoundBezierPath) -> CompoundBezierPath { - var newPaths = paths - newPaths.append(contentsOf: compoundBezier.paths) - return CompoundBezierPath(paths: newPaths, length: length + compoundBezier.length) - } - - func trim(fromPosition: CGFloat, toPosition: CGFloat, offset: CGFloat, trimSimultaneously: Bool) -> CompoundBezierPath { - if fromPosition == toPosition { - return CompoundBezierPath() - } - - if trimSimultaneously { - /// Trim each path individually. - var newPaths = [BezierPath]() - for path in paths { - newPaths.append(contentsOf: path.trim( - fromLength: fromPosition * path.length, - toLength: toPosition * path.length, - offsetLength: offset * path.length)) - } - return CompoundBezierPath(paths: newPaths) - } - - /// Normalize lengths to the curve length. - var startPosition = (fromPosition + offset).truncatingRemainder(dividingBy: 1) - var endPosition = (toPosition + offset).truncatingRemainder(dividingBy: 1) - - if startPosition < 0 { - startPosition = 1 + startPosition - } - - if endPosition < 0 { - endPosition = 1 + endPosition - } - - if startPosition == 1 { - startPosition = 0 - } - if endPosition == 0 { - endPosition = 1 - } - - if - startPosition == 0 && endPosition == 1 || - startPosition == endPosition || - startPosition == 1 && endPosition == 0 - { - /// The trim encompasses the entire path. Return. - return self - } - - var positions: [(start: CGFloat, end: CGFloat)] - if endPosition < startPosition { - positions = [ - (start: 0, end: endPosition * length), - (start: startPosition * length, end: length), - ] - } else { - positions = [(start: startPosition * length, end: endPosition * length)] - } - - var compoundPath = CompoundBezierPath() - var trim = positions.remove(at: 0) - var pathStartPosition: CGFloat = 0 - - var finishedTrimming: Bool = false - var i: Int = 0 - - while !finishedTrimming { - if paths.count <= i { - /// Rounding errors - finishedTrimming = true - continue - } - let path = paths[i] - - let pathEndPosition = pathStartPosition + path.length - - if pathEndPosition < trim.start { - /// Path is not included in the trim, continue. - pathStartPosition = pathEndPosition - i = i + 1 - continue - - } else if trim.start <= pathStartPosition, pathEndPosition <= trim.end { - /// Full Path is inside of trim. Add full path. - compoundPath = compoundPath.addPath(path: path) - } else { - if - let trimPath = path.trim( - fromLength: trim.start > pathStartPosition ? (trim.start - pathStartPosition) : 0, - toLength: trim.end < pathEndPosition ? (trim.end - pathStartPosition) : path.length, - offsetLength: 0).first - { - compoundPath = compoundPath.addPath(path: trimPath) - } - } - - if trim.end <= pathEndPosition { - /// We are done with the current trim. - /// Advance trim but remain on the same path in case the next trim overlaps it. - if positions.count > 0 { - trim = positions.remove(at: 0) - } else { - finishedTrimming = true - } - } else { - pathStartPosition = pathEndPosition - i = i + 1 - } - } - return compoundPath - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift deleted file mode 100644 index c9f9fbd9..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/CurveVertex.swift +++ /dev/null @@ -1,192 +0,0 @@ -// -// CurveVertex.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/11/19. -// - -import CoreGraphics -import Foundation - -/// A single vertex with an in and out tangent -struct CurveVertex { - - // MARK: Lifecycle - - /// Initializes a curve point with absolute values - init(_ inTangent: CGPoint, _ point: CGPoint, _ outTangent: CGPoint) { - self.point = point - self.inTangent = inTangent - self.outTangent = outTangent - } - - /// Initializes a curve point with relative values - init(point: CGPoint, inTangentRelative: CGPoint, outTangentRelative: CGPoint) { - self.point = point - inTangent = point.add(inTangentRelative) - outTangent = point.add(outTangentRelative) - } - - /// Initializes a curve point with absolute values - init(point: CGPoint, inTangent: CGPoint, outTangent: CGPoint) { - self.point = point - self.inTangent = inTangent - self.outTangent = outTangent - } - - // MARK: Internal - - let point: CGPoint - - let inTangent: CGPoint - let outTangent: CGPoint - - var inTangentRelative: CGPoint { - inTangent.subtract(point) - } - - var outTangentRelative: CGPoint { - outTangent.subtract(point) - } - - func reversed() -> CurveVertex { - CurveVertex(point: point, inTangent: outTangent, outTangent: inTangent) - } - - func translated(_ translation: CGPoint) -> CurveVertex { - CurveVertex(point: point + translation, inTangent: inTangent + translation, outTangent: outTangent + translation) - } - - /** - Trims a path defined by two Vertices at a specific position, from 0 to 1 - - The path can be visualized below. - - F is fromVertex. - V is the vertex of the receiver. - P is the position from 0-1. - O is the outTangent of fromVertex. - F====O=========P=======I====V - - After trimming the curve can be visualized below. - - S is the returned Start vertex. - E is the returned End vertex. - T is the trim point. - TI and TO are the new tangents for the trimPoint - NO and NI are the new tangents for the startPoint and endPoints - S==NO=========TI==T==TO=======NI==E - */ - func splitCurve(toVertex: CurveVertex, position: CGFloat) -> - (start: CurveVertex, trimPoint: CurveVertex, end: CurveVertex) - { - - /// If position is less than or equal to 0, trim at start. - if position <= 0 { - return ( - start: CurveVertex(point: point, inTangentRelative: inTangentRelative, outTangentRelative: .zero), - trimPoint: CurveVertex(point: point, inTangentRelative: .zero, outTangentRelative: outTangentRelative), - end: toVertex) - } - - /// If position is greater than or equal to 1, trim at end. - if position >= 1 { - return ( - start: self, - trimPoint: CurveVertex( - point: toVertex.point, - inTangentRelative: toVertex.inTangentRelative, - outTangentRelative: .zero), - end: CurveVertex( - point: toVertex.point, - inTangentRelative: .zero, - outTangentRelative: toVertex.outTangentRelative)) - } - - if outTangentRelative.isZero && toVertex.inTangentRelative.isZero { - /// If both tangents are zero, then span to be trimmed is a straight line. - let trimPoint = point.interpolate(toVertex.point, amount: position) - return ( - start: self, - trimPoint: CurveVertex(point: trimPoint, inTangentRelative: .zero, outTangentRelative: .zero), - end: toVertex) - } - /// Cutting by amount gives incorrect length.... - /// One option is to cut by a stride until it gets close then edge it down. - /// Measuring a percentage of the spans does not equal the same as measuring a percentage of length. - /// This is where the historical trim path bugs come from. - let a = point.interpolate(outTangent, amount: position) - let b = outTangent.interpolate(toVertex.inTangent, amount: position) - let c = toVertex.inTangent.interpolate(toVertex.point, amount: position) - let d = a.interpolate(b, amount: position) - let e = b.interpolate(c, amount: position) - let f = d.interpolate(e, amount: position) - return ( - start: CurveVertex(point: point, inTangent: inTangent, outTangent: a), - trimPoint: CurveVertex(point: f, inTangent: d, outTangent: e), - end: CurveVertex(point: toVertex.point, inTangent: c, outTangent: toVertex.outTangent)) - } - - /** - Trims a curve of a known length to a specific length and returns the points. - - There is not a performant yet accurate way to cut a curve to a specific length. - This calls splitCurve(toVertex: position:) to split the curve and then measures - the length of the new curve. The function then iterates through the samples, - adjusting the position of the cut for a more precise cut. - Usually a single iteration is enough to get within 0.5 points of the desired - length. - - This function should probably live in PathElement, since it deals with curve - lengths. - */ - func trimCurve(toVertex: CurveVertex, atLength: CGFloat, curveLength: CGFloat, maxSamples: Int, accuracy: CGFloat = 1) -> - (start: CurveVertex, trimPoint: CurveVertex, end: CurveVertex) - { - var currentPosition = atLength / curveLength - var results = splitCurve(toVertex: toVertex, position: currentPosition) - - if maxSamples == 0 { - return results - } - - for _ in 1...maxSamples { - let length = results.start.distanceTo(results.trimPoint) - let lengthDiff = atLength - length - /// Check if length is correct. - if lengthDiff < accuracy { - return results - } - let diffPosition = max(min((currentPosition / length) * lengthDiff, currentPosition * 0.5), currentPosition * -0.5) - currentPosition = diffPosition + currentPosition - results = splitCurve(toVertex: toVertex, position: currentPosition) - } - return results - } - - /** - The distance from the receiver to the provided vertex. - - For lines (zeroed tangents) the distance between the two points is measured. - For curves the curve is iterated over by sample count and the points are measured. - This is ~99% accurate at a sample count of 30 - */ - func distanceTo(_ toVertex: CurveVertex, sampleCount: Int = 25) -> CGFloat { - - if outTangentRelative.isZero && toVertex.inTangentRelative.isZero { - /// Return a linear distance. - return point.distanceTo(toVertex.point) - } - - var distance: CGFloat = 0 - - var previousPoint = point - for i in 0.. PathElement { - PathElement(length: vertex.distanceTo(toVertex), vertex: toVertex) - } - - func updateVertex(newVertex: CurveVertex) -> PathElement { - PathElement(length: length, vertex: newVertex) - } - - /// Splits an element span defined by the receiver and fromElement to a position 0-1 - func splitElementAtPosition(fromElement: PathElement, atLength: CGFloat) -> - (leftSpan: (start: PathElement, end: PathElement), rightSpan: (start: PathElement, end: PathElement)) - { - /// Trim the span. Start and trim go into the first, trim and end go into second. - let trimResults = fromElement.vertex.trimCurve(toVertex: vertex, atLength: atLength, curveLength: length, maxSamples: 3) - - /// Create the elements for the break - let spanAStart = PathElement( - length: fromElement.length, - vertex: CurveVertex( - point: fromElement.vertex.point, - inTangent: fromElement.vertex.inTangent, - outTangent: trimResults.start.outTangent)) - /// Recalculating the length here is a waste as the trimCurve function also accurately calculates this length. - let spanAEnd = spanAStart.pathElementTo(trimResults.trimPoint) - - let spanBStart = PathElement(vertex: trimResults.trimPoint) - let spanBEnd = spanBStart.pathElementTo(trimResults.end) - return ( - leftSpan: (start: spanAStart, end: spanAEnd), - rightSpan: (start: spanBStart, end: spanBEnd)) - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift deleted file mode 100644 index 6dfd7d72..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Private/Utility/Primitives/VectorsExtensions.swift +++ /dev/null @@ -1,283 +0,0 @@ -// -// Vector.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/7/19. -// - -import CoreGraphics -import Foundation -import QuartzCore - -// MARK: - Vector1D + Codable - -/** - Single value container. Needed because lottie sometimes wraps a Double in an array. - */ -extension Vector1D: Codable { - - // MARK: Lifecycle - - public init(from decoder: Decoder) throws { - /// Try to decode an array of doubles - do { - var container = try decoder.unkeyedContainer() - value = try container.decode(Double.self) - } catch { - value = try decoder.singleValueContainer().decode(Double.self) - } - } - - // MARK: Public - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(value) - } - - // MARK: Internal - - var cgFloatValue: CGFloat { - CGFloat(value) - } - -} - -extension Double { - var vectorValue: Vector1D { - Vector1D(self) - } -} - -// MARK: - Vector2D - -/** - Needed for decoding json {x: y:} to a CGPoint - */ -struct Vector2D: Codable { - - // MARK: Lifecycle - - init(x: Double, y: Double) { - self.x = x - self.y = y - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Vector2D.CodingKeys.self) - - do { - let xValue: [Double] = try container.decode([Double].self, forKey: .x) - x = xValue[0] - } catch { - x = try container.decode(Double.self, forKey: .x) - } - - do { - let yValue: [Double] = try container.decode([Double].self, forKey: .y) - y = yValue[0] - } catch { - y = try container.decode(Double.self, forKey: .y) - } - } - - // MARK: Internal - - var x: Double - var y: Double - - var pointValue: CGPoint { - CGPoint(x: x, y: y) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: Vector2D.CodingKeys.self) - try container.encode(x, forKey: .x) - try container.encode(y, forKey: .y) - } - - // MARK: Private - - private enum CodingKeys: String, CodingKey { - case x - case y - } -} - -extension Vector2D { - -} - -extension CGPoint { - var vector2dValue: Vector2D { - Vector2D(x: Double(x), y: Double(y)) - } -} - -// MARK: - Vector3D + Codable - -/** - A three dimensional vector. - These vectors are encoded and decoded from [Double] - */ - -extension Vector3D: Codable { - - // MARK: Lifecycle - - init(x: CGFloat, y: CGFloat, z: CGFloat) { - self.x = Double(x) - self.y = Double(y) - self.z = Double(z) - } - - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - - if !container.isAtEnd { - x = try container.decode(Double.self) - } else { - x = 0 - } - - if !container.isAtEnd { - y = try container.decode(Double.self) - } else { - y = 0 - } - - if !container.isAtEnd { - z = try container.decode(Double.self) - } else { - z = 0 - } - } - - // MARK: Public - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(x) - try container.encode(y) - try container.encode(z) - } - -} - -extension Vector3D { - public var pointValue: CGPoint { - CGPoint(x: x, y: y) - } - - public var sizeValue: CGSize { - CGSize(width: x, height: y) - } -} - -extension CGPoint { - var vector3dValue: Vector3D { - Vector3D(x: x, y: y, z: 0) - } -} - -extension CGSize { - var vector3dValue: Vector3D { - Vector3D(x: width, y: height, z: 1) - } -} - -extension CATransform3D { - - static func makeSkew(skew: CGFloat, skewAxis: CGFloat) -> CATransform3D { - let mCos = cos(skewAxis.toRadians()) - let mSin = sin(skewAxis.toRadians()) - let aTan = tan(skew.toRadians()) - - let transform1 = CATransform3D( - m11: mCos, - m12: mSin, - m13: 0, - m14: 0, - m21: -mSin, - m22: mCos, - m23: 0, - m24: 0, - m31: 0, - m32: 0, - m33: 1, - m34: 0, - m41: 0, - m42: 0, - m43: 0, - m44: 1) - - let transform2 = CATransform3D( - m11: 1, - m12: 0, - m13: 0, - m14: 0, - m21: aTan, - m22: 1, - m23: 0, - m24: 0, - m31: 0, - m32: 0, - m33: 1, - m34: 0, - m41: 0, - m42: 0, - m43: 0, - m44: 1) - - let transform3 = CATransform3D( - m11: mCos, - m12: -mSin, - m13: 0, - m14: 0, - m21: mSin, - m22: mCos, - m23: 0, - m24: 0, - m31: 0, - m32: 0, - m33: 1, - m34: 0, - m41: 0, - m42: 0, - m43: 0, - m44: 1) - return CATransform3DConcat(transform3, CATransform3DConcat(transform2, transform1)) - } - - static func makeTransform( - anchor: CGPoint, - position: CGPoint, - scale: CGSize, - rotation: CGFloat, - skew: CGFloat?, - skewAxis: CGFloat?) - -> CATransform3D - { - if let skew = skew, let skewAxis = skewAxis { - return CATransform3DMakeTranslation(position.x, position.y, 0).rotated(rotation).skewed(skew: -skew, skewAxis: skewAxis) - .scaled(scale * 0.01).translated(anchor * -1) - } - return CATransform3DMakeTranslation(position.x, position.y, 0).rotated(rotation).scaled(scale * 0.01).translated(anchor * -1) - } - - func rotated(_ degrees: CGFloat) -> CATransform3D { - CATransform3DRotate(self, degrees.toRadians(), 0, 0, 1) - } - - func translated(_ translation: CGPoint) -> CATransform3D { - CATransform3DTranslate(self, translation.x, translation.y, 0) - } - - func scaled(_ scale: CGSize) -> CATransform3D { - CATransform3DScale(self, scale.width, scale.height, 1) - } - - func skewed(skew: CGFloat, skewAxis: CGFloat) -> CATransform3D { - CATransform3DConcat(CATransform3D.makeSkew(skew: skew, skewAxis: skewAxis), self) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift deleted file mode 100644 index 1e8012be..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationPublic.swift +++ /dev/null @@ -1,206 +0,0 @@ -// -// AnimationPublic.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/5/19. -// - -import CoreGraphics -import Foundation - -extension Animation { - - /// A closure for an Animation download. The closure is passed `nil` if there was an error. - public typealias DownloadClosure = (Animation?) -> Void - - /// The duration in seconds of the animation. - public var duration: TimeInterval { - Double(endFrame - startFrame) / framerate - } - - /// The natural bounds in points of the animation. - public var bounds: CGRect { - CGRect(x: 0, y: 0, width: width, height: height) - } - - /// The natural size in points of the animation. - public var size: CGSize { - CGSize(width: width, height: height) - } - - // MARK: Animation (Loading) - - /** - Loads an animation model from a bundle by its name. Returns `nil` if an animation is not found. - - - Parameter name: The name of the json file without the json extension. EG "StarAnimation" - - Parameter bundle: The bundle in which the animation is located. Defaults to `Bundle.main` - - Parameter subdirectory: A subdirectory in the bundle in which the animation is located. Optional. - - Parameter animationCache: A cache for holding loaded animations. Optional. - - - Returns: Deserialized `Animation`. Optional. - */ - public static func named( - _ name: String, - bundle: Bundle = Bundle.main, - subdirectory: String? = nil, - animationCache: AnimationCacheProvider? = nil) - -> Animation? - { - /// Create a cache key for the animation. - let cacheKey = bundle.bundlePath + (subdirectory ?? "") + "/" + name - - /// Check cache for animation - if - let animationCache = animationCache, - let animation = animationCache.animation(forKey: cacheKey) - { - /// If found, return the animation. - return animation - } - - do { - /// Decode animation. - guard let json = try bundle.getAnimationData(name, subdirectory: subdirectory) else { - return nil - } - let animation = try JSONDecoder().decode(Animation.self, from: json) - animationCache?.setAnimation(animation, forKey: cacheKey) - return animation - } catch { - /// Decoding error. - print(error) - return nil - } - } - - /** - Loads an animation from a specific filepath. - - Parameter filepath: The absolute filepath of the animation to load. EG "/User/Me/starAnimation.json" - - Parameter animationCache: A cache for holding loaded animations. Optional. - - - Returns: Deserialized `Animation`. Optional. - */ - public static func filepath( - _ filepath: String, - animationCache: AnimationCacheProvider? = nil) - -> Animation? - { - - /// Check cache for animation - if - let animationCache = animationCache, - let animation = animationCache.animation(forKey: filepath) - { - return animation - } - - do { - /// Decode the animation. - let json = try Data(contentsOf: URL(fileURLWithPath: filepath)) - let animation = try JSONDecoder().decode(Animation.self, from: json) - animationCache?.setAnimation(animation, forKey: filepath) - return animation - } catch { - /// Decoding Error. - return nil - } - } - - /** - Loads a Lottie animation asynchronously from the URL. - - - Parameter url: The url to load the animation from. - - Parameter closure: A closure to be called when the animation has loaded. - - Parameter animationCache: A cache for holding loaded animations. - - */ - public static func loadedFrom( - url: URL, - closure: @escaping Animation.DownloadClosure, - animationCache: AnimationCacheProvider?) - { - - if let animationCache = animationCache, let animation = animationCache.animation(forKey: url.absoluteString) { - closure(animation) - } else { - let task = URLSession.shared.dataTask(with: url) { data, _, error in - guard error == nil, let jsonData = data else { - DispatchQueue.main.async { - closure(nil) - } - return - } - do { - let animation = try JSONDecoder().decode(Animation.self, from: jsonData) - DispatchQueue.main.async { - animationCache?.setAnimation(animation, forKey: url.absoluteString) - closure(animation) - } - } catch { - DispatchQueue.main.async { - closure(nil) - } - } - - } - task.resume() - } - } - - // MARK: Animation (Helpers) - - /** - Markers are a way to describe a point in time by a key name. - - Markers are encoded into animation JSON. By using markers a designer can mark - playback points for a developer to use without having to worry about keeping - track of animation frames. If the animation file is updated, the developer - does not need to update playback code. - - Returns the Progress Time for the marker named. Returns nil if no marker found. - */ - public func progressTime(forMarker named: String) -> AnimationProgressTime? { - guard let markers = markerMap, let marker = markers[named] else { - return nil - } - return progressTime(forFrame: marker.frameTime) - } - - /** - Markers are a way to describe a point in time by a key name. - - Markers are encoded into animation JSON. By using markers a designer can mark - playback points for a developer to use without having to worry about keeping - track of animation frames. If the animation file is updated, the developer - does not need to update playback code. - - Returns the Frame Time for the marker named. Returns nil if no marker found. - */ - public func frameTime(forMarker named: String) -> AnimationFrameTime? { - guard let markers = markerMap, let marker = markers[named] else { - return nil - } - return marker.frameTime - } - - /// Converts Frame Time (Seconds * Framerate) into Progress Time (0 to 1). - public func progressTime(forFrame frameTime: AnimationFrameTime) -> AnimationProgressTime { - ((frameTime - startFrame) / (endFrame - startFrame)).clamp(0, 1) - } - - /// Converts Progress Time (0 to 1) into Frame Time (Seconds * Framerate) - public func frameTime(forProgress progressTime: AnimationProgressTime) -> AnimationFrameTime { - ((endFrame - startFrame) * progressTime) + startFrame - } - - /// Converts Frame Time (Seconds * Framerate) into Time (Seconds) - public func time(forFrame frameTime: AnimationFrameTime) -> TimeInterval { - Double(frameTime - startFrame) / framerate - } - - /// Converts Time (Seconds) into Frame Time (Seconds * Framerate) - public func frameTime(forTime time: TimeInterval) -> AnimationFrameTime { - CGFloat(time * framerate) + startFrame - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift deleted file mode 100644 index 5a2616c8..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationView.swift +++ /dev/null @@ -1,1045 +0,0 @@ -// -// LottieView.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/23/19. -// - -import Foundation -import QuartzCore - -// MARK: - LottieBackgroundBehavior - -/// Describes the behavior of an AnimationView when the app is moved to the background. -public enum LottieBackgroundBehavior { - /// Stop the animation and reset it to the beginning of its current play time. The completion block is called. - case stop - /// Pause the animation in its current state. The completion block is called. - case pause - /// Pause the animation and restart it when the application moves to the foreground. The completion block is stored and called when the animation completes. - case pauseAndRestore - /// Stops the animation and sets it to the end of its current play time. The completion block is called. - case forceFinish -} - -// MARK: - LottieLoopMode - -/// Defines animation loop behavior -public enum LottieLoopMode { - /// Animation is played once then stops. - case playOnce - /// Animation will loop from beginning to end until stopped. - case loop - /// Animation will play forward, then backwards and loop until stopped. - case autoReverse - /// Animation will loop from beginning to end up to defined amount of times. - case `repeat`(Float) - /// Animation will play forward, then backwards a defined amount of times. - case repeatBackwards(Float) -} - -// MARK: Equatable - -extension LottieLoopMode: Equatable { - public static func == (lhs: LottieLoopMode, rhs: LottieLoopMode) -> Bool { - switch (lhs, rhs) { - case (.repeat(let lhsAmount), .repeat(let rhsAmount)), - (.repeatBackwards(let lhsAmount), .repeatBackwards(let rhsAmount)): - return lhsAmount == rhsAmount - case (.playOnce, .playOnce), - (.loop, .loop), - (.autoReverse, .autoReverse): - return true - default: - return false - } - } -} - -// MARK: - AnimationView - -@IBDesignable -final public class AnimationView: LottieView { - - // MARK: Lifecycle - - // MARK: - Public (Initializers) - - /// Initializes a LottieView with an animation. - public init( - animation: Animation?, - imageProvider: AnimationImageProvider? = nil, - textProvider: AnimationTextProvider = DefaultTextProvider(), - fontProvider: AnimationFontProvider = DefaultFontProvider()) - { - self.animation = animation - self.imageProvider = imageProvider ?? BundleImageProvider(bundle: Bundle.main, searchPath: nil) - self.textProvider = textProvider - self.fontProvider = fontProvider - super.init(frame: .zero) - commonInit() - makeAnimationLayer() - if let animation = animation { - frame = animation.bounds - } - } - - public init() { - animation = nil - imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil) - textProvider = DefaultTextProvider() - fontProvider = DefaultFontProvider() - super.init(frame: .zero) - commonInit() - } - - public override init(frame _: CGRect) { - animation = nil - imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil) - textProvider = DefaultTextProvider() - fontProvider = DefaultFontProvider() - super.init(frame: .zero) - commonInit() - } - - required public init?(coder aDecoder: NSCoder) { - imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil) - textProvider = DefaultTextProvider() - fontProvider = DefaultFontProvider() - super.init(coder: aDecoder) - commonInit() - } - - // MARK: Public - - /** - Describes the behavior of an AnimationView when the app is moved to the background. - - The default is `pause` which pauses the animation when the application moves to - the background. The completion block is called with `false` for completed. - */ - public var backgroundBehavior: LottieBackgroundBehavior = .pause - - // MARK: - Public Properties - - /** - Sets the animation backing the animation view. Setting this will clear the - view's contents, completion blocks and current state. The new animation will - be loaded up and set to the beginning of its timeline. - */ - public var animation: Animation? { - didSet { - makeAnimationLayer() - } - } - - /** - Sets the image provider for the animation view. An image provider provides the - animation with its required image data. - - Setting this will cause the animation to reload its image contents. - */ - public var imageProvider: AnimationImageProvider { - didSet { - animationLayer?.imageProvider = imageProvider - reloadImages() - } - } - - /** - Sets the text provider for animation view. A text provider provides the - animation with values for text layers - */ - public var textProvider: AnimationTextProvider { - didSet { - animationLayer?.textProvider = textProvider - } - } - - /** - Sets the text provider for animation view. A text provider provides the - animation with values for text layers - */ - public var fontProvider: AnimationFontProvider { - didSet { - animationLayer?.fontProvider = fontProvider - } - } - - /// Returns `true` if the animation is currently playing. - public var isAnimationPlaying: Bool { - animationLayer?.animation(forKey: activeAnimationName) != nil - } - - /// Returns `true` if the animation will start playing when this view is added to a window. - public var isAnimationQueued: Bool { - animationContext != nil && waitingToPlayAnimation - } - - /// Sets the loop behavior for `play` calls. Defaults to `playOnce` - public var loopMode: LottieLoopMode = .playOnce { - didSet { - updateInFlightAnimation() - } - } - - /** - When `true` the animation view will rasterize its contents when not animating. - Rasterizing will improve performance of static animations. - - Note: this will not produce crisp results at resolutions above the animations natural resolution. - - Defaults to `false` - */ - public var shouldRasterizeWhenIdle: Bool = false { - didSet { - updateRasterizationState() - } - } - - /** - Sets the current animation time with a Progress Time - - Note: Setting this will stop the current animation, if any. - Note 2: If `animation` is nil, setting this will fallback to 0 - */ - public var currentProgress: AnimationProgressTime { - set { - if let animation = animation { - currentFrame = animation.frameTime(forProgress: newValue) - } else { - currentFrame = 0 - } - } - get { - if let animation = animation { - return animation.progressTime(forFrame: currentFrame) - } else { - return 0 - } - } - } - - /** - Sets the current animation time with a time in seconds. - - Note: Setting this will stop the current animation, if any. - Note 2: If `animation` is nil, setting this will fallback to 0 - */ - public var currentTime: TimeInterval { - set { - if let animation = animation { - currentFrame = animation.frameTime(forTime: newValue) - } else { - currentFrame = 0 - } - } - get { - if let animation = animation { - return animation.time(forFrame: currentFrame) - } else { - return 0 - } - } - } - - /** - Sets the current animation time with a frame in the animations framerate. - - Note: Setting this will stop the current animation, if any. - */ - public var currentFrame: AnimationFrameTime { - set { - removeCurrentAnimation() - updateAnimationFrame(newValue) - } - get { - animationLayer?.currentFrame ?? 0 - } - } - - /// Returns the current animation frame while an animation is playing. - public var realtimeAnimationFrame: AnimationFrameTime { - isAnimationPlaying ? animationLayer?.presentation()?.currentFrame ?? currentFrame : currentFrame - } - - /// Returns the current animation frame while an animation is playing. - public var realtimeAnimationProgress: AnimationProgressTime { - if let animation = animation { - return animation.progressTime(forFrame: realtimeAnimationFrame) - } - return 0 - } - - /// Sets the speed of the animation playback. Defaults to 1 - public var animationSpeed: CGFloat = 1 { - didSet { - updateInFlightAnimation() - } - } - - /** - When `true` the animation will play back at the framerate encoded in the - `Animation` model. When `false` the animation will play at the framerate - of the device. - - Defaults to false - */ - public var respectAnimationFrameRate: Bool = false { - didSet { - animationLayer?.respectAnimationFrameRate = respectAnimationFrameRate - } - } - - /** - Controls the cropping of an Animation. Setting this property will crop the animation - to the current views bounds by the viewport frame. The coordinate space is specified - in the animation's coordinate space. - - Animatable. - */ - public var viewportFrame: CGRect? = nil { - didSet { - - /* - This is really ugly, but is needed to trigger a layout pass within an animation block. - Typically this happens automatically, when layout objects are UIView based. - The animation layer is a CALayer which will not implicitly grab the animation - duration of a UIView animation block. - - By setting bounds and then resetting bounds the UIView animation block's - duration and curve are captured and added to the layer. This is used in the - layout block to animate the animationLayer's position and size. - */ - let rect = bounds - self.bounds = CGRect.zero - self.bounds = rect - self.setNeedsLayout() - } - } - - // MARK: - Public (UIView Overrides) - - override public var intrinsicContentSize: CGSize { - if let animation = animation { - return animation.bounds.size - } - return .zero - } - - // MARK: - Public Functions - - /** - Plays the animation from its current state to the end. - - - Parameter completion: An optional completion closure to be called when the animation completes playing. - */ - public func play(completion: LottieCompletionBlock? = nil) { - guard let animation = animation else { - return - } - - /// Build a context for the animation. - let context = AnimationContext( - playFrom: CGFloat(animation.startFrame), - playTo: CGFloat(animation.endFrame), - closure: completion) - removeCurrentAnimation() - addNewAnimationForContext(context) - } - - /** - Plays the animation from a progress (0-1) to a progress (0-1). - - - Parameter fromProgress: The start progress of the animation. If `nil` the animation will start at the current progress. - - Parameter toProgress: The end progress of the animation. - - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used. - - Parameter completion: An optional completion closure to be called when the animation stops. - */ - public func play( - fromProgress: AnimationProgressTime? = nil, - toProgress: AnimationProgressTime, - loopMode: LottieLoopMode? = nil, - completion: LottieCompletionBlock? = nil) - { - guard let animation = animation else { - return - } - - removeCurrentAnimation() - if let loopMode = loopMode { - /// Set the loop mode, if one was supplied - self.loopMode = loopMode - } - let context = AnimationContext( - playFrom: animation.frameTime(forProgress: fromProgress ?? currentProgress), - playTo: animation.frameTime(forProgress: toProgress), - closure: completion) - addNewAnimationForContext(context) - } - - /** - Plays the animation from a start frame to an end frame in the animation's framerate. - - - Parameter fromFrame: The start frame of the animation. If `nil` the animation will start at the current frame. - - Parameter toFrame: The end frame of the animation. - - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used. - - Parameter completion: An optional completion closure to be called when the animation stops. - */ - public func play( - fromFrame: AnimationFrameTime? = nil, - toFrame: AnimationFrameTime, - loopMode: LottieLoopMode? = nil, - completion: LottieCompletionBlock? = nil) - { - removeCurrentAnimation() - if let loopMode = loopMode { - /// Set the loop mode, if one was supplied - self.loopMode = loopMode - } - - let context = AnimationContext( - playFrom: fromFrame ?? currentProgress, - playTo: toFrame, - closure: completion) - addNewAnimationForContext(context) - } - - /** - Plays the animation from a named marker to another marker. - - Markers are point in time that are encoded into the Animation data and assigned - a name. - - NOTE: If markers are not found the play command will exit. - - - Parameter fromMarker: The start marker for the animation playback. If `nil` the - animation will start at the current progress. - - Parameter toMarker: The end marker for the animation playback. - - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used. - - Parameter completion: An optional completion closure to be called when the animation stops. - */ - public func play( - fromMarker: String? = nil, - toMarker: String, - loopMode: LottieLoopMode? = nil, - completion: LottieCompletionBlock? = nil) - { - - guard let animation = animation, let markers = animation.markerMap, let to = markers[toMarker] else { - return - } - - removeCurrentAnimation() - if let loopMode = loopMode { - /// Set the loop mode, if one was supplied - self.loopMode = loopMode - } - - let fromTime: CGFloat - if let fromName = fromMarker, let from = markers[fromName] { - fromTime = CGFloat(from.frameTime) - } else { - fromTime = currentFrame - } - - let context = AnimationContext( - playFrom: fromTime, - playTo: CGFloat(to.frameTime), - closure: completion) - addNewAnimationForContext(context) - } - - /** - Stops the animation and resets the view to its start frame. - - The completion closure will be called with `false` - */ - public func stop() { - removeCurrentAnimation() - currentFrame = 0 - } - - /** - Pauses the animation in its current state. - - The completion closure will be called with `false` - */ - public func pause() { - removeCurrentAnimation() - } - - /// Reloads the images supplied to the animation from the `imageProvider` - public func reloadImages() { - animationLayer?.reloadImages() - } - - /// Forces the AnimationView to redraw its contents. - public func forceDisplayUpdate() { - animationLayer?.forceDisplayUpdate() - } - - // MARK: - Public (Dynamic Properties) - - /** - - Sets a ValueProvider for the specified keypath. The value provider will be set - on all properties that match the keypath. - - Nearly all properties of a Lottie animation can be changed at runtime using a - combination of `Animation Keypaths` and `Value Providers`. - Setting a ValueProvider on a keypath will cause the animation to update its - contents and read the new Value Provider. - - A value provider provides a typed value on a frame by frame basis. - - - Parameter valueProvider: The new value provider for the properties. - - Parameter keypath: The keypath used to search for properties. - - Example: - ``` - /// A keypath that finds the color value for all `Fill 1` nodes. - let fillKeypath = AnimationKeypath(keypath: "**.Fill 1.Color") - /// A Color Value provider that returns a reddish color. - let redValueProvider = ColorValueProvider(Color(r: 1, g: 0.2, b: 0.3, a: 1)) - /// Set the provider on the animationView. - animationView.setValueProvider(redValueProvider, keypath: fillKeypath) - ``` - */ - public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) { - animationLayer?.setValueProvider(valueProvider, keypath: keypath) - } - - /** - Reads the value of a property specified by the Keypath. - Returns nil if no property is found. - - - Parameter for: The keypath used to search for the property. - - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used. - */ - public func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? { - animationLayer?.getValue(for: keypath, atFrame: atFrame) - } - - /// Logs all child keypaths. - public func logHierarchyKeypaths() { - animationLayer?.logHierarchyKeypaths() - } - - // MARK: - Public (Add Subview) - - /** - Searches for the nearest child layer to the first Keypath and adds the subview - to that layer. The subview will move and animate with the child layer. - Furthermore the subview will be in the child layers coordinate space. - - Note: if no layer is found for the keypath, then nothing happens. - - - Parameter subview: The subview to add to the found animation layer. - - Parameter keypath: The keypath used to find the animation layer. - - Example: - ``` - /// A keypath that finds `Layer 1` - let layerKeypath = AnimationKeypath(keypath: "Layer 1") - - /// Wrap the custom view in an `AnimationSubview` - let subview = AnimationSubview() - subview.addSubview(customView) - - /// Set the provider on the animationView. - animationView.addSubview(subview, forLayerAt: layerKeypath) - ``` - */ - public func addSubview(_ subview: AnimationSubview, forLayerAt keypath: AnimationKeypath) { - guard let sublayer = animationLayer?.layer(for: keypath) else { - return - } - setNeedsLayout() - layoutIfNeeded() - forceDisplayUpdate() - addSubview(subview) - if let subViewLayer = subview.viewLayer { - sublayer.addSublayer(subViewLayer) - } - } - - /** - Converts a CGRect from the AnimationView's coordinate space into the - coordinate space of the layer found at Keypath. - - If no layer is found, nil is returned - - - Parameter rect: The CGRect to convert. - - Parameter toLayerAt: The keypath used to find the layer. - */ - public func convert(_ rect: CGRect, toLayerAt keypath: AnimationKeypath?) -> CGRect? { - guard let animationLayer = animationLayer else { return nil } - guard let keypath = keypath else { - return viewLayer?.convert(rect, to: animationLayer) - } - guard let sublayer = animationLayer.layer(for: keypath) else { - return nil - } - setNeedsLayout() - layoutIfNeeded() - forceDisplayUpdate() - return animationLayer.convert(rect, to: sublayer) - } - - /** - Converts a CGPoint from the AnimationView's coordinate space into the - coordinate space of the layer found at Keypath. - - If no layer is found, nil is returned - - - Parameter point: The CGPoint to convert. - - Parameter toLayerAt: The keypath used to find the layer. - */ - public func convert(_ point: CGPoint, toLayerAt keypath: AnimationKeypath?) -> CGPoint? { - guard let animationLayer = animationLayer else { return nil } - guard let keypath = keypath else { - return viewLayer?.convert(point, to: animationLayer) - } - guard let sublayer = animationLayer.layer(for: keypath) else { - return nil - } - setNeedsLayout() - layoutIfNeeded() - forceDisplayUpdate() - return animationLayer.convert(point, to: sublayer) - } - - // MARK: - Public (Animation Contents) - - /** - Sets the enabled state of all animator nodes found with the keypath search. - This can be used to interactively enable / disable parts of the animation. - - - Parameter isEnabled: When true the animator nodes affect the rendering tree. When false the node is removed from the tree. - - Parameter keypath: The keypath used to find the node(s). - */ - public func setNodeIsEnabled(isEnabled: Bool, keypath: AnimationKeypath) { - guard let animationLayer = animationLayer else { return } - let nodes = animationLayer.animatorNodes(for: keypath) - if let nodes = nodes { - for node in nodes { - node.isEnabled = isEnabled - } - forceDisplayUpdate() - } - } - - // MARK: - Public (Markers) - - /** - Markers are a way to describe a point in time by a key name. - - Markers are encoded into animation JSON. By using markers a designer can mark - playback points for a developer to use without having to worry about keeping - track of animation frames. If the animation file is updated, the developer - does not need to update playback code. - - Returns the Progress Time for the marker named. Returns nil if no marker found. - */ - public func progressTime(forMarker named: String) -> AnimationProgressTime? { - guard let animation = animation else { - return nil - } - return animation.progressTime(forMarker: named) - } - - /** - Markers are a way to describe a point in time by a key name. - - Markers are encoded into animation JSON. By using markers a designer can mark - playback points for a developer to use without having to worry about keeping - track of animation frames. If the animation file is updated, the developer - does not need to update playback code. - - Returns the Frame Time for the marker named. Returns nil if no marker found. - */ - public func frameTime(forMarker named: String) -> AnimationFrameTime? { - guard let animation = animation else { - return nil - } - return animation.frameTime(forMarker: named) - } - - // MARK: Internal - - // MARK: - Private (Properties) - - var animationLayer: AnimationContainer? = nil - - /// Set animation name from Interface Builder - @IBInspectable var animationName: String? { - didSet { - self.animation = animationName.flatMap { - Animation.named($0, animationCache: nil) - } - } - } - - override func layoutAnimation() { - guard let animation = animation, let animationLayer = animationLayer else { return } - var position = animation.bounds.center - let xform: CATransform3D - var shouldForceUpdates: Bool = false - - if let viewportFrame = viewportFrame { - shouldForceUpdates = contentMode == .redraw - - let compAspect = viewportFrame.size.width / viewportFrame.size.height - let viewAspect = bounds.size.width / bounds.size.height - let dominantDimension = compAspect > viewAspect ? bounds.size.width : bounds.size.height - let compDimension = compAspect > viewAspect ? viewportFrame.size.width : viewportFrame.size.height - let scale = dominantDimension / compDimension - - let viewportOffset = animation.bounds.center - viewportFrame.center - xform = CATransform3DTranslate(CATransform3DMakeScale(scale, scale, 1), viewportOffset.x, viewportOffset.y, 0) - position = bounds.center - } else { - switch contentMode { - case .scaleToFill: - position = bounds.center - xform = CATransform3DMakeScale( - bounds.size.width / animation.size.width, - bounds.size.height / animation.size.height, - 1); - case .scaleAspectFit: - position = bounds.center - let compAspect = animation.size.width / animation.size.height - let viewAspect = bounds.size.width / bounds.size.height - let dominantDimension = compAspect > viewAspect ? bounds.size.width : bounds.size.height - let compDimension = compAspect > viewAspect ? animation.size.width : animation.size.height - let scale = dominantDimension / compDimension - xform = CATransform3DMakeScale(scale, scale, 1) - case .scaleAspectFill: - position = bounds.center - let compAspect = animation.size.width / animation.size.height - let viewAspect = bounds.size.width / bounds.size.height - let scaleWidth = compAspect < viewAspect - let dominantDimension = scaleWidth ? bounds.size.width : bounds.size.height - let compDimension = scaleWidth ? animation.size.width : animation.size.height - let scale = dominantDimension / compDimension - xform = CATransform3DMakeScale(scale, scale, 1) - case .redraw: - shouldForceUpdates = true - xform = CATransform3DIdentity - case .center: - position = bounds.center - xform = CATransform3DIdentity - case .top: - position.x = bounds.center.x - xform = CATransform3DIdentity - case .bottom: - position.x = bounds.center.x - position.y = bounds.maxY - animation.bounds.midY - xform = CATransform3DIdentity - case .left: - position.y = bounds.center.y - xform = CATransform3DIdentity - case .right: - position.y = bounds.center.y - position.x = bounds.maxX - animation.bounds.midX - xform = CATransform3DIdentity - case .topLeft: - xform = CATransform3DIdentity - case .topRight: - position.x = bounds.maxX - animation.bounds.midX - xform = CATransform3DIdentity - case .bottomLeft: - position.y = bounds.maxY - animation.bounds.midY - xform = CATransform3DIdentity - case .bottomRight: - position.x = bounds.maxX - animation.bounds.midX - position.y = bounds.maxY - animation.bounds.midY - xform = CATransform3DIdentity - - #if os(iOS) || os(tvOS) - @unknown default: - print("unsupported contentMode: \(contentMode.rawValue); please update lottie-ios") - xform = CATransform3DIdentity - #endif - } - } - - /* - UIView Animation does not implicitly set CAAnimation time or timing fuctions. - If layout is changed in an animation we must get the current animation duration - and timing function and then manually create a CAAnimation to match the UIView animation. - If layout is changed without animation, explicitly set animation duration to 0.0 - inside CATransaction to avoid unwanted artifacts. - */ - /// Check if any animation exist on the view's layer, and match it. - if let key = viewLayer?.animationKeys()?.first, let animation = viewLayer?.animation(forKey: key) { - // The layout is happening within an animation block. Grab the animation data. - - let positionKey = "LayoutPositionAnimation" - let transformKey = "LayoutTransformAnimation" - animationLayer.removeAnimation(forKey: positionKey) - animationLayer.removeAnimation(forKey: transformKey) - - let positionAnimation = animation.copy() as? CABasicAnimation ?? CABasicAnimation(keyPath: "position") - positionAnimation.keyPath = "position" - positionAnimation.isAdditive = false - positionAnimation.fromValue = (animationLayer.presentation() ?? animationLayer).position - positionAnimation.toValue = position - positionAnimation.isRemovedOnCompletion = true - - let xformAnimation = animation.copy() as? CABasicAnimation ?? CABasicAnimation(keyPath: "transform") - xformAnimation.keyPath = "transform" - xformAnimation.isAdditive = false - xformAnimation.fromValue = (animationLayer.presentation() ?? animationLayer).transform - xformAnimation.toValue = xform - xformAnimation.isRemovedOnCompletion = true - - animationLayer.position = position - animationLayer.transform = xform - #if os(OSX) - animationLayer.anchorPoint = layer?.anchorPoint ?? CGPoint.zero - #else - animationLayer.anchorPoint = layer.anchorPoint - #endif - animationLayer.add(positionAnimation, forKey: positionKey) - animationLayer.add(xformAnimation, forKey: transformKey) - } else { - CATransaction.begin() - CATransaction.setAnimationDuration(0.0) - CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: .linear)) - animationLayer.position = position - animationLayer.transform = xform - CATransaction.commit() - } - - if shouldForceUpdates { - animationLayer.forceDisplayUpdate() - } - } - - func updateRasterizationState() { - if isAnimationPlaying { - animationLayer?.shouldRasterize = false - } else { - animationLayer?.shouldRasterize = shouldRasterizeWhenIdle - } - } - - // MARK: - Private (Animation Playback) - - /// Updates the animation frame. Does not affect any current animations - func updateAnimationFrame(_ newFrame: CGFloat) { - CATransaction.begin() - CATransaction.setCompletionBlock { - self.animationLayer?.forceDisplayUpdate() - } - CATransaction.setDisableActions(true) - animationLayer?.currentFrame = newFrame - CATransaction.commit() - } - - @objc - override func animationWillMoveToBackground() { - updateAnimationForBackgroundState() - } - - @objc - override func animationWillEnterForeground() { - updateAnimationForForegroundState() - } - - override func animationMovedToWindow() { - /// Don't update any state if the `superview` is `nil` - /// When A viewA owns superViewB, it removes the superViewB from the window. At this point, viewA still owns superViewB and triggers the viewA method: -didmovetowindow - guard superview != nil else { return } - - if window != nil { - updateAnimationForForegroundState() - } else { - updateAnimationForBackgroundState() - } - } - - // MARK: Fileprivate - - fileprivate var animationContext: AnimationContext? - fileprivate var activeAnimationName: String = AnimationView.animationName - fileprivate var animationID: Int = 0 - - fileprivate var waitingToPlayAnimation: Bool = false - - // MARK: - Private (Building Animation View) - - fileprivate func makeAnimationLayer() { - - /// Remove current animation if any - removeCurrentAnimation() - - if let oldAnimation = self.animationLayer { - oldAnimation.removeFromSuperlayer() - } - - invalidateIntrinsicContentSize() - - guard let animation = animation else { - return - } - - let animationLayer = AnimationContainer( - animation: animation, - imageProvider: imageProvider, - textProvider: textProvider, - fontProvider: fontProvider) - animationLayer.renderScale = screenScale - viewLayer?.addSublayer(animationLayer) - self.animationLayer = animationLayer - reloadImages() - animationLayer.setNeedsDisplay() - setNeedsLayout() - currentFrame = CGFloat(animation.startFrame) - } - - fileprivate func updateAnimationForBackgroundState() { - if let currentContext = animationContext { - switch backgroundBehavior { - case .stop: - removeCurrentAnimation() - updateAnimationFrame(currentContext.playFrom) - case .pause: - removeCurrentAnimation() - case .pauseAndRestore: - currentContext.closure.ignoreDelegate = true - removeCurrentAnimation() - /// Keep the stale context around for when the app enters the foreground. - animationContext = currentContext - case .forceFinish: - removeCurrentAnimation() - updateAnimationFrame(currentContext.playTo) - } - } - } - - fileprivate func updateAnimationForForegroundState() { - if let currentContext = animationContext { - if waitingToPlayAnimation { - waitingToPlayAnimation = false - addNewAnimationForContext(currentContext) - } else if backgroundBehavior == .pauseAndRestore { - /// Restore animation from saved state - updateInFlightAnimation() - } - } - } - - /// Stops the current in flight animation and freezes the animation in its current state. - fileprivate func removeCurrentAnimation() { - guard animationContext != nil else { return } - let pauseFrame = realtimeAnimationFrame - animationLayer?.removeAnimation(forKey: activeAnimationName) - updateAnimationFrame(pauseFrame) - animationContext = nil - } - - /// Updates an in flight animation. - fileprivate func updateInFlightAnimation() { - guard let animationContext = animationContext else { return } - - guard animationContext.closure.animationState != .complete else { - // Tried to re-add an already completed animation. Cancel. - self.animationContext = nil - return - } - - /// Tell existing context to ignore its closure - animationContext.closure.ignoreDelegate = true - - /// Make a new context, stealing the completion block from the previous. - let newContext = AnimationContext( - playFrom: animationContext.playFrom, - playTo: animationContext.playTo, - closure: animationContext.closure.completionBlock) - - /// Remove current animation, and freeze the current frame. - let pauseFrame = realtimeAnimationFrame - animationLayer?.removeAnimation(forKey: activeAnimationName) - animationLayer?.currentFrame = pauseFrame - - addNewAnimationForContext(newContext) - } - - /// Adds animation to animation layer and sets the delegate. If animation layer or animation are nil, exits. - fileprivate func addNewAnimationForContext(_ animationContext: AnimationContext) { - guard let animationlayer = animationLayer, let animation = animation else { - return - } - - self.animationContext = animationContext - - guard window != nil else { waitingToPlayAnimation = true; return } - - animationID = animationID + 1 - activeAnimationName = AnimationView.animationName + String(animationID) - - /// At this point there is no animation on animationLayer and its state is set. - - let framerate = animation.framerate - - let playFrom = animationContext.playFrom.clamp(animation.startFrame, animation.endFrame) - let playTo = animationContext.playTo.clamp(animation.startFrame, animation.endFrame) - - let duration = ((max(playFrom, playTo) - min(playFrom, playTo)) / CGFloat(framerate)) - - let playingForward: Bool = - ( - (animationSpeed > 0 && playFrom < playTo) || - (animationSpeed < 0 && playTo < playFrom)) - - var startFrame = currentFrame.clamp(min(playFrom, playTo), max(playFrom, playTo)) - if startFrame == playTo { - startFrame = playFrom - } - - let timeOffset: TimeInterval = playingForward ? - Double(startFrame - min(playFrom, playTo)) / framerate : - Double(max(playFrom, playTo) - startFrame) / framerate - - let layerAnimation = CABasicAnimation(keyPath: "currentFrame") - layerAnimation.fromValue = playFrom - layerAnimation.toValue = playTo - layerAnimation.speed = Float(animationSpeed) - layerAnimation.duration = TimeInterval(duration) - layerAnimation.fillMode = CAMediaTimingFillMode.both - - switch loopMode { - case .playOnce: - layerAnimation.repeatCount = 1 - case .loop: - layerAnimation.repeatCount = HUGE - case .autoReverse: - layerAnimation.repeatCount = HUGE - layerAnimation.autoreverses = true - case .repeat(let amount): - layerAnimation.repeatCount = amount - case .repeatBackwards(let amount): - layerAnimation.repeatCount = amount - layerAnimation.autoreverses = true - } - - layerAnimation.isRemovedOnCompletion = false - if timeOffset != 0 { - let currentLayerTime = viewLayer?.convertTime(CACurrentMediaTime(), from: nil) ?? 0 - layerAnimation.beginTime = currentLayerTime - (timeOffset * 1 / Double(abs(animationSpeed))) - } - layerAnimation.delegate = animationContext.closure - animationContext.closure.animationLayer = animationlayer - animationContext.closure.animationKey = activeAnimationName - - animationlayer.add(layerAnimation, forKey: activeAnimationName) - updateRasterizationState() - } - - // MARK: Private - - static private let animationName: String = "Lottie" -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift deleted file mode 100644 index e97fb15c..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Animation/AnimationViewInitializers.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// AnimationViewInitializers.swift -// lottie-swift-iOS -// -// Created by Brandon Withrow on 2/6/19. -// - -import Foundation - -extension AnimationView { - - // MARK: Lifecycle - - /** - Loads a Lottie animation from a JSON file in the supplied bundle. - - - Parameter name: The string name of the lottie animation with no file - extension provided. - - Parameter bundle: The bundle in which the animation is located. - Defaults to the Main bundle. - - Parameter imageProvider: An image provider for the animation's image data. - If none is supplied Lottie will search in the supplied bundle for images. - */ - public convenience init( - name: String, - bundle: Bundle = Bundle.main, - imageProvider: AnimationImageProvider? = nil, - animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) - { - let animation = Animation.named(name, bundle: bundle, subdirectory: nil, animationCache: animationCache) - let provider = imageProvider ?? BundleImageProvider(bundle: bundle, searchPath: nil) - self.init(animation: animation, imageProvider: provider) - } - - /** - Loads a Lottie animation from a JSON file in a specific path on disk. - - - Parameter name: The absolute path of the Lottie Animation. - - Parameter imageProvider: An image provider for the animation's image data. - If none is supplied Lottie will search in the supplied filepath for images. - */ - public convenience init( - filePath: String, - imageProvider: AnimationImageProvider? = nil, - animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) - { - let animation = Animation.filepath(filePath, animationCache: animationCache) - let provider = imageProvider ?? - FilepathImageProvider(filepath: URL(fileURLWithPath: filePath).deletingLastPathComponent().path) - self.init(animation: animation, imageProvider: provider) - } - - /** - Loads a Lottie animation asynchronously from the URL - - - Parameter url: The url to load the animation from. - - Parameter imageProvider: An image provider for the animation's image data. - If none is supplied Lottie will search in the main bundle for images. - - Parameter closure: A closure to be called when the animation has loaded. - */ - public convenience init( - url: URL, - imageProvider: AnimationImageProvider? = nil, - closure: @escaping AnimationView.DownloadClosure, - animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) - { - - if let animationCache = animationCache, let animation = animationCache.animation(forKey: url.absoluteString) { - self.init(animation: animation, imageProvider: imageProvider) - closure(nil) - } else { - - self.init(animation: nil, imageProvider: imageProvider) - - Animation.loadedFrom(url: url, closure: { animation in - if let animation = animation { - self.animation = animation - closure(nil) - } else { - closure(LottieDownloadError.downloadFailed) - } - }, animationCache: animationCache) - } - } - - // MARK: Public - - public typealias DownloadClosure = (Error?) -> Void - -} - -// MARK: - LottieDownloadError - -enum LottieDownloadError: Error { - case downloadFailed -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift deleted file mode 100644 index 88b416c8..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/AnimationCacheProvider.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// AnimationCacheProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/5/19. -// - -import Foundation -/** - `AnimationCacheProvider` is a protocol that describes an Animation Cache. - Animation Cache is used when loading `Animation` models. Using an Animation Cache - can increase performance when loading an animation multiple times. - - Lottie comes with a prebuilt LRU Animation Cache. - */ -public protocol AnimationCacheProvider { - - func animation(forKey: String) -> Animation? - - func setAnimation(_ animation: Animation, forKey: String) - - func clearCache() - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift deleted file mode 100644 index c7fbc668..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/AnimationCache/LRUAnimationCache.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// LRUAnimationCache.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/5/19. -// - -import Foundation - -/** - An Animation Cache that will store animations up to `cacheSize`. - - Once `cacheSize` is reached, the least recently used animation will be ejected. - The default size of the cache is 100. - */ -public class LRUAnimationCache: AnimationCacheProvider { - - // MARK: Lifecycle - - public init() { } - - // MARK: Public - - /// The global shared Cache. - public static let sharedCache = LRUAnimationCache() - - /// The size of the cache. - public var cacheSize: Int = 100 - - /// Clears the Cache. - public func clearCache() { - cacheMap.removeAll() - lruList.removeAll() - } - - public func animation(forKey: String) -> Animation? { - guard let animation = cacheMap[forKey] else { - return nil - } - if let index = lruList.firstIndex(of: forKey) { - lruList.remove(at: index) - lruList.append(forKey) - } - return animation - } - - public func setAnimation(_ animation: Animation, forKey: String) { - cacheMap[forKey] = animation - lruList.append(forKey) - if lruList.count > cacheSize { - let removed = lruList.remove(at: 0) - if removed != forKey { - cacheMap[removed] = nil - } - } - } - - // MARK: Fileprivate - - fileprivate var cacheMap: [String: Animation] = [:] - fileprivate var lruList: [String] = [] - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift deleted file mode 100644 index aabcbd0f..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnimationKeypath.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// AnimationKeypath.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation - -/** - `AnimationKeypath` is an object that describes a keypath search for nodes in the - animation JSON. `AnimationKeypath` matches views and properties inside of `AnimationView` - to their backing `Animation` model by name. - - A keypath can be used to set properties on an existing animation, or can be validated - with an existing `Animation`. - - `AnimationKeypath` can describe a specific object, or can use wildcards for fuzzy matching - of objects. Acceptable wildcards are either "*" (star) or "**" (double star). - Single star will search a single depth for the next object. - Double star will search any depth. - - Read More at https://airbnb.io/lottie/#/ios?id=dynamic-animation-properties - - EG: - @"Layer.Shape Group.Stroke 1.Color" - Represents a specific color node on a specific stroke. - - @"**.Stroke 1.Color" - Represents the color node for every Stroke named "Stroke 1" in the animation. - */ -public struct AnimationKeypath { - - /// Creates a keypath from a dot separated string. The string is separated by "." - public init(keypath: String) { - keys = keypath.components(separatedBy: ".") - } - - /// Creates a keypath from a list of strings. - public init(keys: [String]) { - self.keys = keys - } - - let keys: [String] - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift deleted file mode 100644 index 957b74c8..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/AnyValueProvider.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// AnyValueProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/30/19. -// - -import CoreGraphics -import Foundation - -/** - `AnyValueProvider` is a protocol that return animation data for a property at a - given time. Every fame an `AnimationView` queries all of its properties and asks - if their ValueProvider has an update. If it does the AnimationView will read the - property and update that portion of the animation. - - Value Providers can be used to dynamically set animation properties at run time. - */ -public protocol AnyValueProvider { - - /// The Type of the value provider - var valueType: Any.Type { get } - - /// Asks the provider if it has an update for the given frame. - func hasUpdate(frame: AnimationFrameTime) -> Bool - - /// Asks the provider to update the container with its value for the frame. - func value(frame: AnimationFrameTime) -> Any -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift deleted file mode 100644 index c3a3e383..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// ColorValueProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import CoreGraphics -import Foundation - -/// A `ValueProvider` that returns a CGColor Value -public final class ColorValueProvider: AnyValueProvider { - - // MARK: Lifecycle - - /// Initializes with a block provider - public init(block: @escaping ColorValueBlock) { - self.block = block - color = Color(r: 0, g: 0, b: 0, a: 1) - } - - /// Initializes with a single color. - public init(_ color: Color) { - self.color = color - block = nil - hasUpdate = true - } - - // MARK: Public - - /// Returns a Color for a CGColor(Frame Time) - public typealias ColorValueBlock = (CGFloat) -> Color - - /// The color value of the provider. - public var color: Color { - didSet { - hasUpdate = true - } - } - - // MARK: ValueProvider Protocol - - public var valueType: Any.Type { - Color.self - } - - public func hasUpdate(frame _: CGFloat) -> Bool { - if block != nil { - return true - } - return hasUpdate - } - - public func value(frame: CGFloat) -> Any { - hasUpdate = false - let newColor: Color - if let block = block { - newColor = block(frame) - } else { - newColor = color - } - return newColor - } - - // MARK: Private - - private var hasUpdate: Bool = true - - private var block: ColorValueBlock? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift deleted file mode 100644 index 8bea6660..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// DoubleValueProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import CoreGraphics -import Foundation - -/// A `ValueProvider` that returns a CGFloat Value -public final class FloatValueProvider: AnyValueProvider { - - // MARK: Lifecycle - - /// Initializes with a block provider - public init(block: @escaping CGFloatValueBlock) { - self.block = block - float = 0 - } - - /// Initializes with a single float. - public init(_ float: CGFloat) { - self.float = float - block = nil - hasUpdate = true - } - - // MARK: Public - - /// Returns a CGFloat for a CGFloat(Frame Time) - public typealias CGFloatValueBlock = (CGFloat) -> CGFloat - - public var float: CGFloat { - didSet { - hasUpdate = true - } - } - - // MARK: ValueProvider Protocol - - public var valueType: Any.Type { - Vector1D.self - } - - public func hasUpdate(frame _: CGFloat) -> Bool { - if block != nil { - return true - } - return hasUpdate - } - - public func value(frame: CGFloat) -> Any { - hasUpdate = false - let newCGFloat: CGFloat - if let block = block { - newCGFloat = block(frame) - } else { - newCGFloat = float - } - return Vector1D(Double(newCGFloat)) - } - - // MARK: Private - - private var hasUpdate: Bool = true - - private var block: CGFloatValueBlock? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift deleted file mode 100644 index b71568ca..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// GradientValueProvider.swift -// lottie-swift -// -// Created by Enrique Bermúdez on 10/27/19. -// - -import CoreGraphics -import Foundation - -/// A `ValueProvider` that returns a Gradient Color Value. -public final class GradientValueProvider: AnyValueProvider { - - // MARK: Lifecycle - - /// Initializes with a block provider. - public init( - block: @escaping ColorsValueBlock, - locations: ColorLocationsBlock? = nil) - { - self.block = block - locationsBlock = locations - colors = [] - self.locations = [] - } - - /// Initializes with an array of colors. - public init( - _ colors: [Color], - locations: [Double] = []) - { - self.colors = colors - self.locations = locations - updateValueArray() - hasUpdate = true - } - - // MARK: Public - - /// Returns a [Color] for a CGFloat(Frame Time). - public typealias ColorsValueBlock = (CGFloat) -> [Color] - /// Returns a [Double](Color locations) for a CGFloat(Frame Time). - public typealias ColorLocationsBlock = (CGFloat) -> [Double] - - /// The colors values of the provider. - public var colors: [Color] { - didSet { - updateValueArray() - hasUpdate = true - } - } - - /// The color location values of the provider. - public var locations: [Double] { - didSet { - updateValueArray() - hasUpdate = true - } - } - - // MARK: ValueProvider Protocol - - public var valueType: Any.Type { - [Double].self - } - - public func hasUpdate(frame _: CGFloat) -> Bool { - if block != nil || locationsBlock != nil { - return true - } - return hasUpdate - } - - public func value(frame: CGFloat) -> Any { - hasUpdate = false - - if let block = block { - let newColors = block(frame) - let newLocations = locationsBlock?(frame) ?? [] - value = value(from: newColors, locations: newLocations) - } - - return value - } - - // MARK: Private - - private var hasUpdate: Bool = true - - private var block: ColorsValueBlock? - private var locationsBlock: ColorLocationsBlock? - private var value: [Double] = [] - - private func value(from colors: [Color], locations: [Double]) -> [Double] { - - var colorValues = [Double]() - var alphaValues = [Double]() - var shouldAddAlphaValues = false - - for i in 0.. i ? locations[i] : - (Double(i) / Double(colors.count - 1)) - - colorValues.append(location) - colorValues.append(colors[i].r) - colorValues.append(colors[i].g) - colorValues.append(colors[i].b) - - alphaValues.append(location) - alphaValues.append(colors[i].a) - } - - return colorValues + (shouldAddAlphaValues ? alphaValues : []) - } - - private func updateValueArray() { - value = value(from: colors, locations: locations) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift deleted file mode 100644 index 29d6a47a..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// PointValueProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import CoreGraphics -import Foundation -/// A `ValueProvider` that returns a CGPoint Value -public final class PointValueProvider: AnyValueProvider { - - // MARK: Lifecycle - - /// Initializes with a block provider - public init(block: @escaping PointValueBlock) { - self.block = block - point = .zero - } - - /// Initializes with a single point. - public init(_ point: CGPoint) { - self.point = point - block = nil - hasUpdate = true - } - - // MARK: Public - - /// Returns a CGPoint for a CGFloat(Frame Time) - public typealias PointValueBlock = (CGFloat) -> CGPoint - - public var point: CGPoint { - didSet { - hasUpdate = true - } - } - - // MARK: ValueProvider Protocol - - public var valueType: Any.Type { - Vector3D.self - } - - public func hasUpdate(frame _: CGFloat) -> Bool { - if block != nil { - return true - } - return hasUpdate - } - - public func value(frame: CGFloat) -> Any { - hasUpdate = false - let newPoint: CGPoint - if let block = block { - newPoint = block(frame) - } else { - newPoint = point - } - return newPoint.vector3dValue - } - - // MARK: Private - - private var hasUpdate: Bool = true - - private var block: PointValueBlock? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift deleted file mode 100644 index 5b639911..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// SizeValueProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import CoreGraphics -import Foundation - -/// A `ValueProvider` that returns a CGSize Value -public final class SizeValueProvider: AnyValueProvider { - - // MARK: Lifecycle - - /// Initializes with a block provider - public init(block: @escaping SizeValueBlock) { - self.block = block - size = .zero - } - - /// Initializes with a single size. - public init(_ size: CGSize) { - self.size = size - block = nil - hasUpdate = true - } - - // MARK: Public - - /// Returns a CGSize for a CGFloat(Frame Time) - public typealias SizeValueBlock = (CGFloat) -> CGSize - - public var size: CGSize { - didSet { - hasUpdate = true - } - } - - // MARK: ValueProvider Protocol - - public var valueType: Any.Type { - Vector3D.self - } - - public func hasUpdate(frame _: CGFloat) -> Bool { - if block != nil { - return true - } - return hasUpdate - } - - public func value(frame: CGFloat) -> Any { - hasUpdate = false - let newSize: CGSize - if let block = block { - newSize = block(frame) - } else { - newSize = size - } - return newSize.vector3dValue - } - - // MARK: Private - - private var hasUpdate: Bool = true - - private var block: SizeValueBlock? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift deleted file mode 100644 index 4662cb27..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/FontProvider/AnimationFontProvider.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// AnimationFontProvider.swift -// Lottie -// -// Created by Brandon Withrow on 8/5/20. -// Copyright © 2020 YurtvilleProds. All rights reserved. -// - -import CoreGraphics -import CoreText -import Foundation - -// MARK: - AnimationFontProvider - -/** - Font provider is a protocol that is used to supply fonts to `AnimationView`. - - */ -public protocol AnimationFontProvider { - func fontFor(family: String, size: CGFloat) -> CTFont? -} - -// MARK: - DefaultFontProvider - -/// Default Font provider. -public final class DefaultFontProvider: AnimationFontProvider { - - // MARK: Lifecycle - - public init() {} - - // MARK: Public - - public func fontFor(family: String, size: CGFloat) -> CTFont? { - CTFontCreateWithName(family as CFString, size, nil) - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift deleted file mode 100644 index 6d7a708e..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/ImageProvider/AnimationImageProvider.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// LottieImageProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import CoreGraphics -import Foundation - -/** - Image provider is a protocol that is used to supply images to `AnimationView`. - - Some animations require a reference to an image. The image provider loads and - provides those images to the `AnimationView`. Lottie includes a couple of - prebuilt Image Providers that supply images from a Bundle, or from a FilePath. - - Additionally custom Image Providers can be made to load images from a URL, - or to Cache images. - */ -public protocol AnimationImageProvider { - func imageForAsset(asset: ImageAsset) -> CGImage? -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift deleted file mode 100644 index 2c33e2b4..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/AnimationTime.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// AnimationTime.swift -// lottie-swift-iOS -// -// Created by Brandon Withrow on 2/6/19. -// - -import CoreGraphics -import Foundation - -/// Defines animation time in Frames (Seconds * Framerate). -public typealias AnimationFrameTime = CGFloat - -/// Defines animation time by a progress from 0 (beginning of the animation) to 1 (end of the animation) -public typealias AnimationProgressTime = CGFloat diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift deleted file mode 100644 index 55fc093d..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Color.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Color.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation - -// MARK: - ColorFormatDenominator - -public enum ColorFormatDenominator { - case One - case OneHundred - case TwoFiftyFive - - var value: Double { - switch self { - case .One: - return 1.0 - case .OneHundred: - return 100.0 - case .TwoFiftyFive: - return 255.0 - } - } -} - -// MARK: - Color - -public struct Color { - - public var r: Double - public var g: Double - public var b: Double - public var a: Double - - public init(r: Double, g: Double, b: Double, a: Double, denominator: ColorFormatDenominator = .One) { - self.r = r / denominator.value - self.g = g / denominator.value - self.b = b / denominator.value - self.a = a / denominator.value - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift deleted file mode 100644 index 8efb3483..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/Primitives/Vectors.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Vectors.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation - -// MARK: - Vector1D - -public struct Vector1D { - - public init(_ value: Double) { - self.value = value - } - - public let value: Double - -} - -// MARK: - Vector3D - -/** - A three dimensional vector. - These vectors are encoded and decoded from [Double] - */ -public struct Vector3D { - - public let x: Double - public let y: Double - public let z: Double - - public init(x: Double, y: Double, z: Double) { - self.x = x - self.y = y - self.z = z - } - -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift deleted file mode 100644 index b3e4e52e..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/TextProvider/AnimationTextProvider.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// AnimationImageProvider.swift -// Lottie_iOS -// -// Created by Alexandr Goncharov on 07/06/2019. -// - -import Foundation - -// MARK: - AnimationTextProvider - -/** - Text provider is a protocol that is used to supply text to `AnimationView`. - */ -public protocol AnimationTextProvider: AnyObject { - func textFor(keypathName: String, sourceText: String) -> String -} - -// MARK: - DictionaryTextProvider - -/// Text provider that simply map values from dictionary -public final class DictionaryTextProvider: AnimationTextProvider { - - // MARK: Lifecycle - - public init(_ values: [String: String]) { - self.values = values - } - - // MARK: Public - - public func textFor(keypathName: String, sourceText: String) -> String { - values[keypathName] ?? sourceText - } - - // MARK: Internal - - let values: [String: String] -} - -// MARK: - DefaultTextProvider - -/// Default text provider. Uses text in the animation file -public final class DefaultTextProvider: AnimationTextProvider { - - // MARK: Lifecycle - - public init() {} - - // MARK: Public - - public func textFor(keypathName _: String, sourceText: String) -> String { - sourceText - } -} diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift deleted file mode 100644 index 0d5e3cc5..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedButton.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// AnimatedButton.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit -/** - An interactive button that plays an animation when pressed. - */ -open class AnimatedButton: AnimatedControl { - - // MARK: Lifecycle - - public override init(animation: Animation) { - super.init(animation: animation) - accessibilityTraits = UIAccessibilityTraits.button - } - - public override init() { - super.init() - accessibilityTraits = UIAccessibilityTraits.button - } - - required public init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - // MARK: Public - - /// Sets the play range for the given UIControlEvent. - public func setPlayRange(fromProgress: AnimationProgressTime, toProgress: AnimationProgressTime, event: UIControl.Event) { - rangesForEvents[event.rawValue] = (from: fromProgress, to: toProgress) - } - - /// Sets the play range for the given UIControlEvent. - public func setPlayRange(fromMarker fromName: String, toMarker toName: String, event: UIControl.Event) { - if - let start = animationView.progressTime(forMarker: fromName), - let end = animationView.progressTime(forMarker: toName) - { - rangesForEvents[event.rawValue] = (from: start, to: end) - } - } - - public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - let _ = super.beginTracking(touch, with: event) - let touchEvent = UIControl.Event.touchDown - if let playrange = rangesForEvents[touchEvent.rawValue] { - animationView.play(fromProgress: playrange.from, toProgress: playrange.to, loopMode: LottieLoopMode.playOnce) - } - return true - } - - public override func endTracking(_ touch: UITouch?, with event: UIEvent?) { - super.endTracking(touch, with: event) - let touchEvent: UIControl.Event - if let touch = touch, bounds.contains(touch.location(in: self)) { - touchEvent = UIControl.Event.touchUpInside - } else { - touchEvent = UIControl.Event.touchUpOutside - } - - if let playrange = rangesForEvents[touchEvent.rawValue] { - animationView.play(fromProgress: playrange.from, toProgress: playrange.to, loopMode: LottieLoopMode.playOnce) - } - } - - // MARK: Fileprivate - - fileprivate var rangesForEvents: [UInt : (from: CGFloat, to: CGFloat)] = - [UIControl.Event.touchUpInside.rawValue : (from: 0, to: 1)] -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift deleted file mode 100644 index e7fb063b..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedControl.swift +++ /dev/null @@ -1,173 +0,0 @@ -// -// AnimatedControl.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -/** - Lottie comes prepacked with a two Animated Controls, `AnimatedSwitch` and - `AnimatedButton`. Both of these controls are built on top of `AnimatedControl` - - `AnimatedControl` is a subclass of `UIControl` that provides an interactive - mechanism for controlling the visual state of an animation in response to - user actions. - - The `AnimatedControl` will show and hide layers depending on the current - `UIControl.State` of the control. - - Users of `AnimationControl` can set a Layer Name for each `UIControl.State`. - When the state is change the `AnimationControl` will change the visibility - of its layers. - - NOTE: Do not initialize directly. This is intended to be subclassed. - */ -open class AnimatedControl: UIControl { - - // MARK: Lifecycle - - // MARK: Initializers - - public init(animation: Animation) { - animationView = AnimationView(animation: animation) - super.init(frame: animation.bounds) - commonInit() - } - - public init() { - animationView = AnimationView() - super.init(frame: .zero) - commonInit() - } - - required public init?(coder aDecoder: NSCoder) { - animationView = AnimationView() - super.init(coder: aDecoder) - commonInit() - } - - // MARK: Open - - // MARK: UIControl Overrides - - open override var isEnabled: Bool { - didSet { - updateForState() - } - } - - open override var isSelected: Bool { - didSet { - updateForState() - } - } - - open override var isHighlighted: Bool { - didSet { - updateForState() - } - } - - open override var intrinsicContentSize: CGSize { - animationView.intrinsicContentSize - } - - open override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - updateForState() - return super.beginTracking(touch, with: event) - } - - open override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - updateForState() - return super.continueTracking(touch, with: event) - } - - open override func endTracking(_ touch: UITouch?, with event: UIEvent?) { - updateForState() - return super.endTracking(touch, with: event) - } - - open override func cancelTracking(with event: UIEvent?) { - updateForState() - super.cancelTracking(with: event) - } - - open func animationDidSet() { - - } - - // MARK: Public - - /// The animation view in which the animation is rendered. - public let animationView: AnimationView - - /// The animation backing the animated control. - public var animation: Animation? { - didSet { - animationView.animation = animation - animationView.bounds = animation?.bounds ?? .zero - setNeedsLayout() - updateForState() - animationDidSet() - } - } - - /// The speed of the animation playback. Defaults to 1 - public var animationSpeed: CGFloat { - set { animationView.animationSpeed = newValue } - get { animationView.animationSpeed } - } - - /// Sets which Animation Layer should be visible for the given state. - public func setLayer(named: String, forState: UIControl.State) { - stateMap[forState.rawValue] = named - updateForState() - } - - /// Sets a ValueProvider for the specified keypath - public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) { - animationView.setValueProvider(valueProvider, keypath: keypath) - } - - // MARK: Internal - - var stateMap: [UInt: String] = [:] - - func updateForState() { - guard let animationLayer = animationView.animationLayer else { return } - if - let layerName = stateMap[state.rawValue], - let stateLayer = animationLayer.layer(for: AnimationKeypath(keypath: layerName)) - { - for layer in animationLayer.animationLayers { - layer.isHidden = true - } - stateLayer.isHidden = false - } else { - for layer in animationLayer.animationLayers { - layer.isHidden = false - } - } - } - - // MARK: Fileprivate - - fileprivate func commonInit() { - animationView.clipsToBounds = false - clipsToBounds = true - animationView.translatesAutoresizingMaskIntoConstraints = false - animationView.backgroundBehavior = .forceFinish - addSubview(animationView) - animationView.contentMode = .scaleAspectFit - animationView.isUserInteractionEnabled = false - animationView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - animationView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true - animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - } -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift deleted file mode 100644 index ed76dcb5..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimatedSwitch.swift +++ /dev/null @@ -1,216 +0,0 @@ -// -// AnimatedSwitch.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -/** - An interactive switch with an 'On' and 'Off' state. When the user taps on the - switch the state is toggled and the appropriate animation is played. - - Both the 'On' and 'Off' have an animation play range associated with their state. - */ -open class AnimatedSwitch: AnimatedControl { - - // MARK: Lifecycle - - public override init(animation: Animation) { - /// Generate a haptic generator if available. - #if os(iOS) - if #available(iOS 10.0, *) { - self.hapticGenerator = HapticGenerator() - } else { - hapticGenerator = NullHapticGenerator() - } - #else - hapticGenerator = NullHapticGenerator() - #endif - super.init(animation: animation) - updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false) - accessibilityTraits = UIAccessibilityTraits.button - } - - public override init() { - /// Generate a haptic generator if available. - #if os(iOS) - if #available(iOS 10.0, *) { - self.hapticGenerator = HapticGenerator() - } else { - hapticGenerator = NullHapticGenerator() - } - #else - hapticGenerator = NullHapticGenerator() - #endif - super.init() - updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false) - accessibilityTraits = UIAccessibilityTraits.button - } - - required public init?(coder aDecoder: NSCoder) { - /// Generate a haptic generator if available. - #if os(iOS) - if #available(iOS 10.0, *) { - self.hapticGenerator = HapticGenerator() - } else { - hapticGenerator = NullHapticGenerator() - } - #else - hapticGenerator = NullHapticGenerator() - #endif - super.init(coder: aDecoder) - accessibilityTraits = UIAccessibilityTraits.button - } - - // MARK: Public - - /// Defines what happens when the user taps the switch while an - /// animation is still in flight - public enum CancelBehavior { - case reverse // default - plays the current animation in reverse - case none // does not update the animation when canceled - } - - /// The cancel behavior for the switch. See CancelBehavior for options - public var cancelBehavior: CancelBehavior = .reverse - - /// The current state of the switch. - public var isOn: Bool { - set { - /// This is forwarded to a private variable because the animation needs to be updated without animation when set externally and with animation when set internally. - guard _isOn != newValue else { return } - updateOnState(isOn: newValue, animated: false, shouldFireHaptics: false) - } - get { - _isOn - } - } - - /// Set the state of the switch and specify animation and haptics - public func setIsOn(_ isOn: Bool, animated: Bool, shouldFireHaptics: Bool = true) { - guard isOn != _isOn else { return } - updateOnState(isOn: isOn, animated: animated, shouldFireHaptics: shouldFireHaptics) - } - - /// Sets the play range for the given state. When the switch is toggled, the animation range is played. - public func setProgressForState( - fromProgress: AnimationProgressTime, - toProgress: AnimationProgressTime, - forOnState: Bool) - { - if forOnState { - onStartProgress = fromProgress - onEndProgress = toProgress - } else { - offStartProgress = fromProgress - offEndProgress = toProgress - } - - updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false) - } - - public override func endTracking(_ touch: UITouch?, with event: UIEvent?) { - super.endTracking(touch, with: event) - updateOnState(isOn: !_isOn, animated: true, shouldFireHaptics: true) - sendActions(for: .valueChanged) - } - - public override func animationDidSet() { - updateOnState(isOn: _isOn, animated: true, shouldFireHaptics: false) - } - - // MARK: Internal - - // MARK: Animation State - - func updateOnState(isOn: Bool, animated: Bool, shouldFireHaptics: Bool) { - _isOn = isOn - var startProgress = isOn ? onStartProgress : offStartProgress - var endProgress = isOn ? onEndProgress : offEndProgress - let finalProgress = endProgress - - if cancelBehavior == .reverse { - let realtimeProgress = animationView.realtimeAnimationProgress - - let previousStateStart = isOn ? offStartProgress : onStartProgress - let previousStateEnd = isOn ? offEndProgress : onEndProgress - if - realtimeProgress.isInRange( - min(previousStateStart, previousStateEnd), - max(previousStateStart, previousStateEnd)) - { - /// Animation is currently in the previous time range. Reverse the previous play. - startProgress = previousStateEnd - endProgress = previousStateStart - } - } - - updateAccessibilityLabel() - - guard animated == true else { - animationView.currentProgress = finalProgress - return - } - - if shouldFireHaptics { - hapticGenerator.generateImpact() - } - - animationView.play(fromProgress: startProgress, toProgress: endProgress, loopMode: LottieLoopMode.playOnce) { finished in - if finished == true { - self.animationView.currentProgress = finalProgress - } - } - } - - // MARK: Fileprivate - - fileprivate var onStartProgress: CGFloat = 0 - fileprivate var onEndProgress: CGFloat = 1 - fileprivate var offStartProgress: CGFloat = 1 - fileprivate var offEndProgress: CGFloat = 0 - fileprivate var _isOn: Bool = false - fileprivate var hapticGenerator: ImpactGenerator - - // MARK: Private - - private func updateAccessibilityLabel() { - accessibilityValue = _isOn ? NSLocalizedString("On", comment: "On") : NSLocalizedString("Off", comment: "Off") - } - -} -#endif - -// MARK: - ImpactGenerator - -protocol ImpactGenerator { - func generateImpact() -} - -// MARK: - NullHapticGenerator - -class NullHapticGenerator: ImpactGenerator { - func generateImpact() { - - } -} - -#if os(iOS) -@available(iOS 10.0, *) -class HapticGenerator: ImpactGenerator { - - // MARK: Internal - - func generateImpact() { - impact.impactOccurred() - } - - // MARK: Fileprivate - - fileprivate let impact = UIImpactFeedbackGenerator(style: .light) -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift deleted file mode 100644 index bc4df54d..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/AnimationSubview.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// AnimationSubview.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -/// A view that can be added to a keypath of an AnimationView -public final class AnimationSubview: UIView { - - var viewLayer: CALayer? { - layer - } - -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift deleted file mode 100644 index 83cbe37c..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/BundleImageProvider.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// LottieBundleImageProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 1/25/19. -// - -import CoreGraphics -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -/** - An `AnimationImageProvider` that provides images by name from a specific bundle. - The BundleImageProvider is initialized with a bundle and an optional searchPath. - */ -public class BundleImageProvider: AnimationImageProvider { - - // MARK: Lifecycle - - /** - Initializes an image provider with a bundle and an optional subpath. - - Provides images for an animation from a bundle. Additionally the provider can - search a specific subpath for the images. - - - Parameter bundle: The bundle containing images for the provider. - - Parameter searchPath: The subpath is a path within the bundle to search for image assets. - - */ - public init(bundle: Bundle, searchPath: String?) { - self.bundle = bundle - self.searchPath = searchPath - } - - // MARK: Public - - public func imageForAsset(asset: ImageAsset) -> CGImage? { - - if - asset.name.hasPrefix("data:"), - let url = URL(string: asset.name), - let data = try? Data(contentsOf: url), - let image = UIImage(data: data) - { - return image.cgImage - } - - let imagePath: String? - /// Try to find the image in the bundle. - if let searchPath = searchPath { - /// Search in the provided search path for the image - var directoryPath = URL(fileURLWithPath: searchPath) - directoryPath.appendPathComponent(asset.directory) - - if let path = bundle.path(forResource: asset.name, ofType: nil, inDirectory: directoryPath.path) { - /// First search for the image in the asset provided sub directory. - imagePath = path - } else if let path = bundle.path(forResource: asset.name, ofType: nil, inDirectory: searchPath) { - /// Try finding the image in the search path. - imagePath = path - } else { - imagePath = bundle.path(forResource: asset.name, ofType: nil) - } - } else { - if let path = bundle.path(forResource: asset.name, ofType: nil, inDirectory: asset.directory) { - /// First search for the image in the asset provided sub directory. - imagePath = path - } else { - /// First search for the image in bundle. - imagePath = bundle.path(forResource: asset.name, ofType: nil) - } - } - - if imagePath == nil { - guard let image = UIImage(named: asset.name, in: bundle, compatibleWith: nil) else { - return nil - } - return image.cgImage - } - - guard let foundPath = imagePath, let image = UIImage(contentsOfFile: foundPath) else { - /// No image found. - return nil - } - return image.cgImage - } - - // MARK: Internal - - let bundle: Bundle - let searchPath: String? -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift deleted file mode 100644 index 73963557..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// CompatibleAnimationKeypath.swift -// Lottie_iOS -// -// Created by Tyler Hedrick on 3/6/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) - -/// An Objective-C compatible wrapper around Lottie's AnimationKeypath -@objc -public final class CompatibleAnimationKeypath: NSObject { - - // MARK: Lifecycle - - /// Creates a keypath from a dot separated string. The string is separated by "." - @objc - public init(keypath: String) { - animationKeypath = AnimationKeypath(keypath: keypath) - } - - /// Creates a keypath from a list of strings. - @objc - public init(keys: [String]) { - animationKeypath = AnimationKeypath(keys: keys) - } - - // MARK: Public - - public let animationKeypath: AnimationKeypath -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift deleted file mode 100644 index 6dd08ae5..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +++ /dev/null @@ -1,325 +0,0 @@ -// -// CompatibleAnimationView.swift -// Lottie_iOS -// -// Created by Tyler Hedrick on 3/6/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -/// An Objective-C compatible wrapper around Lottie's Animation class. -/// Use in tandem with CompatibleAnimationView when using Lottie in Objective-C -@objc -public final class CompatibleAnimation: NSObject { - - // MARK: Lifecycle - - @objc - public init(name: String, bundle: Bundle = Bundle.main) { - self.name = name - self.bundle = bundle - super.init() - } - - // MARK: Internal - - internal var animation: Animation? { - Animation.named(name, bundle: bundle) - } - - @objc - static func named(_ name: String) -> CompatibleAnimation { - CompatibleAnimation(name: name) - } - - // MARK: Private - - private let name: String - private let bundle: Bundle -} - -/// An Objective-C compatible wrapper around Lottie's AnimationView. -@objc -public final class CompatibleAnimationView: UIView { - - // MARK: Lifecycle - - @objc - public init(compatibleAnimation: CompatibleAnimation) { - animationView = AnimationView(animation: compatibleAnimation.animation) - self.compatibleAnimation = compatibleAnimation - super.init(frame: .zero) - commonInit() - } - - @objc - public override init(frame: CGRect) { - animationView = AnimationView() - super.init(frame: frame) - commonInit() - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Public - - @objc - public var compatibleAnimation: CompatibleAnimation? { - didSet { - animationView.animation = compatibleAnimation?.animation - } - } - - @objc - public var loopAnimationCount: CGFloat = 0 { - didSet { - animationView.loopMode = loopAnimationCount == -1 ? .loop : .repeat(Float(loopAnimationCount)) - } - } - - @objc - public override var contentMode: UIView.ContentMode { - set { animationView.contentMode = newValue } - get { animationView.contentMode } - } - - @objc - public var shouldRasterizeWhenIdle: Bool { - set { animationView.shouldRasterizeWhenIdle = newValue } - get { animationView.shouldRasterizeWhenIdle } - } - - @objc - public var currentProgress: CGFloat { - set { animationView.currentProgress = newValue } - get { animationView.currentProgress } - } - - @objc - public var currentTime: TimeInterval { - set { animationView.currentTime = newValue } - get { animationView.currentTime } - } - - @objc - public var currentFrame: CGFloat { - set { animationView.currentFrame = newValue } - get { animationView.currentFrame } - } - - @objc - public var realtimeAnimationFrame: CGFloat { - animationView.realtimeAnimationFrame - } - - @objc - public var realtimeAnimationProgress: CGFloat { - animationView.realtimeAnimationProgress - } - - @objc - public var animationSpeed: CGFloat { - set { animationView.animationSpeed = newValue } - get { animationView.animationSpeed } - } - - @objc - public var respectAnimationFrameRate: Bool { - set { animationView.respectAnimationFrameRate = newValue } - get { animationView.respectAnimationFrameRate } - } - - @objc - public var isAnimationPlaying: Bool { - animationView.isAnimationPlaying - } - - @objc - public func play() { - play(completion: nil) - } - - @objc - public func play(completion: ((Bool) -> Void)?) { - animationView.play(completion: completion) - } - - @objc - public func play( - fromProgress: CGFloat, - toProgress: CGFloat, - completion: ((Bool) -> Void)? = nil) - { - animationView.play( - fromProgress: fromProgress, - toProgress: toProgress, - loopMode: nil, - completion: completion) - } - - @objc - public func play( - fromFrame: CGFloat, - toFrame: CGFloat, - completion: ((Bool) -> Void)? = nil) - { - animationView.play( - fromFrame: fromFrame, - toFrame: toFrame, - loopMode: nil, - completion: completion) - } - - @objc - public func play( - fromMarker: String, - toMarker: String, - completion: ((Bool) -> Void)? = nil) - { - animationView.play( - fromMarker: fromMarker, - toMarker: toMarker, - completion: completion) - } - - @objc - public func stop() { - animationView.stop() - } - - @objc - public func pause() { - animationView.pause() - } - - @objc - public func reloadImages() { - animationView.reloadImages() - } - - @objc - public func forceDisplayUpdate() { - animationView.forceDisplayUpdate() - } - - @objc - public func getValue( - for keypath: CompatibleAnimationKeypath, - atFrame: CGFloat) - -> Any? - { - animationView.getValue( - for: keypath.animationKeypath, - atFrame: atFrame) - } - - @objc - public func logHierarchyKeypaths() { - animationView.logHierarchyKeypaths() - } - - @objc - public func setColorValue(_ color: UIColor, forKeypath keypath: CompatibleAnimationKeypath) - { - var red: CGFloat = 0 - var green: CGFloat = 0 - var blue: CGFloat = 0 - var alpha: CGFloat = 0 - // TODO: Fix color spaces - let colorspace = CGColorSpaceCreateDeviceRGB() - - let convertedColor = color.cgColor.converted(to: colorspace, intent: .defaultIntent, options: nil) - - if let components = convertedColor?.components, components.count == 4 { - red = components[0] - green = components[1] - blue = components[2] - alpha = components[3] - } else { - color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - } - - let valueProvider = ColorValueProvider(Color(r: Double(red), g: Double(green), b: Double(blue), a: Double(alpha))) - animationView.setValueProvider(valueProvider, keypath: keypath.animationKeypath) - } - - @objc - public func getColorValue(for keypath: CompatibleAnimationKeypath, atFrame: CGFloat) -> UIColor? - { - let value = animationView.getValue(for: keypath.animationKeypath, atFrame: atFrame) - guard let colorValue = value as? Color else { - return nil; - } - - return UIColor( - red: CGFloat(colorValue.r), - green: CGFloat(colorValue.g), - blue: CGFloat(colorValue.b), - alpha: CGFloat(colorValue.a)) - } - - @objc - public func addSubview( - _ subview: AnimationSubview, - forLayerAt keypath: CompatibleAnimationKeypath) - { - animationView.addSubview( - subview, - forLayerAt: keypath.animationKeypath) - } - - @objc - public func convert( - rect: CGRect, - toLayerAt keypath: CompatibleAnimationKeypath?) - -> CGRect - { - animationView.convert( - rect, - toLayerAt: keypath?.animationKeypath) ?? .zero - } - - @objc - public func convert( - point: CGPoint, - toLayerAt keypath: CompatibleAnimationKeypath?) - -> CGPoint - { - animationView.convert( - point, - toLayerAt: keypath?.animationKeypath) ?? .zero - } - - @objc - public func progressTime(forMarker named: String) -> CGFloat { - animationView.progressTime(forMarker: named) ?? 0 - } - - @objc - public func frameTime(forMarker named: String) -> CGFloat { - animationView.frameTime(forMarker: named) ?? 0 - } - - // MARK: Private - - private let animationView: AnimationView - - private func commonInit() { - translatesAutoresizingMaskIntoConstraints = false - setUpViews() - } - - private func setUpViews() { - animationView.translatesAutoresizingMaskIntoConstraints = false - addSubview(animationView) - animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true - animationView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - animationView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - } -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift deleted file mode 100644 index 41705edf..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/FilepathImageProvider.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// FilepathImageProvider.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/1/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -/** - Provides an image for a lottie animation from a provided Bundle. - */ -public class FilepathImageProvider: AnimationImageProvider { - - // MARK: Lifecycle - - /** - Initializes an image provider with a specific filepath. - - - Parameter filepath: The absolute filepath containing the images. - - */ - public init(filepath: String) { - self.filepath = URL(fileURLWithPath: filepath) - } - - public init(filepath: URL) { - self.filepath = filepath - } - - // MARK: Public - - public func imageForAsset(asset: ImageAsset) -> CGImage? { - - if - asset.name.hasPrefix("data:"), - let url = URL(string: asset.name), - let data = try? Data(contentsOf: url), - let image = UIImage(data: data) - { - return image.cgImage - } - - let directPath = filepath.appendingPathComponent(asset.name).path - if FileManager.default.fileExists(atPath: directPath) { - return UIImage(contentsOfFile: directPath)?.cgImage - } - - let pathWithDirectory = filepath.appendingPathComponent(asset.directory).appendingPathComponent(asset.name).path - if FileManager.default.fileExists(atPath: pathWithDirectory) { - return UIImage(contentsOfFile: pathWithDirectory)?.cgImage - } - - return nil - } - - // MARK: Internal - - let filepath: URL -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift deleted file mode 100644 index 352bb1c6..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/LottieView.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// LottieView.swift -// lottie-swift-iOS -// -// Created by Brandon Withrow on 2/6/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -//public typealias LottieView = UIView - -open class LottieView: UIView { - - // MARK: Open - - open override var contentMode: UIView.ContentMode { - didSet { - setNeedsLayout() - } - } - - open override func didMoveToWindow() { - super.didMoveToWindow() - animationMovedToWindow() - } - - open override func layoutSubviews() { - super.layoutSubviews() - layoutAnimation() - } - - // MARK: Internal - - var viewLayer: CALayer? { - layer - } - - var screenScale: CGFloat { - UIScreen.main.scale - } - - func layoutAnimation() { - - } - - func animationMovedToWindow() { - - } - - func commonInit() { - contentMode = .scaleAspectFit - clipsToBounds = true - NotificationCenter.default.addObserver( - self, - selector: #selector(animationWillEnterForeground), - name: UIApplication.willEnterForegroundNotification, - object: nil) - NotificationCenter.default.addObserver( - self, - selector: #selector(animationWillMoveToBackground), - name: UIApplication.didEnterBackgroundNotification, - object: nil) - } - - @objc - func animationWillMoveToBackground() { - } - - @objc - func animationWillEnterForeground() { - } - -} -#endif diff --git a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift b/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift deleted file mode 100644 index 4cf335e0..00000000 --- a/test/fixtures/cocoapods/Pods/lottie-ios/Sources/Public/iOS/UIColorExtension.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// UIColorExtension.swift -// lottie-swift -// -// Created by Brandon Withrow on 2/4/19. -// - -import Foundation -#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst) -import UIKit - -extension UIColor { - - public var lottieColorValue: Color { - var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 - getRed(&r, green: &g, blue: &b, alpha: &a) - return Color(r: Double(r), g: Double(g), b: Double(b), a: Double(a)) - } - -} -#endif diff --git a/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj b/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj index 58afa91b..f61ab755 100644 --- a/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj +++ b/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj @@ -6,11 +6,6 @@ objectVersion = 51; objects = { -/* Begin PBXBuildFile section */ - 0CF5F21A69185AE3EF49F2E4 /* Pods_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75118201331B9B43260F206F /* Pods_iosTests.framework */; }; - 206D51D19909DDC2DF2F8F9B /* Pods_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 07BD39C03FE8E8CAFD12089D /* Pods_ios.framework */; }; -/* End PBXBuildFile section */ - /* Begin PBXContainerItemProxy section */ E3999824D112043DE8E88846 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -23,56 +18,10 @@ /* Begin PBXFileReference section */ 07598229C97B25EC09E1E750 /* ios.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = ios.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 07BD39C03FE8E8CAFD12089D /* Pods_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0E0BF18A09067936D0265C86 /* Pods-iosTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosTests.release.xcconfig"; path = "Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig"; sourceTree = ""; }; 56F698D1A2EE3DA1EA36472F /* iosTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = iosTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 75118201331B9B43260F206F /* Pods_iosTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D8C67DC4A1CD159A069C9AEE /* Pods-iosTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosTests.debug.xcconfig"; path = "Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig"; sourceTree = ""; }; - F3A1614067EF4812EDA3D314 /* Pods-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.debug.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.debug.xcconfig"; sourceTree = ""; }; - FC5F267974B29AC83A364346 /* Pods-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.release.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ -/* Begin PBXFrameworksBuildPhase section */ - 179C02C13A01CC36806AC803 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0CF5F21A69185AE3EF49F2E4 /* Pods_iosTests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 1E3BD8F8C521406FB9F27C04 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 206D51D19909DDC2DF2F8F9B /* Pods_ios.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - /* Begin PBXGroup section */ - 036BA08F9EE41F4E16965C82 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 07BD39C03FE8E8CAFD12089D /* Pods_ios.framework */, - 75118201331B9B43260F206F /* Pods_iosTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 253D585F95682170F1B976EF /* Pods */ = { - isa = PBXGroup; - children = ( - F3A1614067EF4812EDA3D314 /* Pods-ios.debug.xcconfig */, - FC5F267974B29AC83A364346 /* Pods-ios.release.xcconfig */, - D8C67DC4A1CD159A069C9AEE /* Pods-iosTests.debug.xcconfig */, - 0E0BF18A09067936D0265C86 /* Pods-iosTests.release.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; 56DF4E8E10A8991E1530FAE8 /* iosTests */ = { isa = PBXGroup; children = ( @@ -102,8 +51,6 @@ AD5CE3D685274DEA767D3FBA /* ios */, 56DF4E8E10A8991E1530FAE8 /* iosTests */, 98DCE871D516BA368C096FB6 /* Products */, - 253D585F95682170F1B976EF /* Pods */, - 036BA08F9EE41F4E16965C82 /* Frameworks */, ); sourceTree = ""; }; @@ -114,10 +61,7 @@ isa = PBXNativeTarget; buildConfigurationList = B09050E69F79472FFA774918 /* Build configuration list for PBXNativeTarget "iosTests" */; buildPhases = ( - C7CB9ED39206B4C10DB34AFF /* [CP] Check Pods Manifest.lock */, 4ADB27C84D37D42F473C6F1A /* Sources */, - 179C02C13A01CC36806AC803 /* Frameworks */, - D0657FBA6383576028DBBEE7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -133,10 +77,7 @@ isa = PBXNativeTarget; buildConfigurationList = 254407FBAE608B768D0F34E8 /* Build configuration list for PBXNativeTarget "ios" */; buildPhases = ( - 45255666F5A9D2C9859414B9 /* [CP] Check Pods Manifest.lock */, 1A12E8A67ABB0C6B7845294E /* Sources */, - 1E3BD8F8C521406FB9F27C04 /* Frameworks */, - ADE380BA7BE133CCE1852A82 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -166,7 +107,6 @@ en, ); mainGroup = DCC682D4A34A47286DC3668D; - productRefGroup = 98DCE871D516BA368C096FB6 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( @@ -176,87 +116,6 @@ }; /* End PBXProject section */ -/* Begin PBXShellScriptBuildPhase section */ - 45255666F5A9D2C9859414B9 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ios-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - ADE380BA7BE133CCE1852A82 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - C7CB9ED39206B4C10DB34AFF /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-iosTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - D0657FBA6383576028DBBEE7 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 1A12E8A67ABB0C6B7845294E /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -285,7 +144,6 @@ /* Begin XCBuildConfiguration section */ 1588C5088418414A4D125A34 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0E0BF18A09067936D0265C86 /* Pods-iosTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; LD_RUNPATH_SEARCH_PATHS = ( @@ -301,7 +159,6 @@ }; 3F06443FC191E03534A2DE7A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D8C67DC4A1CD159A069C9AEE /* Pods-iosTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; LD_RUNPATH_SEARCH_PATHS = ( @@ -317,7 +174,6 @@ }; 94AAA1C5A9C82E2F2012CF9C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FC5F267974B29AC83A364346 /* Pods-ios.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -450,7 +306,6 @@ }; F406B4E41AD59D4051593F2C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F3A1614067EF4812EDA3D314 /* Pods-ios.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; diff --git a/test/fixtures/command/cocoapods.yml b/test/fixtures/command/cocoapods.yml new file mode 100644 index 00000000..c18fc41f --- /dev/null +++ b/test/fixtures/command/cocoapods.yml @@ -0,0 +1,5 @@ +expected_dependency: Chatto +source_path: test/fixtures/cocoapods +cache_path: test/fixtures/cocoapods/.licenses +sources: + cocoapods: true \ No newline at end of file From 02bf91468a167b7bbf8425383ac7488a36e9b98f Mon Sep 17 00:00:00 2001 From: Louis Boudreau Date: Mon, 12 Dec 2022 11:21:21 -0500 Subject: [PATCH 4/5] Remove test for metadata --- test/fixtures/command/cocoapods.yml | 2 +- test/sources/cocoapods_test.rb | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/test/fixtures/command/cocoapods.yml b/test/fixtures/command/cocoapods.yml index c18fc41f..b89148d3 100644 --- a/test/fixtures/command/cocoapods.yml +++ b/test/fixtures/command/cocoapods.yml @@ -2,4 +2,4 @@ expected_dependency: Chatto source_path: test/fixtures/cocoapods cache_path: test/fixtures/cocoapods/.licenses sources: - cocoapods: true \ No newline at end of file + cocoapods: true diff --git a/test/sources/cocoapods_test.rb b/test/sources/cocoapods_test.rb index d7dfab67..372e9ec6 100644 --- a/test/sources/cocoapods_test.rb +++ b/test/sources/cocoapods_test.rb @@ -45,14 +45,6 @@ dep = source.dependencies.detect { |d| d.name == "Chatto" } end end - - it "it adds metadata when available" do - Dir.chdir(fixtures) do - dep = source.dependencies.detect { |d| d.name == "Alamofire" } - assert dep.record["homepage"] != nil - assert dep.record["summary"] != nil - end - end end describe "targets" do From bca6a2ac73e6d3e4d44ac820817889d66e084eb9 Mon Sep 17 00:00:00 2001 From: Louis Boudreau Date: Wed, 21 Dec 2022 18:25:07 -0500 Subject: [PATCH 5/5] Remove unused method --- lib/licensed/sources/cocoapods.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/licensed/sources/cocoapods.rb b/lib/licensed/sources/cocoapods.rb index 9059def6..07444dbe 100644 --- a/lib/licensed/sources/cocoapods.rb +++ b/lib/licensed/sources/cocoapods.rb @@ -53,14 +53,6 @@ def podfile @podfile ||= Pod::Podfile.from_file(config.pwd.join("Podfile")) end - def dependency_metadata(pod) - metadata = JSON.parse(Licensed::Shell.execute("pod", "spec", "cat", "--regex", "^#{pod.root_name}$")) - # The version returned by `pod spec cat` is the most recent version that exists which may not be the one installed. - metadata["version"] = lockfile.version(pod.name).version - metadata["name"] = pod.name - metadata - end - def dependency_path(name) config.pwd.join("Pods/#{name}") end