diff --git a/Santander-Test/Podfile b/Santander-Test/Podfile new file mode 100644 index 00000000..b5bb0780 --- /dev/null +++ b/Santander-Test/Podfile @@ -0,0 +1,25 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'Santander-Test' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + # Pods for Santander-Test + + pod 'Moya' + pod 'StatusAlert' + pod 'IQKeyboardManagerSwift' + pod 'AMPopTip' + + target 'Santander-TestTests' do + inherit! :search_paths + # Pods for testing + end + + target 'Santander-TestUITests' do + inherit! :search_paths + # Pods for testing + end + +end diff --git a/Santander-Test/Podfile.lock b/Santander-Test/Podfile.lock new file mode 100644 index 00000000..15324d25 --- /dev/null +++ b/Santander-Test/Podfile.lock @@ -0,0 +1,38 @@ +PODS: + - Alamofire (4.8.1) + - AMPopTip (3.6.0) + - IQKeyboardManagerSwift (6.2.0) + - Moya (12.0.1): + - Moya/Core (= 12.0.1) + - Moya/Core (12.0.1): + - Alamofire (~> 4.1) + - Result (~> 4.0) + - Result (4.1.0) + - StatusAlert (1.1.0) + +DEPENDENCIES: + - AMPopTip + - IQKeyboardManagerSwift + - Moya + - StatusAlert + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - Alamofire + - AMPopTip + - IQKeyboardManagerSwift + - Moya + - Result + - StatusAlert + +SPEC CHECKSUMS: + Alamofire: 16ce2c353fb72865124ddae8a57c5942388f4f11 + AMPopTip: ebfa860ade372f383e8b3500d3101e8c4a4f73ce + IQKeyboardManagerSwift: b07ccf9d8cafe993dcd6cb794eb4ba34611a0c4e + Moya: cf730b3cd9e005401ef37a85143aa141a12fd38f + Result: bd966fac789cc6c1563440b348ab2598cc24d5c7 + StatusAlert: 4d6dadebf5f7abd5648fa774ef3062f2f95cc4aa + +PODFILE CHECKSUM: 4e150ff8c831fa6b97c7c1e6355aa319f0463e89 + +COCOAPODS: 1.6.0 diff --git a/Santander-Test/Pods/AMPopTip/LICENSE b/Santander-Test/Pods/AMPopTip/LICENSE new file mode 100644 index 00000000..530c63e3 --- /dev/null +++ b/Santander-Test/Pods/AMPopTip/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Andrea Mazzini + +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/Santander-Test/Pods/AMPopTip/README.md b/Santander-Test/Pods/AMPopTip/README.md new file mode 100644 index 00000000..2ae0b934 --- /dev/null +++ b/Santander-Test/Pods/AMPopTip/README.md @@ -0,0 +1,266 @@ +

+ +

+ +[![Build Status](https://travis-ci.org/andreamazz/AMPopTip.svg)](https://travis-ci.org/andreamazz/AMPopTip) +[![codecov](https://codecov.io/gh/andreamazz/AMPopTip/branch/master/graph/badge.svg)](https://codecov.io/gh/andreamazz/AMPopTip) +[![CocoaPods](https://cocoapod-badges.herokuapp.com/v/AMPopTip/badge.svg)](http://cocoapods.org/?q=ampoptip) +[![Docs](https://img.shields.io/cocoapods/metrics/doc-percent/AMPopTip.svg)](http://cocoadocs.org/docsets/AMPopTip) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +![Swift 3.0](https://img.shields.io/badge/swift-4.2-orange.svg) +[![Join the chat at https://gitter.im/andreamazz/AMPopTip](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/andreamazz/AMPopTip?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Animated popover that pops out of a frame. You can specify the direction of the popover and the arrow that points to its origin. Color, border radius and font can be easily customized. +This popover can be used to leave subtle hints about your UI and provide fun looking onboarding popups. + +

+ + + +

+ +# Screenshot +![AMPopTip](https://raw.githubusercontent.com/andreamazz/AMPopTip/master/assets/screenshot.gif) + +### Versioning notes + +With version `2.0.0` the library was re-written in Swift, and the API was slightly updated. Checkout version `1.5.x` for the previous Objective-C implementation. + +Version `3.0.0` introduces Swift 4 support, `3.5.0` Swift 4.2. + +# Setup with CocoaPods +* Add ```pod 'AMPopTip'``` to your ```Podfile``` +* Run ```pod install``` +* Run ```open App.xcworkspace``` + +# Setup with Carthage +* Add ```github "andreamazz/AMPopTip"``` +* Run ```carthage update``` +* Add ```AMPopTip.framework``` in your project + +# Usage +The API is fairly straight forward, you can show and hide the popover at any time. + +## Showing the popover +You must specify the text that you want to display alongside the popover direction, its max width, the view that will contain it and the frame of the view that the popover's arrow will point to. + +```swift +let popTip = PopTip() +popTip.show(text: "Hey! Listen!", direction: .up, maxWidth: 200, in: view, from: someView.frame) +``` + +You can also display the popover in the center, with no arrow, in this case the `from` can be the whole view: +```swift +popTip.show(text: "Hey! Listen!", direction: .none, maxWidth: 200, in: view, from: view.frame) +``` + +## Coordinate system +Please note that the frame you are intended to provide needs to refer to the absolute coordinate system of the view you are presenting the popover in. This means that if you are presenting the popover in a view, pointing to a nested subview, you'll need to convert its frame using UIKit's `convertRect(_:toView:)`. Read the reference [here](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instm/UIView/convertRect:toView:). + +## Showing a custom view +You can provide a custom view that will be wrapped in the PopTip and presented. + +```swift +let customView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) +// Configure your view +popTip.show(customView: customView, direction: .down, in: view, from: someView.frame) +``` + +## Dismissing the popover + +You can hide the popover by calling: +```swift +popTip.hide() +``` + +Or you can specify the duration of the popover: +```swift +popTip.show(text: "Hey! Listen!", direction: .up, maxWidth: 200, in: view, from: someView.frame, duration: 3) +``` + +You can also let the user dismiss the popover by tapping on it: +```swift +popTip.shouldDismissOnTap = true +``` + +You can add a block that will be fired when the user taps the PopTip... +```swift +popTip.tapHandler = { popTip in + print("\(popTip) tapped") +} +``` + +... when the popover is shown... +```swift +popTip.appearHandler = { popTip in + print("\(popTip) appeared") +}; +``` + +... or when the popover is dismissed: +```swift +popTip.dismissHandler = { popTip in + print("\(popTip) dismissed") +} + +popTip.tapOutsideHandler = { _ in + print("tap outside") +} + +popTip.swipeOutsideHandler = { _ in + print("swipe outside") +} +``` + +# Updating the PopTip + +You can update the text, attributed text, or custom view to a PopTip already visible: + +```swift +popTip.update(text: "New string") +popTip.update(attributedText: someAttributedString) +popTip.update(customView: someView) +``` + +The position can also be changed by updating the `from` property: + +```swift +let here = CGRect(x: 100, 100, 10, 10) +let there = CGRect(x: 400, 400, 10, 10) + +popTip.show(text: "Hey! Listen!", direction: .up, maxWidth: 200, in: view, from: here) +DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + popTip.from = there +} +``` + +# Custom entrance animation + +You can choose which animation should be performed when the popTip is displayed: +```swift +popTip.entranceAnimation = .scale; +``` + +Available animations: +```swift +PopTipEntranceAnimation.scale, +PopTipEntranceAnimation.transition, +PopTipEntranceAnimation.none, +PopTipEntranceAnimation.custom +``` + +## PopTipEntranceAnimation.custom + +You can provide your own animation block when using `PopTipEntranceAnimation.custom`: +```swift +popTip.entranceAnimationHandler = { [weak self] completion in + guard let `self` = self else { return } + self.popTip.transform = CGAffineTransform(rotationAngle: 0.3) + UIView.animate(withDuration: 0.5, animations: { + self.popTip.transform = .identity + }, completion: { (_) in + completion() + }) +} +``` +This sample makes the PopTip rotate on entrance. Make sure to call the completion block when the animation is done. Also note that the animation is fired as soon as the PopTip is added as subview. + +# Action animations +Action animations are subtle animations that can be performed to get the user's attention. +Set your preferred animation: +```swift +popTip.actionAnimation = .bounce() +``` + +Available animations: +```swift +PopTipActionAnimation.bounce, +PopTipActionAnimation.float, +PopTipActionAnimation.pulse, +PopTipActionAnimation.none +``` +The animation is fired as soon as the popover enters the scene and completes its entrance animation, if `startActionAnimationOnShow` is set to true. + +## Customize the animations + +You can pass a custom value as an associated value to customize the action animation: + +```swift +popTip.actionAnimation = .bounce(16) // This will bounce for 16px instead of the default value +``` + +![AMPopTip bounce](assets/bounce_effect.gif) + +# Customizing the arrow position + +The arrow is centered by default, and moves to avoid the edge of the screen. You can manually change the offset from the center using the `bubbleOffset` property. + +# A note about subviews + +The popover is presented inside the view provided in the `in` parameter. If this view is smaller than the resulting popover, to prevent clipping set `clipsToBounds = false` on the presenting view, and set `constrainInContainerView = false` to the pop tip instance. See #175 for more context. + +# Customization + +Use the appearance proxy to customize the popover before creating the instance, or just use its public properties: +```swift +textColor = <#UIColor#>; +textAlignment = <#NSTextAlignment#> +bubbleColor = <#UIColor#> +borderColor = <#UIColor#> +borderWidth = <#CGFloat#> +cornerRadius = <#CGFloat#> // Popover's border radius +rounded = <#Bool#> // If set to YES the radius will equal frame.height / 2 +offset = <#CGFloat#> // Offset between the popover and the origin +font = <#UIFont#> +padding = <#CGFloat#> +edgeInsets = <#UIEdgeInsets#> +arrowSize = <#CGSize#> +animationIn = <#TimeInterval#> +animationOut = <#TimeInterval#> +delayIn = <#TimeInterval#> +delayOut = <#TimeInterval#> +entranceAnimation = <#PopTipEntranceAnimation#> +actionAnimation = <#PopTipActionAnimation#> +actionAnimationIn = <#TimeInterval#> +actionAnimationOut = <#TimeInterval#> +actionDelayIn = <#TimeInterval#> +actionDelayOut = <#TimeInterval#> +edgeMargin = <#CGFloat#> +bubbleOffset = <#CGFloat#> // Offset between the bubble and the arrow +arrowRadius = <#CGFloat#> +shadowOpacity = <#Float#> +shadowRadius = <#Float#> +shadowOffset = <#CGSize#> +shadowColor = <#UIColor#> +``` + +# Author +[Andrea Mazzini](https://twitter.com/theandreamazz). I'm available for freelance work, feel free to contact me. + +Want to support the development of [these free libraries](https://cocoapods.org/owners/734)? Buy me a coffee ☕️ via [Paypal](https://www.paypal.me/andreamazzini). + +# Contributors +Thanks to [everyone](https://github.com/andreamazz/AMPopTip/graphs/contributors) kind enough to submit a pull request. + +# MIT License + + Copyright (c) 2017 Andrea Mazzini. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Santander-Test/Pods/AMPopTip/Source/PopTip+Draw.swift b/Santander-Test/Pods/AMPopTip/Source/PopTip+Draw.swift new file mode 100644 index 00000000..b6a2d6c5 --- /dev/null +++ b/Santander-Test/Pods/AMPopTip/Source/PopTip+Draw.swift @@ -0,0 +1,190 @@ +// +// PopTip.swift +// AMPopTip +// +// Created by Andrea Mazzini on 01/05/2017. +// Copyright © 2017 Andrea Mazzini. All rights reserved. +// + +import UIKit + +fileprivate struct CornerPoint { + var center: CGPoint + var startAngle: CGFloat + var endAngle: CGFloat +} + +// MARK: - Draw helper +public extension PopTip { + class func pathWith(rect: CGRect, frame: CGRect, direction: PopTipDirection, arrowSize: CGSize, arrowPosition: CGPoint, arrowRadius: CGFloat, borderWidth: CGFloat = 0, radius: CGFloat = 0) -> UIBezierPath { + var path = UIBezierPath() + var baloonFrame = CGRect.zero + + switch direction { + case .none: + baloonFrame = CGRect(x: borderWidth, y: borderWidth, width: frame.width - borderWidth * 2, height: frame.height - borderWidth * 2) + path = UIBezierPath(roundedRect: baloonFrame, cornerRadius: radius) + case .down: + baloonFrame = CGRect(x: 0, y: arrowSize.height, width: rect.width - borderWidth * 2, height: rect.height - arrowSize.height - borderWidth * 2) + + let arrowStartPoint = CGPoint(x: arrowPosition.x - arrowSize.width / 2, y: arrowPosition.y + arrowSize.height) + let arrowEndPoint = CGPoint(x: arrowPosition.x + arrowSize.width / 2, y: arrowPosition.y + arrowSize.height) + let arrowVertex = arrowPosition + let cornerPoint = self.roundCornerCircleCenter(start: arrowStartPoint, vertex: arrowVertex, end: arrowEndPoint, radius: arrowRadius) + + // 1: Arrow starting point + path.move(to: CGPoint(x: arrowStartPoint.x, y: arrowStartPoint.y)) + // 2: Arrow vertex arc + path.addArc(withCenter: cornerPoint.center, radius: arrowRadius, startAngle: cornerPoint.startAngle, endAngle: cornerPoint.endAngle, clockwise: true) + // 3: End drawing arrow + path.addLine(to: CGPoint(x: arrowEndPoint.x, y: arrowEndPoint.y)) + // 4: Top right line + path.addLine(to: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY)) + // 5: Top right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY + radius), radius:radius, startAngle: CGFloat.pi * 1.5, endAngle: 0, clockwise:true) + // 6: Right line + path.addLine(to: CGPoint(x: baloonFrame.width, y: baloonFrame.maxY - radius - borderWidth)) + // 7: Bottom right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.maxX - radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true) + // 8: Bottom line + path.addLine(to: CGPoint(x: baloonFrame.minX + radius + borderWidth, y: baloonFrame.maxY)) + // 9: Bottom left arc + path.addArc(withCenter: CGPoint(x: borderWidth + radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true) + // 10: Left line + path.addLine(to: CGPoint(x: borderWidth, y: baloonFrame.minY + radius + borderWidth)) + // 11: Top left arc + path.addArc(withCenter: CGPoint(x: borderWidth + radius, y: baloonFrame.minY + radius), radius:radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 1.5, clockwise: true) + // 13: Close path + path.close() + + case .up: + baloonFrame = CGRect(x: 0, y: 0, width: rect.size.width - borderWidth * 2, height: rect.size.height - arrowSize.height - borderWidth * 2) + + let arrowStartPoint = CGPoint(x: arrowPosition.x + arrowSize.width/2, y: arrowPosition.y - arrowSize.height) + let arrowEndPoint = CGPoint(x: arrowPosition.x - arrowSize.width/2, y: arrowPosition.y - arrowSize.height) + let arrowVertex = arrowPosition + let cornerPoint = self.roundCornerCircleCenter(start: arrowStartPoint, vertex: arrowVertex, end: arrowEndPoint, radius: arrowRadius) + + // 1: Arrow starting point + path.move(to: CGPoint(x: arrowStartPoint.x, y: arrowStartPoint.y)) + // 2: Arrow vertex arc + path.addArc(withCenter: cornerPoint.center, radius: arrowRadius, startAngle: cornerPoint.startAngle, endAngle: cornerPoint.endAngle, clockwise: true) + // 3: End drawing arrow + path.addLine(to: CGPoint(x: arrowEndPoint.x, y: arrowEndPoint.y)) + // 4: Bottom left line + path.addLine(to: CGPoint(x: baloonFrame.minX + radius + borderWidth, y: baloonFrame.maxY)) + // 5: Bottom left arc + path.addArc(withCenter: CGPoint(x: borderWidth + radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true) + // 6: Left line + path.addLine(to: CGPoint(x: borderWidth, y: baloonFrame.minY + radius + borderWidth)) + // 7: Top left arc + path.addArc(withCenter: CGPoint(x: baloonFrame.minX + radius + borderWidth, y: baloonFrame.minY + radius), radius:radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 1.5, clockwise: true) + // 8: Top line + path.addLine(to: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY)) + // 9: Top right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY + radius), radius:radius, startAngle: CGFloat.pi * 1.5, endAngle: 0, clockwise:true) + // 10: Right line + path.addLine(to: CGPoint(x: baloonFrame.width, y: baloonFrame.maxY - radius - borderWidth)) + // 11: Bottom right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.maxX - radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true) + // 12: Close path + path.close() + + case .left: + baloonFrame = CGRect(x: 0, y: 0, width: rect.size.width - arrowSize.height - borderWidth * 2, height: rect.size.height - borderWidth * 2) + + let arrowStartPoint = CGPoint(x: arrowPosition.x - arrowSize.height, y: arrowPosition.y - arrowSize.width / 2) + let arrowEndPoint = CGPoint(x: arrowPosition.x - arrowSize.height, y: arrowPosition.y + arrowSize.width / 2) + let arrowVertex = arrowPosition + let cornerPoint = self.roundCornerCircleCenter(start: arrowStartPoint, vertex: arrowVertex, end: arrowEndPoint, radius: arrowRadius) + + // 1: Arrow starting point + path.move(to: CGPoint(x: arrowStartPoint.x, y: arrowStartPoint.y)) + // 2: Arrow vertex arc + path.addArc(withCenter: cornerPoint.center, radius: arrowRadius, startAngle: cornerPoint.startAngle, endAngle: cornerPoint.endAngle, clockwise: true) + // 3: End drawing arrow + path.addLine(to: CGPoint(x: arrowEndPoint.x, y: arrowEndPoint.y)) + // 4: Right bottom line + path.addLine(to: CGPoint(x: baloonFrame.width, y: baloonFrame.maxY - radius - borderWidth)) + // 5: Bottom right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.maxX - radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true) + // 6: Bottom line + path.addLine(to: CGPoint(x: baloonFrame.minX + radius + borderWidth, y: baloonFrame.maxY)) + // 7: Bottom left arc + path.addArc(withCenter: CGPoint(x: borderWidth + radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true) + // 8: Left line + path.addLine(to: CGPoint(x: borderWidth, y: baloonFrame.minY + radius + borderWidth)) + // 9: Top left arc + path.addArc(withCenter: CGPoint(x: borderWidth + radius, y: baloonFrame.minY + radius + borderWidth), radius:radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 1.5, clockwise: true) + // 10: Top line + path.addLine(to: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY + borderWidth)) + // 11: Top right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY + radius + borderWidth), radius:radius, startAngle: CGFloat.pi * 1.5, endAngle: 0, clockwise:true) + // 12: Close path + path.close() + + case .right: + baloonFrame = CGRect(x: arrowSize.height, y: 0, width: rect.size.width - arrowSize.height - borderWidth * 2, height: rect.size.height - borderWidth * 2) + + let arrowStartPoint = CGPoint(x: arrowPosition.x + arrowSize.height, y: arrowPosition.y + arrowSize.width / 2) + let arrowEndPoint = CGPoint(x: arrowPosition.x + arrowSize.height, y: arrowPosition.y - arrowSize.width / 2) + let arrowVertex = arrowPosition + let cornerPoint = self.roundCornerCircleCenter(start: arrowStartPoint, vertex: arrowVertex, end: arrowEndPoint, radius: arrowRadius) + + // 1: Arrow starting point + path.move(to: CGPoint(x: arrowStartPoint.x, y: arrowStartPoint.y)) + // 2: Arrow vertex arc + path.addArc(withCenter: cornerPoint.center, radius: arrowRadius, startAngle: cornerPoint.startAngle, endAngle: cornerPoint.endAngle, clockwise: true) + // 3: End drawing arrow + path.addLine(to: CGPoint(x: arrowEndPoint.x, y: arrowEndPoint.y)) + // 6: Left top line + path.addLine(to: CGPoint(x: baloonFrame.minX, y: baloonFrame.minY + radius + borderWidth)) + // 7: Top left arc + path.addArc(withCenter: CGPoint(x: baloonFrame.minX + radius, y: baloonFrame.minY + radius + borderWidth), radius:radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 1.5, clockwise: true) + // 8: Top line + path.addLine(to: CGPoint(x: baloonFrame.width - radius, y: baloonFrame.minY + borderWidth)) + // 9: Top right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.maxX - radius, y: baloonFrame.minY + radius + borderWidth), radius:radius, startAngle: CGFloat.pi * 1.5, endAngle: 0, clockwise:true) + // 10: Right line + path.addLine(to: CGPoint(x: baloonFrame.maxX, y: baloonFrame.maxY - radius)) + // 11: Bottom right arc + path.addArc(withCenter: CGPoint(x: baloonFrame.maxX - radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true) + // 4: Bottom line + path.addLine(to: CGPoint(x: baloonFrame.minX + radius, y: baloonFrame.maxY )) + // 5: Bottom left arc + path.addArc(withCenter: CGPoint(x: baloonFrame.minX + radius, y: baloonFrame.maxY - radius), radius:radius, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true) + path.close() + } + + return path + } + + private class func roundCornerCircleCenter(start: CGPoint, vertex: CGPoint, end: CGPoint, radius: CGFloat) -> CornerPoint { + + let firstLineAngle: CGFloat = atan2(vertex.y - start.y , vertex.x - start.x) + let secondLineAngle: CGFloat = atan2(end.y - vertex.y , end.x - vertex.x) + + let firstLineOffset = CGVector(dx: -sin(firstLineAngle) * radius, dy: cos(firstLineAngle) * radius) + let secondLineOffset = CGVector(dx: -sin(secondLineAngle) * radius, dy: cos(secondLineAngle) * radius) + + let x1 = start.x + firstLineOffset.dx + let y1 = start.y + firstLineOffset.dy + + let x2 = vertex.x + firstLineOffset.dx + let y2 = vertex.y + firstLineOffset.dy + + let x3 = vertex.x + secondLineOffset.dx + let y3 = vertex.y + secondLineOffset.dy + + let x4 = end.x + secondLineOffset.dx + let y4 = end.y + secondLineOffset.dy + + let intersectionX = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)) + let intersectionY = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)) + + return CornerPoint(center: CGPoint(x: intersectionX, y: intersectionY), + startAngle: firstLineAngle - CGFloat.pi / 2, + endAngle: secondLineAngle - CGFloat.pi / 2) + } +} + diff --git a/Santander-Test/Pods/AMPopTip/Source/PopTip+Transitions.swift b/Santander-Test/Pods/AMPopTip/Source/PopTip+Transitions.swift new file mode 100644 index 00000000..72b132a3 --- /dev/null +++ b/Santander-Test/Pods/AMPopTip/Source/PopTip+Transitions.swift @@ -0,0 +1,131 @@ +// +// PopTip.swift +// AMPopTip +// +// Created by Andrea Mazzini on 01/05/2017. +// Copyright © 2017 Andrea Mazzini. All rights reserved. +// + +import UIKit + +public extension PopTip { + + /// Triggers the chosen entrance animation + /// + /// - Parameter completion: the completion handler + public func performEntranceAnimation(completion: @escaping () -> Void) { + switch entranceAnimation { + case .scale: + entranceScale(completion: completion) + case .transition: + entranceTransition(completion: completion) + case .fadeIn: + entranceFadeIn(completion: completion) + case .custom: + if let backgroundMask = backgroundMask { + containerView?.addSubview(backgroundMask) + } + containerView?.addSubview(self) + entranceAnimationHandler?(completion) + case .none: + if let backgroundMask = backgroundMask { + containerView?.addSubview(backgroundMask) + } + containerView?.addSubview(self) + completion() + } + } + + /// Triggers the chosen exit animation + /// + /// - Parameter completion: the completion handler + public func performExitAnimation(completion: @escaping () -> Void) { + switch exitAnimation { + case .scale: + exitScale(completion: completion) + case .fadeOut: + exitFadeOut(completion: completion) + case .custom: + exitAnimationHandler?(completion) + case .none: + completion() + } + } + + private func entranceTransition(completion: @escaping () -> Void) { + transform = CGAffineTransform(scaleX: 0.6, y: 0.6) + switch direction { + case .up: + transform = transform.translatedBy(x: 0, y: -from.origin.y) + case .down, .none: + transform = transform.translatedBy(x: 0, y: (containerView?.frame.height ?? 0) - from.origin.y) + case .left: + transform = transform.translatedBy(x: from.origin.x, y: 0) + case .right: + transform = transform.translatedBy(x: (containerView?.frame.width ?? 0) - from.origin.x, y: 0) + } + if let backgroundMask = backgroundMask { + containerView?.addSubview(backgroundMask) + } + containerView?.addSubview(self) + + UIView.animate(withDuration: animationIn, delay: delayIn, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.5, options: [.curveEaseInOut, .beginFromCurrentState], animations: { + self.transform = .identity + self.backgroundMask?.alpha = 1 + }) { (_) in + completion() + } + } + + private func entranceScale(completion: @escaping () -> Void) { + transform = CGAffineTransform(scaleX: 0, y: 0) + if let backgroundMask = backgroundMask { + containerView?.addSubview(backgroundMask) + } + containerView?.addSubview(self) + + UIView.animate(withDuration: animationIn, delay: delayIn, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.5, options: [.curveEaseInOut, .beginFromCurrentState], animations: { + self.transform = .identity + self.backgroundMask?.alpha = 1 + }) { (_) in + completion() + } + } + + private func entranceFadeIn(completion: @escaping () -> Void) { + if let backgroundMask = backgroundMask { + containerView?.addSubview(backgroundMask) + } + containerView?.addSubview(self) + + alpha = 0 + UIView.animate(withDuration: animationIn, delay: delayIn, options: [.curveEaseInOut, .beginFromCurrentState], animations: { + self.alpha = 1 + self.backgroundMask?.alpha = 1 + }) { (_) in + completion() + } + } + + private func exitScale(completion: @escaping () -> Void) { + transform = .identity + + UIView.animate(withDuration: animationOut, delay: delayOut, options: [.curveEaseInOut, .beginFromCurrentState], animations: { + self.transform = CGAffineTransform(scaleX: 0.0001, y: 0.0001) + self.backgroundMask?.alpha = 0 + }) { (_) in + completion() + } + } + + private func exitFadeOut(completion: @escaping () -> Void) { + alpha = 1 + + UIView.animate(withDuration: animationOut, delay: delayOut, options: [.curveEaseInOut, .beginFromCurrentState], animations: { + self.alpha = 0 + self.backgroundMask?.alpha = 0 + }) { (_) in + completion() + } + } +} diff --git a/Santander-Test/Pods/AMPopTip/Source/PopTip.swift b/Santander-Test/Pods/AMPopTip/Source/PopTip.swift new file mode 100644 index 00000000..7d92c41e --- /dev/null +++ b/Santander-Test/Pods/AMPopTip/Source/PopTip.swift @@ -0,0 +1,832 @@ +// +// PopTip.swift +// AMPopTip +// +// Created by Andrea Mazzini on 01/05/2017. +// Copyright © 2017 Andrea Mazzini. All rights reserved. +// + +import UIKit + +/// Enum that specifies the direction of the poptip +public enum PopTipDirection { + /// Up, the poptip will appear above the element, arrow pointing down + case up + /// Down, the poptip will appear below the element, arrow pointing up + case down + /// Left, the poptip will appear on the left of the element, arrow pointing right + case left + /// Right, the poptip will appear on the right of the element, arrow pointing left + case right + /// None, the poptip will appear above the element with no arrow + case none +} + +/** Enum that specifies the type of entrance animation. Entrance animations are performed while showing the poptip. + + - `scale`: The poptip scales from 0% to 100% + - `transitions`: The poptip moves in position from the edge of the screen + - `fadeIn`: The poptip fade in + - `custom`: The Animation is provided by the user + - `none`: No Animation + */ +public enum PopTipEntranceAnimation { + /// The poptip scales from 0% to 100% + case scale + /// The poptip moves in position from the edge of the screen + case transition + /// The poptip fades in + case fadeIn + /// The Animation is provided by the user + case custom + /// No Animation + case none +} + +/** Enum that specifies the type of entrance animation. Entrance animations are performed while showing the poptip. + + - `scale`: The poptip scales from 100% to 0% + - `fadeOut`: The poptip fade out + - `custom`: The Animation is provided by the user + - `none`: No Animation + */ +public enum PopTipExitAnimation { + /// The poptip scales from 100% to 0% + case scale + /// The poptip fades out + case fadeOut + /// The Animation is provided by the user + case custom + /// No Animation + case none +} + +/** Enum that specifies the type of action animation. Action animations are performed after the poptip is visible and the entrance animation completed. + + - `bounce(offset: CGFloat?)`: The poptip bounces following its direction. The bounce offset can be provided optionally + - `float(offset: CGFloat?)`: The poptip floats in place. The float offset can be provided optionally + - `pulse(offset: CGFloat?)`: The poptip pulsates by changing its size. The maximum amount of pulse increase can be provided optionally + - `none`: No animation + */ +public enum PopTipActionAnimation { + /// The poptip bounces following its direction. The bounce offset can be provided optionally + case bounce(CGFloat?) + /// The poptip floats in place. The float offset can be provided optionally. Defaults to 8 points + case float(offsetX: CGFloat?, offsetY: CGFloat?) + /// The poptip pulsates by changing its size. The maximum amount of pulse increase can be provided optionally. Defaults to 1.1 (110% of the original size) + case pulse(CGFloat?) + /// No animation + case none +} + +private let DefaultBounceOffset = CGFloat(8) +private let DefaultFloatOffset = CGFloat(8) +private let DefaultPulseOffset = CGFloat(1.1) + +open class PopTip: UIView { + /// The text displayed by the poptip. Can be updated once the poptip is visible + open var text: String? { + didSet { + accessibilityLabel = text + setNeedsLayout() + } + } + /// The `UIFont` used in the poptip's text + open var font = UIFont.systemFont(ofSize: UIFont.systemFontSize) + /// The `UIColor` of the text + @objc open dynamic var textColor = UIColor.white + /// The `NSTextAlignment` of the text + @objc open dynamic var textAlignment = NSTextAlignment.center + /// The `UIColor` for the poptip's background + @objc open dynamic var bubbleColor = UIColor.red + /// The `UIColor` for the poptip's bordedr + @objc open dynamic var borderColor = UIColor.clear + /// The width for the poptip's border + @objc open dynamic var borderWidth = CGFloat(0.0) + /// The `Double` with the poptip's border radius + @objc open dynamic var cornerRadius = CGFloat(4.0) + /// The `BOOL` that determines wether the poptip is rounded. If set to `true` the radius will equal `frame.height / 2` + @objc open dynamic var isRounded = false + /// The `UIColor` with the poptip's shadow color + @objc open dynamic var shadowColor: UIColor = .clear + /// The `CGSize` with the poptip's shadow offset + @objc open dynamic var shadowOffset: CGSize = .zero + /// The `Float` with the poptip's shadow radius + @objc open dynamic var shadowRadius: Float = 0 + /// The `Float` with the poptip's shadow opacity + @objc open dynamic var shadowOpacity: Float = 0 + /// Holds the offset between the poptip and origin + @objc open dynamic var offset = CGFloat(0.0) + /// Holds the CGFloat with the padding used for the inner text + @objc open dynamic var padding = CGFloat(6.0) + /// Holds the insets setting for padding different direction + @objc open dynamic var edgeInsets = UIEdgeInsets.zero + /// Holds the CGSize with the width and height of the arrow + @objc open dynamic var arrowSize = CGSize(width: 8, height: 8) + /// CGfloat value that determines the radius of the vertex for the pointing arrow + @objc open dynamic var arrowRadius = CGFloat(0.0) + /// Holds the NSTimeInterval with the duration of the revealing animation + @objc open dynamic var animationIn: TimeInterval = 0.4 + /// Holds the NSTimeInterval with the duration of the disappearing animation + @objc open dynamic var animationOut: TimeInterval = 0.2 + /// Holds the NSTimeInterval with the delay of the revealing animation + @objc open dynamic var delayIn: TimeInterval = 0 + /// Holds the NSTimeInterval with the delay of the disappearing animation + @objc open dynamic var delayOut: TimeInterval = 0 + /// Holds the enum with the type of entrance animation (triggered once the poptip is shown) + open var entranceAnimation = PopTipEntranceAnimation.scale + /// Holds the enum with the type of exit animation (triggered once the poptip is dismissed) + open var exitAnimation = PopTipExitAnimation.scale + /// Holds the enum with the type of action animation (triggered once the poptip is shown) + open var actionAnimation = PopTipActionAnimation.none + /// Holds the NSTimeInterval with the duration of the action animation + @objc open dynamic var actionAnimationIn: TimeInterval = 1.2 + /// Holds the NSTimeInterval with the duration of the action stop animation + @objc open dynamic var actionAnimationOut: TimeInterval = 1.0 + /// Holds the NSTimeInterval with the delay of the action animation + @objc open dynamic var actionDelayIn: TimeInterval = 0 + /// Holds the NSTimeInterval with the delay of the action animation stop + @objc open dynamic var actionDelayOut: TimeInterval = 0 + /// CGfloat value that determines the leftmost margin from the screen + @objc open dynamic var edgeMargin = CGFloat(0.0) + /// Holds the offset between the bubble and origin + @objc open dynamic var bubbleOffset = CGFloat(0.0) + /// Color of the mask that is going to dim the background when the pop up is visible + @objc open dynamic var maskColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6) + /// Flag to enable or disable background mask + @objc open dynamic var shouldShowMask = false + /// Flag to enable or disable the checks that make sure that the tip does not extend over the container + @objc open dynamic var constrainInContainerView = true + /// Holds the CGrect with the rect the tip is pointing to + open var from = CGRect.zero { + didSet { + setup() + } + } + /// Holds the readonly BOOL with the poptip visiblity. The poptip is considered visible as soon as + /// the animation is complete, and invisible when the subview is removed from its parent. + open var isVisible: Bool { get { return self.superview != nil } } + /// A boolean value that determines whether the poptip is dismissed on tap. + @objc open dynamic var shouldDismissOnTap = true + /// A boolean value that determines whether to dismiss when tapping outside the poptip. + @objc open dynamic var shouldDismissOnTapOutside = true + /// A boolean value that determines whether to dismiss when swiping outside the poptip. + @objc open dynamic var shouldDismissOnSwipeOutside = false + /// A boolean value that determines if the action animation should start automatically when the poptip is shown + @objc open dynamic var startActionAnimationOnShow = true + /// A direction that determines what swipe direction to dismiss when swiping outside the poptip. + /// The default direction is `right` + open var swipeRemoveGestureDirection = UISwipeGestureRecognizer.Direction.right { + didSet { + swipeGestureRecognizer?.direction = swipeRemoveGestureDirection + } + } + /// A block that will be fired when the user taps the poptip. + open var tapHandler: ((PopTip) -> Void)? + /// A block that will be fired when the user taps outside the poptip. + open var tapOutsideHandler: ((PopTip) -> Void)? + /// A block that will be fired when the user swipes outside the poptip. + open var swipeOutsideHandler: ((PopTip) -> Void)? + /// A block that will be fired when the poptip appears. + open var appearHandler: ((PopTip) -> Void)? + /// A block that will be fired when the poptip is dismissed. + open var dismissHandler: ((PopTip) -> Void)? + /// A block that handles the entrance animation of the poptip. Should be provided + /// when using a `PopTipActionAnimationCustom` entrance animation type. + /// Please note that the poptip will be automatically added as a subview before firing the block + /// Remember to call the completion block provided + open var entranceAnimationHandler: ((@escaping () -> Void) -> Void)? + /// A block block that handles the exit animation of the poptip. Should be provided + /// when using a `AMPopTipActionAnimationCustom` exit animation type. + /// Remember to call the completion block provided + open var exitAnimationHandler: ((@escaping () -> Void) -> Void)? + /// The CGPoint originating the arrow. Read only. + open private(set) var arrowPosition = CGPoint.zero + /// A read only reference to the view containing the poptip + open private(set) weak var containerView: UIView? + /// The direction from which the poptip is shown. Read only. + open private(set) var direction = PopTipDirection.none + /// Holds the readonly BOOL with the poptip animation state. + open private(set) var isAnimating: Bool = false + /// The view that dims the background (including the button that triggered PopTip. + /// The mask by appears with fade in effect only. + open private(set) var backgroundMask: UIView? + /// The tap gesture recognizer. Read-only. + open private(set) var tapGestureRecognizer: UITapGestureRecognizer? + fileprivate var attributedText: NSAttributedString? + fileprivate var paragraphStyle = NSMutableParagraphStyle() + fileprivate var tapRemoveGestureRecognizer: UITapGestureRecognizer? + fileprivate var swipeGestureRecognizer: UISwipeGestureRecognizer? + fileprivate var dismissTimer: Timer? + fileprivate var textBounds = CGRect.zero + fileprivate var maxWidth = CGFloat(0) + fileprivate var customView: UIView? + fileprivate var isApplicationInBackground: Bool? + fileprivate var label: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() + private var shouldBounce = false + + /// Setup a poptip oriented vertically (direction .up or .down). Returns the bubble frame and the arrow position + /// + /// - Returns: a tuple with the bubble frame and the arrow position + internal func setupVertically() -> (CGRect, CGPoint) { + guard let containerView = containerView else { return (CGRect.zero, CGPoint.zero) } + + var frame = CGRect.zero + let offset = self.offset * (direction == .up ? -1 : 1) + + frame.size = CGSize(width: textBounds.width + padding * 2 + edgeInsets.horizontal, height: textBounds.height + padding * 2 + edgeInsets.vertical + arrowSize.height) + var x = from.origin.x + from.width / 2 - frame.width / 2 + if x < 0 { x = edgeMargin } + if constrainInContainerView && (x + frame.width > containerView.bounds.width) { + x = containerView.bounds.width - frame.width - edgeMargin + } + + if direction == .down { + frame.origin = CGPoint(x: x, y: from.origin.y + from.height + offset) + } else { + frame.origin = CGPoint(x: x, y: from.origin.y - frame.height + offset) + } + + // Make sure that the bubble doesn't leave the boundaries of the view + var arrowPosition = CGPoint( + x: from.origin.x + from.width / 2 - frame.origin.x, + y: (direction == .up) ? frame.height : from.origin.y + from.height - frame.origin.y + offset + ) + + if bubbleOffset > 0 && arrowPosition.x < bubbleOffset { + bubbleOffset = arrowPosition.x - arrowSize.width + } else if bubbleOffset < 0 && frame.width < abs(bubbleOffset) { + bubbleOffset = -(arrowPosition.x - arrowSize.width) + } else if bubbleOffset < 0 && (frame.origin.x - arrowPosition.x) < abs(bubbleOffset) { + bubbleOffset = -(arrowSize.width + edgeMargin) + } + + if constrainInContainerView { + // Make sure that the bubble doesn't leave the boundaries of the view + let leftSpace = frame.origin.x - containerView.frame.origin.x + let rightSpace = containerView.frame.width - leftSpace - frame.width + + if bubbleOffset < 0 && leftSpace < abs(bubbleOffset) { + bubbleOffset = -leftSpace + edgeMargin + } else if bubbleOffset > 0 && rightSpace < bubbleOffset { + bubbleOffset = rightSpace - edgeMargin + } + } + frame.origin.x += bubbleOffset + frame.size = CGSize(width: frame.width + borderWidth * 2, height: frame.height + borderWidth * 2) + + // Only when the tip is not constrained, make sure to center the frame if the containerView is smaller than the tip + if containerView.frame.width < frame.width, !constrainInContainerView { + frame.origin.x = -frame.width / 2 + containerView.frame.width / 2 + arrowPosition.x += frame.width / 2 - containerView.frame.width / 2 + } + + return (frame, arrowPosition) + } + + /// Setup a poptip oriented horizontally (direction .left or .right). Returns the bubble frame and the arrow position + /// + /// - Returns: a tuple with the bubble frame and the arrow position + internal func setupHorizontally() -> (CGRect, CGPoint) { + guard let containerView = containerView else { return (CGRect.zero, CGPoint.zero) } + + var frame = CGRect.zero + let offset = self.offset * (direction == .left ? -1 : 1) + frame.size = CGSize(width: textBounds.width + padding * 2 + edgeInsets.horizontal + arrowSize.height, height: textBounds.height + padding * 2 + edgeInsets.vertical) + + let x = direction == .left ? from.origin.x - frame.width + offset : from.origin.x + from.width + offset + var y = from.origin.y + from.height / 2 - frame.height / 2 + + if y < 0 { y = edgeMargin } + // Make sure we stay in the view limits except if it has scroll then it must be inside contentview limits not the view + if let containerScrollView = containerView as? UIScrollView { + if y + frame.height > containerScrollView.contentSize.height { + y = containerScrollView.contentSize.height - frame.height - edgeMargin + } + } else { + if y + frame.height > containerView.bounds.height && constrainInContainerView { + y = containerView.bounds.height - frame.height - edgeMargin + } + } + frame.origin = CGPoint(x: x, y: y) + + // Make sure that the bubble doesn't leave the boundaries of the view + let arrowPosition = CGPoint( + x: direction == .left ? from.origin.x - frame.origin.x + offset : from.origin.x + from.width - frame.origin.x + offset, + y: from.origin.y + from.height / 2 - frame.origin.y + ) + + if bubbleOffset > 0 && arrowPosition.y < bubbleOffset { + bubbleOffset = arrowPosition.y - arrowSize.width + } else if bubbleOffset < 0 && frame.height < abs(bubbleOffset) { + bubbleOffset = -(arrowPosition.y - arrowSize.height) + } + + if constrainInContainerView { + let topSpace = frame.origin.y - containerView.frame.origin.y + let bottomSpace = containerView.frame.height - topSpace - frame.height + + if bubbleOffset < 0 && topSpace < abs(bubbleOffset) { + bubbleOffset = -topSpace + edgeMargin + } else if bubbleOffset > 0 && bottomSpace < bubbleOffset { + bubbleOffset = bottomSpace - edgeMargin + } + } + + frame.origin.y += bubbleOffset + frame.size = CGSize(width: frame.width + borderWidth * 2, height: frame.height + borderWidth * 2) + + return (frame, arrowPosition) + } + + /// Checks if the rect with positioning `.none` is inside the container + internal func rectContained(rect: CGRect) -> CGRect { + guard let containerView = containerView, constrainInContainerView else { return rect } + + var finalRect = rect + + // The `.none` positioning implies a rect with the origin in the middle of the poptip + if (rect.origin.x) < containerView.frame.origin.x { + finalRect.origin.x = edgeMargin + } + if (rect.origin.y) < containerView.frame.origin.y { + finalRect.origin.y = edgeMargin + } + if (rect.origin.x + rect.width) > (containerView.frame.origin.x + containerView.frame.width) { + finalRect.origin.x = containerView.frame.origin.x + containerView.frame.width - rect.width - edgeMargin + } + if (rect.origin.y + rect.height) > (containerView.frame.origin.y + containerView.frame.height) { + finalRect.origin.y = containerView.frame.origin.y + containerView.frame.height - rect.height - edgeMargin + } + + return finalRect + } + + fileprivate func textBounds(for text: String?, attributedText: NSAttributedString?, view: UIView?, with font: UIFont, padding: CGFloat, edges: UIEdgeInsets, in maxWidth: CGFloat) -> CGRect { + var bounds = CGRect.zero + if let text = text { + bounds = NSString(string: text).boundingRect(with: CGSize(width: maxWidth, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil) + } + if let attributedText = attributedText { + bounds = attributedText.boundingRect(with: CGSize(width: maxWidth, height: CGFloat.infinity), options: .usesLineFragmentOrigin, context: nil) + } + if let view = view { + bounds = view.frame + } + bounds.origin = CGPoint(x: padding + edges.left, y: padding + edges.top) + return bounds.integral + } + + fileprivate func setup() { + guard let containerView = containerView else { return } + + var rect = CGRect.zero + backgroundColor = .clear + + if direction == .left { + maxWidth = CGFloat.minimum(maxWidth, from.origin.x - padding * 2 - edgeInsets.horizontal - arrowSize.width) + } + if direction == .right { + maxWidth = CGFloat.minimum(maxWidth, containerView.bounds.width - from.origin.x - from.width - padding * 2 - edgeInsets.horizontal - arrowSize.width) + } + + textBounds = textBounds(for: text, attributedText: attributedText, view: customView, with: font, padding: padding, edges: edgeInsets, in: maxWidth) + + switch direction { + case .up: + let dimensions = setupVertically() + rect = dimensions.0 + arrowPosition = dimensions.1 + let anchor = arrowPosition.x / rect.size.width + layer.anchorPoint = CGPoint(x: anchor, y: 1) + layer.position = CGPoint(x: layer.position.x + rect.width * anchor, y: layer.position.y + rect.height / 2) + case .down: + let dimensions = setupVertically() + rect = dimensions.0 + arrowPosition = dimensions.1 + let anchor = arrowPosition.x / rect.size.width + textBounds.origin = CGPoint(x: textBounds.origin.x, y: textBounds.origin.y + arrowSize.height) + layer.anchorPoint = CGPoint(x: anchor, y: 0) + layer.position = CGPoint(x: layer.position.x + rect.width * anchor, y: layer.position.y - rect.height / 2) + case .left: + let dimensions = setupHorizontally() + rect = dimensions.0 + arrowPosition = dimensions.1 + let anchor = arrowPosition.y / rect.height + layer.anchorPoint = CGPoint(x: 1, y: anchor) + layer.position = CGPoint(x: layer.position.x - rect.width / 2, y: layer.position.y + rect.height * anchor) + case .right: + let dimensions = setupHorizontally() + rect = dimensions.0 + arrowPosition = dimensions.1 + textBounds.origin = CGPoint(x: textBounds.origin.x + arrowSize.height, y: textBounds.origin.y) + let anchor = arrowPosition.y / rect.height + layer.anchorPoint = CGPoint(x: 0, y: anchor) + layer.position = CGPoint(x: layer.position.x + rect.width / 2, y: layer.position.y + rect.height * anchor) + case .none: + rect.size = CGSize(width: textBounds.width + padding * 2.0 + edgeInsets.horizontal + borderWidth * 2, height: textBounds.height + padding * 2.0 + edgeInsets.vertical + borderWidth * 2) + rect.origin = CGPoint(x: from.midX - rect.size.width / 2, y: from.midY - rect.height / 2) + rect = rectContained(rect: rect) + arrowPosition = CGPoint.zero + layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) + layer.position = CGPoint(x: from.midX, y: from.midY) + } + + label.frame = textBounds + if label.superview == nil { + addSubview(label) + } + + frame = rect + + if let customView = customView { + customView.frame = textBounds + } + + if !shouldShowMask { + backgroundMask?.removeFromSuperview() + } else { + if backgroundMask == nil { + backgroundMask = UIView() + backgroundMask?.backgroundColor = maskColor + } + backgroundMask?.frame = containerView.bounds + } + + setNeedsDisplay() + + if tapGestureRecognizer == nil { + tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopTip.handleTap(_:))) + tapGestureRecognizer?.cancelsTouchesInView = false + self.addGestureRecognizer(tapGestureRecognizer!) + } + if shouldDismissOnTapOutside && tapRemoveGestureRecognizer == nil { + tapRemoveGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopTip.handleTapOutside(_:))) + } + if shouldDismissOnSwipeOutside && swipeGestureRecognizer == nil { + swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(PopTip.handleSwipeOutside(_:))) + swipeGestureRecognizer?.direction = swipeRemoveGestureDirection + } + + if isApplicationInBackground == nil { + NotificationCenter.default.addObserver(self, selector: #selector(PopTip.handleApplicationActive), name: UIApplication.didBecomeActiveNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PopTip.handleApplicationResignActive), name: UIApplication.willResignActiveNotification, object: nil) + } + } + + /// Custom draw override + /// + /// - Parameter rect: the rect occupied by the view + open override func draw(_ rect: CGRect) { + if isRounded { + let showHorizontally = direction == .left || direction == .right + cornerRadius = (frame.size.height - (showHorizontally ? 0 : arrowSize.height)) / 2 + } + + let path = PopTip.pathWith(rect: rect, frame: frame, direction: direction, arrowSize: arrowSize, arrowPosition: arrowPosition, arrowRadius: arrowRadius, borderWidth: borderWidth, radius: cornerRadius) + + layer.shadowPath = path.cgPath + layer.shadowOpacity = shadowOpacity + layer.shadowRadius = CGFloat(shadowRadius) + layer.shadowOffset = shadowOffset + layer.shadowColor = shadowColor.cgColor + + bubbleColor.setFill() + path.fill() + + borderColor.setStroke() + path.lineWidth = borderWidth + path.stroke() + + paragraphStyle.alignment = textAlignment + + let titleAttributes: [NSAttributedString.Key : Any] = [ + NSAttributedString.Key.paragraphStyle: paragraphStyle, + NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: textColor + ] + + if let text = text { + label.attributedText = NSAttributedString(string: text, attributes: titleAttributes) + } else if let text = attributedText { + label.attributedText = text + } else { + label.attributedText = nil + } + } + + /// Shows an animated poptip in a given view, from a given rectangle. The property `isVisible` will be `true` as soon as the poptip is added to the given view. + /// + /// - Parameters: + /// - text: The text to display + /// - direction: The direction of the poptip in relation to the element that generates it + /// - maxWidth: The maximum width of the poptip. If the poptip won't fit in the given space, this will be overridden. + /// - view: The view that will hold the poptip as a subview. + /// - frame: The originating frame. The poptip's arrow will point to the center of this frame. + /// - duration: Optional time interval that determines when the poptip will self-dismiss. + open func show(text: String, direction: PopTipDirection, maxWidth: CGFloat, in view: UIView, from frame: CGRect, duration: TimeInterval? = nil) { + resetView() + + attributedText = nil + self.text = text + accessibilityLabel = text + self.direction = direction + containerView = view + self.maxWidth = maxWidth + customView?.removeFromSuperview() + customView = nil + from = frame + + show(duration: duration) + } + + /// Shows an animated poptip in a given view, from a given rectangle. The property `isVisible` will be `true` as soon as the poptip is added to the given view. + /// + /// - Parameters: + /// - attributedText: The attributed string to display + /// - direction: The direction of the poptip in relation to the element that generates it + /// - maxWidth: The maximum width of the poptip. If the poptip won't fit in the given space, this will be overridden. + /// - view: The view that will hold the poptip as a subview. + /// - frame: The originating frame. The poptip's arrow will point to the center of this frame. + /// - duration: Optional time interval that determines when the poptip will self-dismiss. + open func show(attributedText: NSAttributedString, direction: PopTipDirection, maxWidth: CGFloat, in view: UIView, from frame: CGRect, duration: TimeInterval? = nil) { + resetView() + + text = nil + self.attributedText = attributedText + accessibilityLabel = attributedText.string + self.direction = direction + containerView = view + self.maxWidth = maxWidth + customView?.removeFromSuperview() + customView = nil + from = frame + + show(duration: duration) + } + + + /// Shows an animated poptip in a given view, from a given rectangle. The property `isVisible` will be `true` as soon as the poptip is added to the given view. + /// + /// - Parameters: + /// - customView: A custom view + /// - direction: The direction of the poptip in relation to the element that generates it + /// - view: The view that will hold the poptip as a subview. + /// - frame: The originating frame. The poptip's arrow will point to the center of this frame. + /// - duration: Optional time interval that determines when the poptip will self-dismiss. + open func show(customView: UIView, direction: PopTipDirection, in view: UIView, from frame: CGRect, duration: TimeInterval? = nil) { + resetView() + + text = nil + attributedText = nil + self.direction = direction + containerView = view + maxWidth = customView.frame.size.width + self.customView?.removeFromSuperview() + self.customView = customView + addSubview(customView) + customView.layoutIfNeeded() + from = frame + + show(duration: duration) + } + + /// Update the current text + /// + /// - Parameter text: the new text + open func update(text: String) { + self.text = text + updateBubble() + } + + /// Update the current text + /// + /// - Parameter attributedText: the new attributs string + open func update(attributedText: NSAttributedString) { + self.attributedText = attributedText + updateBubble() + } + + /// Update the current text + /// + /// - Parameter customView: the new custom view + open func update(customView: UIView) { + self.customView = customView + updateBubble() + } + + /// Hides the poptip and removes it from the view. The property `isVisible` will be set to `false` when the animation is complete and the poptip is removed from the parent view. + /// + /// - Parameter forced: Force the removal, ignoring running animations + @objc open func hide(forced: Bool = false) { + if !forced && isAnimating { + return + } + + resetView() + isAnimating = true + dismissTimer?.invalidate() + dismissTimer = nil + + if let gestureRecognizer = tapRemoveGestureRecognizer { + containerView?.removeGestureRecognizer(gestureRecognizer) + } + if let gestureRecognizer = swipeGestureRecognizer { + containerView?.removeGestureRecognizer(gestureRecognizer) + } + + let completion = { + self.customView?.removeFromSuperview() + self.customView = nil + self.dismissActionAnimation() + self.backgroundMask?.removeFromSuperview() + self.removeFromSuperview() + self.layer.removeAllAnimations() + self.transform = .identity + self.isAnimating = false + self.dismissHandler?(self) + } + + if isApplicationInBackground ?? false { + completion() + } else { + performExitAnimation(completion: completion) + } + } + + /// Makes the poptip perform the action indefinitely. The action animation calls for the user's attention after the poptip is shown + open func startActionAnimation() { + performActionAnimation() + } + + /// Stops the poptip action animation. Does nothing if the poptip wasn't animating in the first place. + /// + /// - Parameter completion: Optional completion block clled once the animation is completed + open func stopActionAnimation(_ completion: (() -> Void)? = nil) { + dismissActionAnimation(completion) + } + + fileprivate func resetView() { + CATransaction.begin() + layer.removeAllAnimations() + CATransaction.commit() + transform = .identity + shouldBounce = false + } + + fileprivate func updateBubble() { + stopActionAnimation { + UIView.animate(withDuration: 0.2, delay: 0, options: [.transitionCrossDissolve, .beginFromCurrentState], animations: { + self.setup() + }) { (_) in + self.startActionAnimation() + } + } + } + + fileprivate func show(duration: TimeInterval? = nil) { + isAnimating = true + dismissTimer?.invalidate() + + setNeedsLayout() + performEntranceAnimation { + if let tapRemoveGesture = self.tapRemoveGestureRecognizer { + self.containerView?.addGestureRecognizer(tapRemoveGesture) + } + if let swipeGesture = self.swipeGestureRecognizer { + self.containerView?.addGestureRecognizer(swipeGesture) + } + + self.appearHandler?(self) + if self.startActionAnimationOnShow { + self.performActionAnimation() + } + self.isAnimating = false + if let duration = duration { + self.dismissTimer = Timer.scheduledTimer(timeInterval: duration, target: self, selector: #selector(PopTip.hide), userInfo: nil, repeats: false) + } + } + } + + @objc fileprivate func handleTap(_ gesture: UITapGestureRecognizer) { + if shouldDismissOnTap { + hide() + } + tapHandler?(self) + } + + @objc fileprivate func handleTapOutside(_ gesture: UITapGestureRecognizer) { + if shouldDismissOnTapOutside { + hide() + } + tapOutsideHandler?(self) + } + + @objc fileprivate func handleSwipeOutside(_ gesture: UITapGestureRecognizer) { + if shouldDismissOnSwipeOutside { + hide() + } + swipeOutsideHandler?(self) + } + + @objc fileprivate func handleApplicationActive() { + isApplicationInBackground = false + } + + @objc fileprivate func handleApplicationResignActive() { + isApplicationInBackground = true + } + + fileprivate func performActionAnimation() { + switch actionAnimation { + case .bounce(let offset): + shouldBounce = true + bounceAnimation(offset: offset ?? DefaultBounceOffset) + case .float(let offsetX, let offsetY): + floatAnimation(offsetX: offsetX ?? DefaultFloatOffset, offsetY: offsetY ?? DefaultFloatOffset) + case .pulse(let offset): + pulseAnimation(offset: offset ?? DefaultPulseOffset) + case .none: + return + } + } + + fileprivate func dismissActionAnimation(_ completion: (() -> Void)? = nil) { + shouldBounce = false + UIView.animate(withDuration: actionAnimationOut / 2, delay: actionDelayOut, options: .beginFromCurrentState, animations: { + self.transform = .identity + }) { (_) in + self.layer.removeAllAnimations() + completion?() + } + } + + fileprivate func bounceAnimation(offset: CGFloat) { + var offsetX = CGFloat(0) + var offsetY = CGFloat(0) + switch direction { + case .up, .none: + offsetY = -offset + case .left: + offsetX = -offset + case .right: + offsetX = offset + case .down: + offsetY = offset + } + + UIView.animate(withDuration: actionAnimationIn / 10, delay: actionDelayIn, options: [.curveEaseIn, .allowUserInteraction, .beginFromCurrentState], animations: { + self.transform = CGAffineTransform(translationX: offsetX, y: offsetY) + }) { (completed) in + if completed { + UIView.animate(withDuration: self.actionAnimationIn - self.actionAnimationIn / 10, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 1, options: [.allowUserInteraction, .beginFromCurrentState], animations: { + self.transform = .identity + }, completion: { (done) in + if self.shouldBounce && done { + self.bounceAnimation(offset: offset) + } + }) + } + } + } + + fileprivate func floatAnimation(offsetX: CGFloat, offsetY: CGFloat) { + var offsetX = offsetX + var offsetY = offsetY + switch direction { + case .up, .none: + offsetY = -offsetY + case .left: + offsetX = -offsetX + default: break + } + + UIView.animate(withDuration: actionAnimationIn / 2, delay: actionDelayIn, options: [.curveEaseInOut, .repeat, .autoreverse, .beginFromCurrentState, .allowUserInteraction], animations: { + self.transform = CGAffineTransform(translationX: offsetX, y: offsetY) + }, completion: nil) + } + + fileprivate func pulseAnimation(offset: CGFloat) { + UIView.animate(withDuration: actionAnimationIn / 2, delay: actionDelayIn, options: [.curveEaseInOut, .allowUserInteraction, .beginFromCurrentState, .autoreverse, .repeat], animations: { + self.transform = CGAffineTransform(scaleX: offset, y: offset) + }, completion: nil) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } +} + +fileprivate extension UIEdgeInsets { + var horizontal: CGFloat { + return self.left + self.right + } + + var vertical: CGFloat { + return self.top + self.bottom + } +} diff --git a/Santander-Test/Pods/Alamofire/LICENSE b/Santander-Test/Pods/Alamofire/LICENSE new file mode 100644 index 00000000..38a301a1 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 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/Santander-Test/Pods/Alamofire/README.md b/Santander-Test/Pods/Alamofire/README.md new file mode 100644 index 00000000..26e364a8 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/README.md @@ -0,0 +1,243 @@ +![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png) + +[![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](https://travis-ci.org/Alamofire/Alamofire) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg)](https://img.shields.io/cocoapods/v/Alamofire.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](https://alamofire.github.io/Alamofire) +[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](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) + +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) + - **Intro -** [Making a Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-a-request), [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), [Parameter Encoding](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#parameter-encoding), [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-manager), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session-delegate), [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) + - **Model Objects -** [Custom Response Serialization](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#custom-response-serialization) + - **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] URL / JSON / plist Parameter Encoding +- [x] Upload File / Data / Stream / MultipartFormData +- [x] Download File using Request or Resume Data +- [x] Authentication with URLCredential +- [x] HTTP Response Validation +- [x] Upload and Download Progress Closures with Progress +- [x] cURL Command Output +- [x] Dynamically Adapt and Retry Requests +- [x] TLS Certificate and Public Key Pinning +- [x] Network Reachability +- [x] Comprehensive Unit and Integration Test Coverage +- [x] [Complete Documentation](https://alamofire.github.io/Alamofire) + +## Component Libraries + +In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem. + +- [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image library including image response serializers, `UIImage` and `UIImageView` extensions, custom image filters, an auto-purging in-memory cache and a priority-based image downloading system. +- [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - Controls the visibility of the network activity indicator on iOS using Alamofire. It contains configurable delay timers to help mitigate flicker and can support `URLSession` instances not managed by Alamofire. + +## Requirements + +- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ +- Xcode 8.3+ +- Swift 3.1+ + +## Migration Guides + +- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md) +- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md) +- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md) + +## Communication +- If you **need help with making network requests**, 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 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. You can install it with the following command: + +```bash +$ gem install cocoapods +``` + +> CocoaPods 1.1+ is required to build Alamofire 4.0+. + +To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '10.0' +use_frameworks! + +target '' do + pod 'Alamofire', '~> 4.7' +end +``` + +Then, run the following command: + +```bash +$ pod install +``` + +### Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. + +You can install Carthage with [Homebrew](https://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "Alamofire/Alamofire" ~> 4.7 +``` + +Run `carthage update` to build the framework and drag the built `Alamofire.framework` into your Xcode project. + +### Swift Package Manager + +The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. It is in early development, but Alamofire does support its use on supported platforms. + +Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. + +#### Swift 3 + +```swift +dependencies: [ + .Package(url: "https://github.com/Alamofire/Alamofire.git", majorVersion: 4) +] +``` + +#### Swift 4 + +```swift +dependencies: [ + .package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.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 OS X. + + > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as either `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS` or `Alamofire watchOS`. + +- And that's it! + + > The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. + +## Open Radars + +The following radars have some effect on the current implementation of Alamofire. + +- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case +- `rdar://26870455` - Background URL Session Configurations do not work in the simulator +- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` +- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+ + +## Resolved Radars + +The following radars have been resolved over time after being filed against the Alamofire project. + +- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage (Resolved on 9/1/17 in Xcode 9 beta 6). + +## FAQ + +### What's the origin of the name Alamofire? + +Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. + +### What logic belongs in a Router vs. a Request Adapter? + +Simple, static data such as paths, parameters and common headers belong in the `Router`. Dynamic data such as an `Authorization` header whose value can changed based on an authentication system belongs in a `RequestAdapter`. + +The reason the dynamic data MUST be placed into the `RequestAdapter` is to support retry operations. When a `Request` is retried, the original request is not rebuilt meaning the `Router` will not be called again. The `RequestAdapter` is called again allowing the dynamic data to be updated on the original request before retrying the `Request`. + +## Credits + +Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases. + +### Security Disclosure + +If you believe you have identified a security vulnerability with Alamofire, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. + +## Donations + +The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization. +Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax free. +Donating to the ASF will enable us to: + +- Pay our 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/Santander-Test/Pods/Alamofire/Source/AFError.swift b/Santander-Test/Pods/Alamofire/Source/AFError.swift new file mode 100644 index 00000000..b163f603 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/AFError.swift @@ -0,0 +1,460 @@ +// +// AFError.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with +/// their own associated reasons. +/// +/// - invalidURL: Returned when a `URLConvertible` type fails to create a valid `URL`. +/// - parameterEncodingFailed: Returned when a parameter encoding object throws an error during the encoding process. +/// - multipartEncodingFailed: Returned when some step in the multipart encoding process fails. +/// - responseValidationFailed: Returned when a `validate()` call fails. +/// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process. +public enum AFError: Error { + /// The underlying reason the parameter encoding error occurred. + /// + /// - missingURL: The URL request did not have a URL to encode. + /// - jsonEncodingFailed: JSON serialization failed with an underlying system error during the + /// encoding process. + /// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during + /// encoding process. + public enum ParameterEncodingFailureReason { + case missingURL + case jsonEncodingFailed(error: Error) + case propertyListEncodingFailed(error: Error) + } + + /// The underlying reason the multipart encoding error occurred. + /// + /// - bodyPartURLInvalid: The `fileURL` provided for reading an encodable body part isn't a + /// file URL. + /// - bodyPartFilenameInvalid: The filename of the `fileURL` provided has either an empty + /// `lastPathComponent` or `pathExtension. + /// - bodyPartFileNotReachable: The file at the `fileURL` provided was not reachable. + /// - bodyPartFileNotReachableWithError: Attempting to check the reachability of the `fileURL` provided threw + /// an error. + /// - bodyPartFileIsDirectory: The file at the `fileURL` provided is actually a directory. + /// - bodyPartFileSizeNotAvailable: The size of the file at the `fileURL` provided was not returned by + /// the system. + /// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided + /// threw an error. + /// - bodyPartInputStreamCreationFailed: An `InputStream` could not be created for the provided `fileURL`. + /// - outputStreamCreationFailed: An `OutputStream` could not be created when attempting to write the + /// encoded data to disk. + /// - outputStreamFileAlreadyExists: The encoded body data could not be writtent disk because a file + /// already exists at the provided `fileURL`. + /// - outputStreamURLInvalid: The `fileURL` provided for writing the encoded body data to disk is + /// not a file URL. + /// - outputStreamWriteFailed: The attempt to write the encoded body data to disk failed with an + /// underlying error. + /// - inputStreamReadFailed: The attempt to read an encoded body part `InputStream` failed with + /// underlying system error. + public enum MultipartEncodingFailureReason { + case bodyPartURLInvalid(url: URL) + case bodyPartFilenameInvalid(in: URL) + case bodyPartFileNotReachable(at: URL) + case bodyPartFileNotReachableWithError(atURL: URL, error: Error) + case bodyPartFileIsDirectory(at: URL) + case bodyPartFileSizeNotAvailable(at: URL) + case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) + case bodyPartInputStreamCreationFailed(for: URL) + + case outputStreamCreationFailed(for: URL) + case outputStreamFileAlreadyExists(at: URL) + case outputStreamURLInvalid(url: URL) + case outputStreamWriteFailed(error: Error) + + case inputStreamReadFailed(error: Error) + } + + /// The underlying reason the response validation error occurred. + /// + /// - dataFileNil: The data file containing the server response did not exist. + /// - dataFileReadFailed: The data file containing the server response could not be read. + /// - missingContentType: The response did not contain a `Content-Type` and the `acceptableContentTypes` + /// provided did not contain wildcard type. + /// - unacceptableContentType: The response `Content-Type` did not match any type in the provided + /// `acceptableContentTypes`. + /// - unacceptableStatusCode: The response status code was not acceptable. + public enum ResponseValidationFailureReason { + case dataFileNil + case dataFileReadFailed(at: URL) + case missingContentType(acceptableContentTypes: [String]) + case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) + case unacceptableStatusCode(code: Int) + } + + /// The underlying reason the response serialization error occurred. + /// + /// - inputDataNil: The server response contained no data. + /// - inputDataNilOrZeroLength: The server response contained no data or the data was zero length. + /// - inputFileNil: The file containing the server response did not exist. + /// - inputFileReadFailed: The file containing the server response could not be read. + /// - stringSerializationFailed: String serialization failed using the provided `String.Encoding`. + /// - jsonSerializationFailed: JSON serialization failed with an underlying system error. + /// - propertyListSerializationFailed: Property list serialization failed with an underlying system error. + public enum ResponseSerializationFailureReason { + case inputDataNil + case inputDataNilOrZeroLength + case inputFileNil + case inputFileReadFailed(at: URL) + case stringSerializationFailed(encoding: String.Encoding) + case jsonSerializationFailed(error: Error) + case propertyListSerializationFailed(error: Error) + } + + case invalidURL(url: URLConvertible) + case parameterEncodingFailed(reason: ParameterEncodingFailureReason) + case multipartEncodingFailed(reason: MultipartEncodingFailureReason) + case responseValidationFailed(reason: ResponseValidationFailureReason) + case responseSerializationFailed(reason: ResponseSerializationFailureReason) +} + +// MARK: - Adapt Error + +struct AdaptError: Error { + let error: Error +} + +extension Error { + var underlyingAdaptError: Error? { return (self as? AdaptError)?.error } +} + +// MARK: - Error Booleans + +extension AFError { + /// Returns whether the AFError is an invalid URL error. + public var isInvalidURLError: Bool { + if case .invalidURL = self { return true } + return false + } + + /// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will + /// contain the associated value. + public var isParameterEncodingError: Bool { + if case .parameterEncodingFailed = self { return true } + return false + } + + /// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties + /// will contain the associated values. + public var isMultipartEncodingError: Bool { + if case .multipartEncodingFailed = self { return true } + return false + } + + /// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`, + /// `responseContentType`, and `responseCode` properties will contain the associated values. + public var isResponseValidationError: Bool { + if case .responseValidationFailed = self { return true } + return false + } + + /// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and + /// `underlyingError` properties will contain the associated values. + public var isResponseSerializationError: Bool { + if case .responseSerializationFailed = self { return true } + return false + } +} + +// MARK: - Convenience Properties + +extension AFError { + /// The `URLConvertible` associated with the error. + public var urlConvertible: URLConvertible? { + switch self { + case .invalidURL(let url): + return url + default: + return nil + } + } + + /// The `URL` associated with the error. + public var url: URL? { + switch self { + case .multipartEncodingFailed(let reason): + return reason.url + default: + return nil + } + } + + /// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`, + /// `.multipartEncodingFailed` or `.responseSerializationFailed` error. + public var underlyingError: Error? { + switch self { + case .parameterEncodingFailed(let reason): + return reason.underlyingError + case .multipartEncodingFailed(let reason): + return reason.underlyingError + case .responseSerializationFailed(let reason): + return reason.underlyingError + default: + return nil + } + } + + /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. + public var acceptableContentTypes: [String]? { + switch self { + case .responseValidationFailed(let reason): + return reason.acceptableContentTypes + default: + return nil + } + } + + /// The response `Content-Type` of a `.responseValidationFailed` error. + public var responseContentType: String? { + switch self { + case .responseValidationFailed(let reason): + return reason.responseContentType + default: + return nil + } + } + + /// The response code of a `.responseValidationFailed` error. + public var responseCode: Int? { + switch self { + case .responseValidationFailed(let reason): + return reason.responseCode + default: + return nil + } + } + + /// The `String.Encoding` associated with a failed `.stringResponse()` call. + public var failedStringEncoding: String.Encoding? { + switch self { + case .responseSerializationFailed(let reason): + return reason.failedStringEncoding + default: + return nil + } + } +} + +extension AFError.ParameterEncodingFailureReason { + var underlyingError: Error? { + switch self { + case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error): + return error + default: + return nil + } + } +} + +extension AFError.MultipartEncodingFailureReason { + var url: URL? { + switch self { + case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url), + .bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url), + .bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url), + .outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url), + .bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _): + return url + default: + return nil + } + } + + var underlyingError: Error? { + switch self { + case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error), + .outputStreamWriteFailed(let error), .inputStreamReadFailed(let error): + return error + default: + return nil + } + } +} + +extension AFError.ResponseValidationFailureReason { + var acceptableContentTypes: [String]? { + switch self { + case .missingContentType(let types), .unacceptableContentType(let types, _): + return types + default: + return nil + } + } + + var responseContentType: String? { + switch self { + case .unacceptableContentType(_, let responseType): + return responseType + default: + return nil + } + } + + var responseCode: Int? { + switch self { + case .unacceptableStatusCode(let code): + return code + default: + return nil + } + } +} + +extension AFError.ResponseSerializationFailureReason { + var failedStringEncoding: String.Encoding? { + switch self { + case .stringSerializationFailed(let encoding): + return encoding + default: + return nil + } + } + + var underlyingError: Error? { + switch self { + case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error): + return error + default: + return nil + } + } +} + +// MARK: - Error Descriptions + +extension AFError: LocalizedError { + public var errorDescription: String? { + switch self { + case .invalidURL(let url): + return "URL is not valid: \(url)" + case .parameterEncodingFailed(let reason): + return reason.localizedDescription + case .multipartEncodingFailed(let reason): + return reason.localizedDescription + case .responseValidationFailed(let reason): + return reason.localizedDescription + case .responseSerializationFailed(let reason): + return reason.localizedDescription + } + } +} + +extension AFError.ParameterEncodingFailureReason { + var localizedDescription: String { + switch self { + case .missingURL: + return "URL request to encode was missing a URL" + case .jsonEncodingFailed(let error): + return "JSON could not be encoded because of error:\n\(error.localizedDescription)" + case .propertyListEncodingFailed(let error): + return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)" + } + } +} + +extension AFError.MultipartEncodingFailureReason { + var localizedDescription: String { + switch self { + case .bodyPartURLInvalid(let url): + return "The URL provided is not a file URL: \(url)" + case .bodyPartFilenameInvalid(let url): + return "The URL provided does not have a valid filename: \(url)" + case .bodyPartFileNotReachable(let url): + return "The URL provided is not reachable: \(url)" + case .bodyPartFileNotReachableWithError(let url, let error): + return ( + "The system returned an error while checking the provided URL for " + + "reachability.\nURL: \(url)\nError: \(error)" + ) + case .bodyPartFileIsDirectory(let url): + return "The URL provided is a directory: \(url)" + case .bodyPartFileSizeNotAvailable(let url): + return "Could not fetch the file size from the provided URL: \(url)" + case .bodyPartFileSizeQueryFailedWithError(let url, let error): + return ( + "The system returned an error while attempting to fetch the file size from the " + + "provided URL.\nURL: \(url)\nError: \(error)" + ) + case .bodyPartInputStreamCreationFailed(let url): + return "Failed to create an InputStream for the provided URL: \(url)" + case .outputStreamCreationFailed(let url): + return "Failed to create an OutputStream for URL: \(url)" + case .outputStreamFileAlreadyExists(let url): + return "A file already exists at the provided URL: \(url)" + case .outputStreamURLInvalid(let url): + return "The provided OutputStream URL is invalid: \(url)" + case .outputStreamWriteFailed(let error): + return "OutputStream write failed with error: \(error)" + case .inputStreamReadFailed(let error): + return "InputStream read failed with error: \(error)" + } + } +} + +extension AFError.ResponseSerializationFailureReason { + var localizedDescription: String { + switch self { + case .inputDataNil: + return "Response could not be serialized, input data was nil." + case .inputDataNilOrZeroLength: + return "Response could not be serialized, input data was nil or zero length." + case .inputFileNil: + return "Response could not be serialized, input file was nil." + case .inputFileReadFailed(let url): + return "Response could not be serialized, input file could not be read: \(url)." + case .stringSerializationFailed(let encoding): + return "String could not be serialized with encoding: \(encoding)." + case .jsonSerializationFailed(let error): + return "JSON could not be serialized because of error:\n\(error.localizedDescription)" + case .propertyListSerializationFailed(let error): + return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)" + } + } +} + +extension AFError.ResponseValidationFailureReason { + var localizedDescription: String { + switch self { + case .dataFileNil: + return "Response could not be validated, data file was nil." + case .dataFileReadFailed(let url): + return "Response could not be validated, data file could not be read: \(url)." + case .missingContentType(let types): + return ( + "Response Content-Type was missing and acceptable content types " + + "(\(types.joined(separator: ","))) do not match \"*/*\"." + ) + case .unacceptableContentType(let acceptableTypes, let responseType): + return ( + "Response Content-Type \"\(responseType)\" does not match any acceptable types: " + + "\(acceptableTypes.joined(separator: ","))." + ) + case .unacceptableStatusCode(let code): + return "Response status code was unacceptable: \(code)." + } + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/Alamofire.swift b/Santander-Test/Pods/Alamofire/Source/Alamofire.swift new file mode 100644 index 00000000..20c3672f --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Alamofire.swift @@ -0,0 +1,465 @@ +// +// Alamofire.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct +/// URL requests. +public protocol URLConvertible { + /// Returns a URL that conforms to RFC 2396 or throws an `Error`. + /// + /// - throws: An `Error` if the type cannot be converted to a `URL`. + /// + /// - returns: A URL or throws an `Error`. + func asURL() throws -> URL +} + +extension String: URLConvertible { + /// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`. + /// + /// - throws: An `AFError.invalidURL` if `self` is not a valid URL string. + /// + /// - returns: A URL or throws an `AFError`. + public func asURL() throws -> URL { + guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } + return url + } +} + +extension URL: URLConvertible { + /// Returns self. + public func asURL() throws -> URL { return self } +} + +extension URLComponents: URLConvertible { + /// Returns a URL if `url` is not nil, otherwise throws an `Error`. + /// + /// - throws: An `AFError.invalidURL` if `url` is `nil`. + /// + /// - returns: A URL or throws an `AFError`. + public func asURL() throws -> URL { + guard let url = url else { throw AFError.invalidURL(url: self) } + return url + } +} + +// MARK: - + +/// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. +public protocol URLRequestConvertible { + /// Returns a URL request or throws if an `Error` was encountered. + /// + /// - throws: An `Error` if the underlying `URLRequest` is `nil`. + /// + /// - returns: A URL request. + func asURLRequest() throws -> URLRequest +} + +extension URLRequestConvertible { + /// The URL request. + public var urlRequest: URLRequest? { return try? asURLRequest() } +} + +extension URLRequest: URLRequestConvertible { + /// Returns a URL request or throws if an `Error` was encountered. + public func asURLRequest() throws -> URLRequest { return self } +} + +// MARK: - + +extension URLRequest { + /// Creates an instance with the specified `method`, `urlString` and `headers`. + /// + /// - parameter url: The URL. + /// - parameter method: The HTTP method. + /// - parameter headers: The HTTP headers. `nil` by default. + /// + /// - returns: The new `URLRequest` instance. + public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { + let url = try url.asURL() + + self.init(url: url) + + httpMethod = method.rawValue + + if let headers = headers { + for (headerField, headerValue) in headers { + setValue(headerValue, forHTTPHeaderField: headerField) + } + } + } + + func adapt(using adapter: RequestAdapter?) throws -> URLRequest { + guard let adapter = adapter else { return self } + return try adapter.adapt(self) + } +} + +// MARK: - Data Request + +/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, +/// `method`, `parameters`, `encoding` and `headers`. +/// +/// - parameter url: The URL. +/// - parameter method: The HTTP method. `.get` by default. +/// - parameter parameters: The parameters. `nil` by default. +/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. +/// - parameter headers: The HTTP headers. `nil` by default. +/// +/// - returns: The created `DataRequest`. +@discardableResult +public func request( + _ url: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil) + -> DataRequest +{ + return SessionManager.default.request( + url, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers + ) +} + +/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the +/// specified `urlRequest`. +/// +/// - parameter urlRequest: The URL request +/// +/// - returns: The created `DataRequest`. +@discardableResult +public func request(_ urlRequest: URLRequestConvertible) -> DataRequest { + return SessionManager.default.request(urlRequest) +} + +// MARK: - Download Request + +// MARK: URL Request + +/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, +/// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`. +/// +/// If `destination` is not specified, the contents will remain in the temporary location determined by the +/// underlying URL session. +/// +/// - parameter url: The URL. +/// - parameter method: The HTTP method. `.get` by default. +/// - parameter parameters: The parameters. `nil` by default. +/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. +/// - parameter headers: The HTTP headers. `nil` by default. +/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. +/// +/// - returns: The created `DownloadRequest`. +@discardableResult +public func download( + _ url: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + to destination: DownloadRequest.DownloadFileDestination? = nil) + -> DownloadRequest +{ + return SessionManager.default.download( + url, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers, + to: destination + ) +} + +/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the +/// specified `urlRequest` and save them to the `destination`. +/// +/// If `destination` is not specified, the contents will remain in the temporary location determined by the +/// underlying URL session. +/// +/// - parameter urlRequest: The URL request. +/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. +/// +/// - returns: The created `DownloadRequest`. +@discardableResult +public func download( + _ urlRequest: URLRequestConvertible, + to destination: DownloadRequest.DownloadFileDestination? = nil) + -> DownloadRequest +{ + return SessionManager.default.download(urlRequest, to: destination) +} + +// MARK: Resume Data + +/// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a +/// previous request cancellation to retrieve the contents of the original request and save them to the `destination`. +/// +/// If `destination` is not specified, the contents will remain in the temporary location determined by the +/// underlying URL session. +/// +/// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken +/// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the +/// data is written incorrectly and will always fail to resume the download. For more information about the bug and +/// possible workarounds, please refer to the following Stack Overflow post: +/// +/// - http://stackoverflow.com/a/39347461/1342462 +/// +/// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask` +/// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional +/// information. +/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. +/// +/// - returns: The created `DownloadRequest`. +@discardableResult +public func download( + resumingWith resumeData: Data, + to destination: DownloadRequest.DownloadFileDestination? = nil) + -> DownloadRequest +{ + return SessionManager.default.download(resumingWith: resumeData, to: destination) +} + +// MARK: - Upload Request + +// MARK: File + +/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` +/// for uploading the `file`. +/// +/// - parameter file: The file to upload. +/// - parameter url: The URL. +/// - parameter method: The HTTP method. `.post` by default. +/// - parameter headers: The HTTP headers. `nil` by default. +/// +/// - returns: The created `UploadRequest`. +@discardableResult +public func upload( + _ fileURL: URL, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil) + -> UploadRequest +{ + return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers) +} + +/// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for +/// uploading the `file`. +/// +/// - parameter file: The file to upload. +/// - parameter urlRequest: The URL request. +/// +/// - returns: The created `UploadRequest`. +@discardableResult +public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest { + return SessionManager.default.upload(fileURL, with: urlRequest) +} + +// MARK: Data + +/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` +/// for uploading the `data`. +/// +/// - parameter data: The data to upload. +/// - parameter url: The URL. +/// - parameter method: The HTTP method. `.post` by default. +/// - parameter headers: The HTTP headers. `nil` by default. +/// +/// - returns: The created `UploadRequest`. +@discardableResult +public func upload( + _ data: Data, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil) + -> UploadRequest +{ + return SessionManager.default.upload(data, to: url, method: method, headers: headers) +} + +/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for +/// uploading the `data`. +/// +/// - parameter data: The data to upload. +/// - parameter urlRequest: The URL request. +/// +/// - returns: The created `UploadRequest`. +@discardableResult +public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest { + return SessionManager.default.upload(data, with: urlRequest) +} + +// MARK: InputStream + +/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` +/// for uploading the `stream`. +/// +/// - parameter stream: The stream to upload. +/// - parameter url: The URL. +/// - parameter method: The HTTP method. `.post` by default. +/// - parameter headers: The HTTP headers. `nil` by default. +/// +/// - returns: The created `UploadRequest`. +@discardableResult +public func upload( + _ stream: InputStream, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil) + -> UploadRequest +{ + return SessionManager.default.upload(stream, to: url, method: method, headers: headers) +} + +/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for +/// uploading the `stream`. +/// +/// - parameter urlRequest: The URL request. +/// - parameter stream: The stream to upload. +/// +/// - returns: The created `UploadRequest`. +@discardableResult +public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest { + return SessionManager.default.upload(stream, with: urlRequest) +} + +// MARK: MultipartFormData + +/// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls +/// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`. +/// +/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative +/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most +/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to +/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory +/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be +/// used for larger payloads such as video content. +/// +/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory +/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, +/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk +/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding +/// technique was used. +/// +/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. +/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. +/// `multipartFormDataEncodingMemoryThreshold` by default. +/// - parameter url: The URL. +/// - parameter method: The HTTP method. `.post` by default. +/// - parameter headers: The HTTP headers. `nil` by default. +/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. +public func upload( + multipartFormData: @escaping (MultipartFormData) -> Void, + usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) +{ + return SessionManager.default.upload( + multipartFormData: multipartFormData, + usingThreshold: encodingMemoryThreshold, + to: url, + method: method, + headers: headers, + encodingCompletion: encodingCompletion + ) +} + +/// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and +/// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`. +/// +/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative +/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most +/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to +/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory +/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be +/// used for larger payloads such as video content. +/// +/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory +/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, +/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk +/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding +/// technique was used. +/// +/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. +/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. +/// `multipartFormDataEncodingMemoryThreshold` by default. +/// - parameter urlRequest: The URL request. +/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. +public func upload( + multipartFormData: @escaping (MultipartFormData) -> Void, + usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, + with urlRequest: URLRequestConvertible, + encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) +{ + return SessionManager.default.upload( + multipartFormData: multipartFormData, + usingThreshold: encodingMemoryThreshold, + with: urlRequest, + encodingCompletion: encodingCompletion + ) +} + +#if !os(watchOS) + +// MARK: - Stream Request + +// MARK: Hostname and Port + +/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname` +/// and `port`. +/// +/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. +/// +/// - parameter hostName: The hostname of the server to connect to. +/// - parameter port: The port of the server to connect to. +/// +/// - returns: The created `StreamRequest`. +@discardableResult +@available(iOS 9.0, macOS 10.11, tvOS 9.0, *) +public func stream(withHostName hostName: String, port: Int) -> StreamRequest { + return SessionManager.default.stream(withHostName: hostName, port: port) +} + +// MARK: NetService + +/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`. +/// +/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. +/// +/// - parameter netService: The net service used to identify the endpoint. +/// +/// - returns: The created `StreamRequest`. +@discardableResult +@available(iOS 9.0, macOS 10.11, tvOS 9.0, *) +public func stream(with netService: NetService) -> StreamRequest { + return SessionManager.default.stream(with: netService) +} + +#endif diff --git a/Santander-Test/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift b/Santander-Test/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift new file mode 100644 index 00000000..a54673cc --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift @@ -0,0 +1,37 @@ +// +// DispatchQueue+Alamofire.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Dispatch +import Foundation + +extension DispatchQueue { + static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } + static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } + static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } + static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } + + func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { + asyncAfter(deadline: .now() + delay, execute: closure) + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/MultipartFormData.swift b/Santander-Test/Pods/Alamofire/Source/MultipartFormData.swift new file mode 100644 index 00000000..9b4189ec --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/MultipartFormData.swift @@ -0,0 +1,580 @@ +// +// MultipartFormData.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +#if os(iOS) || os(watchOS) || os(tvOS) +import MobileCoreServices +#elseif os(macOS) +import CoreServices +#endif + +/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode +/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead +/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the +/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for +/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset. +/// +/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well +/// and the w3 form documentation. +/// +/// - https://www.ietf.org/rfc/rfc2388.txt +/// - https://www.ietf.org/rfc/rfc2045.txt +/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13 +open class MultipartFormData { + + // MARK: - Helper Types + + struct EncodingCharacters { + static let crlf = "\r\n" + } + + struct BoundaryGenerator { + enum BoundaryType { + case initial, encapsulated, final + } + + static func randomBoundary() -> String { + return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random()) + } + + static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data { + let boundaryText: String + + switch boundaryType { + case .initial: + boundaryText = "--\(boundary)\(EncodingCharacters.crlf)" + case .encapsulated: + boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)" + case .final: + boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)" + } + + return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)! + } + } + + class BodyPart { + let headers: HTTPHeaders + let bodyStream: InputStream + let bodyContentLength: UInt64 + var hasInitialBoundary = false + var hasFinalBoundary = false + + init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) { + self.headers = headers + self.bodyStream = bodyStream + self.bodyContentLength = bodyContentLength + } + } + + // MARK: - Properties + + /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. + open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)" + + /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. + public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } } + + /// The boundary used to separate the body parts in the encoded form data. + public let boundary: String + + private var bodyParts: [BodyPart] + private var bodyPartError: AFError? + private let streamBufferSize: Int + + // MARK: - Lifecycle + + /// Creates a multipart form data object. + /// + /// - returns: The multipart form data object. + public init() { + self.boundary = BoundaryGenerator.randomBoundary() + self.bodyParts = [] + + /// + /// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more + /// information, please refer to the following article: + /// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html + /// + + self.streamBufferSize = 1024 + } + + // MARK: - Body Parts + + /// Creates a body part from the data and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header) + /// - Encoded data + /// - Multipart form boundary + /// + /// - parameter data: The data to encode into the multipart form data. + /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. + public func append(_ data: Data, withName name: String) { + let headers = contentHeaders(withName: name) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + append(stream, withLength: length, headers: headers) + } + + /// Creates a body part from the data and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header) + /// - `Content-Type: #{generated mimeType}` (HTTP Header) + /// - Encoded data + /// - Multipart form boundary + /// + /// - parameter data: The data to encode into the multipart form data. + /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. + /// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header. + public func append(_ data: Data, withName name: String, mimeType: String) { + let headers = contentHeaders(withName: name, mimeType: mimeType) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + append(stream, withLength: length, headers: headers) + } + + /// Creates a body part from the data and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) + /// - `Content-Type: #{mimeType}` (HTTP Header) + /// - Encoded file data + /// - Multipart form boundary + /// + /// - parameter data: The data to encode into the multipart form data. + /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. + /// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header. + /// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header. + public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) { + let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + append(stream, withLength: length, headers: headers) + } + + /// Creates a body part from the file and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) + /// - `Content-Type: #{generated mimeType}` (HTTP Header) + /// - Encoded file data + /// - Multipart form boundary + /// + /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the + /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the + /// system associated MIME type. + /// + /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. + /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. + public func append(_ fileURL: URL, withName name: String) { + let fileName = fileURL.lastPathComponent + let pathExtension = fileURL.pathExtension + + if !fileName.isEmpty && !pathExtension.isEmpty { + let mime = mimeType(forPathExtension: pathExtension) + append(fileURL, withName: name, fileName: fileName, mimeType: mime) + } else { + setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL)) + } + } + + /// Creates a body part from the file and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) + /// - Content-Type: #{mimeType} (HTTP Header) + /// - Encoded file data + /// - Multipart form boundary + /// + /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. + /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. + /// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header. + /// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header. + public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) { + let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) + + //============================================================ + // Check 1 - is file URL? + //============================================================ + + guard fileURL.isFileURL else { + setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL)) + return + } + + //============================================================ + // Check 2 - is file URL reachable? + //============================================================ + + do { + let isReachable = try fileURL.checkPromisedItemIsReachable() + guard isReachable else { + setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL)) + return + } + } catch { + setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error)) + return + } + + //============================================================ + // Check 3 - is file URL a directory? + //============================================================ + + var isDirectory: ObjCBool = false + let path = fileURL.path + + guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else { + setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL)) + return + } + + //============================================================ + // Check 4 - can the file size be extracted? + //============================================================ + + let bodyContentLength: UInt64 + + do { + guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else { + setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL)) + return + } + + bodyContentLength = fileSize.uint64Value + } + catch { + setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error)) + return + } + + //============================================================ + // Check 5 - can a stream be created from file URL? + //============================================================ + + guard let stream = InputStream(url: fileURL) else { + setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL)) + return + } + + append(stream, withLength: bodyContentLength, headers: headers) + } + + /// Creates a body part from the stream and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) + /// - `Content-Type: #{mimeType}` (HTTP Header) + /// - Encoded stream data + /// - Multipart form boundary + /// + /// - parameter stream: The input stream to encode in the multipart form data. + /// - parameter length: The content length of the stream. + /// - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header. + /// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header. + /// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header. + public func append( + _ stream: InputStream, + withLength length: UInt64, + name: String, + fileName: String, + mimeType: String) + { + let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) + append(stream, withLength: length, headers: headers) + } + + /// Creates a body part with the headers, stream and length and appends it to the multipart form data object. + /// + /// The body part data will be encoded using the following format: + /// + /// - HTTP headers + /// - Encoded stream data + /// - Multipart form boundary + /// + /// - parameter stream: The input stream to encode in the multipart form data. + /// - parameter length: The content length of the stream. + /// - parameter headers: The HTTP headers for the body part. + public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) { + let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) + bodyParts.append(bodyPart) + } + + // MARK: - Data Encoding + + /// Encodes all the appended body parts into a single `Data` value. + /// + /// It is important to note that this method will load all the appended body parts into memory all at the same + /// time. This method should only be used when the encoded data will have a small memory footprint. For large data + /// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method. + /// + /// - throws: An `AFError` if encoding encounters an error. + /// + /// - returns: The encoded `Data` if encoding is successful. + public func encode() throws -> Data { + if let bodyPartError = bodyPartError { + throw bodyPartError + } + + var encoded = Data() + + bodyParts.first?.hasInitialBoundary = true + bodyParts.last?.hasFinalBoundary = true + + for bodyPart in bodyParts { + let encodedData = try encode(bodyPart) + encoded.append(encodedData) + } + + return encoded + } + + /// Writes the appended body parts into the given file URL. + /// + /// This process is facilitated by reading and writing with input and output streams, respectively. Thus, + /// this approach is very memory efficient and should be used for large body part data. + /// + /// - parameter fileURL: The file URL to write the multipart form data into. + /// + /// - throws: An `AFError` if encoding encounters an error. + public func writeEncodedData(to fileURL: URL) throws { + if let bodyPartError = bodyPartError { + throw bodyPartError + } + + if FileManager.default.fileExists(atPath: fileURL.path) { + throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL)) + } else if !fileURL.isFileURL { + throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL)) + } + + guard let outputStream = OutputStream(url: fileURL, append: false) else { + throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL)) + } + + outputStream.open() + defer { outputStream.close() } + + self.bodyParts.first?.hasInitialBoundary = true + self.bodyParts.last?.hasFinalBoundary = true + + for bodyPart in self.bodyParts { + try write(bodyPart, to: outputStream) + } + } + + // MARK: - Private - Body Part Encoding + + private func encode(_ bodyPart: BodyPart) throws -> Data { + var encoded = Data() + + let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() + encoded.append(initialData) + + let headerData = encodeHeaders(for: bodyPart) + encoded.append(headerData) + + let bodyStreamData = try encodeBodyStream(for: bodyPart) + encoded.append(bodyStreamData) + + if bodyPart.hasFinalBoundary { + encoded.append(finalBoundaryData()) + } + + return encoded + } + + private func encodeHeaders(for bodyPart: BodyPart) -> Data { + var headerText = "" + + for (key, value) in bodyPart.headers { + headerText += "\(key): \(value)\(EncodingCharacters.crlf)" + } + headerText += EncodingCharacters.crlf + + return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)! + } + + private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data { + let inputStream = bodyPart.bodyStream + inputStream.open() + defer { inputStream.close() } + + var encoded = Data() + + while inputStream.hasBytesAvailable { + var buffer = [UInt8](repeating: 0, count: streamBufferSize) + let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) + + if let error = inputStream.streamError { + throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) + } + + if bytesRead > 0 { + encoded.append(buffer, count: bytesRead) + } else { + break + } + } + + return encoded + } + + // MARK: - Private - Writing Body Part to Output Stream + + private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws { + try writeInitialBoundaryData(for: bodyPart, to: outputStream) + try writeHeaderData(for: bodyPart, to: outputStream) + try writeBodyStream(for: bodyPart, to: outputStream) + try writeFinalBoundaryData(for: bodyPart, to: outputStream) + } + + private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { + let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() + return try write(initialData, to: outputStream) + } + + private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { + let headerData = encodeHeaders(for: bodyPart) + return try write(headerData, to: outputStream) + } + + private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws { + let inputStream = bodyPart.bodyStream + + inputStream.open() + defer { inputStream.close() } + + while inputStream.hasBytesAvailable { + var buffer = [UInt8](repeating: 0, count: streamBufferSize) + let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) + + if let streamError = inputStream.streamError { + throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError)) + } + + if bytesRead > 0 { + if buffer.count != bytesRead { + buffer = Array(buffer[0.. 0, outputStream.hasSpaceAvailable { + let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) + + if let error = outputStream.streamError { + throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error)) + } + + bytesToWrite -= bytesWritten + + if bytesToWrite > 0 { + buffer = Array(buffer[bytesWritten.. String { + if + let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), + let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() + { + return contentType as String + } + + return "application/octet-stream" + } + + // MARK: - Private - Content Headers + + private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] { + var disposition = "form-data; name=\"\(name)\"" + if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" } + + var headers = ["Content-Disposition": disposition] + if let mimeType = mimeType { headers["Content-Type"] = mimeType } + + return headers + } + + // MARK: - Private - Boundary Encoding + + private func initialBoundaryData() -> Data { + return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary) + } + + private func encapsulatedBoundaryData() -> Data { + return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary) + } + + private func finalBoundaryData() -> Data { + return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary) + } + + // MARK: - Private - Errors + + private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) { + guard bodyPartError == nil else { return } + bodyPartError = AFError.multipartEncodingFailed(reason: reason) + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/NetworkReachabilityManager.swift b/Santander-Test/Pods/Alamofire/Source/NetworkReachabilityManager.swift new file mode 100644 index 00000000..5470eec4 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/NetworkReachabilityManager.swift @@ -0,0 +1,235 @@ +// +// NetworkReachabilityManager.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#if !os(watchOS) + +import Foundation +import SystemConfiguration + +/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and +/// WiFi network interfaces. +/// +/// Reachability can be used to determine background information about why a network operation failed, or to retry +/// network requests when a connection is established. It should not be used to prevent a user from initiating a network +/// request, as it's possible that an initial request may be required to establish reachability. +open class NetworkReachabilityManager { + /// Defines the various states of network reachability. + /// + /// - unknown: It is unknown whether the network is reachable. + /// - notReachable: The network is not reachable. + /// - reachable: The network is reachable. + public enum NetworkReachabilityStatus { + case unknown + case notReachable + case reachable(ConnectionType) + } + + /// Defines the various connection types detected by reachability flags. + /// + /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi. + /// - wwan: The connection type is a WWAN connection. + public enum ConnectionType { + case ethernetOrWiFi + case wwan + } + + /// A closure executed when the network reachability status changes. The closure takes a single argument: the + /// network reachability status. + public typealias Listener = (NetworkReachabilityStatus) -> Void + + // MARK: - Properties + + /// Whether the network is currently reachable. + open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } + + /// Whether the network is currently reachable over the WWAN interface. + open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } + + /// Whether the network is currently reachable over Ethernet or WiFi interface. + open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } + + /// The current network reachability status. + open var networkReachabilityStatus: NetworkReachabilityStatus { + guard let flags = self.flags else { return .unknown } + return networkReachabilityStatusForFlags(flags) + } + + /// The dispatch queue to execute the `listener` closure on. + open var listenerQueue: DispatchQueue = DispatchQueue.main + + /// A closure executed when the network reachability status changes. + open var listener: Listener? + + open var flags: SCNetworkReachabilityFlags? { + var flags = SCNetworkReachabilityFlags() + + if SCNetworkReachabilityGetFlags(reachability, &flags) { + return flags + } + + return nil + } + + private let reachability: SCNetworkReachability + open var previousFlags: SCNetworkReachabilityFlags + + // MARK: - Initialization + + /// Creates a `NetworkReachabilityManager` instance with the specified host. + /// + /// - parameter host: The host used to evaluate network reachability. + /// + /// - returns: The new `NetworkReachabilityManager` instance. + public convenience init?(host: String) { + guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } + self.init(reachability: reachability) + } + + /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0. + /// + /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing + /// status of the device, both IPv4 and IPv6. + /// + /// - returns: The new `NetworkReachabilityManager` instance. + public convenience init?() { + var address = sockaddr_in() + address.sin_len = UInt8(MemoryLayout.size) + address.sin_family = sa_family_t(AF_INET) + + guard let reachability = withUnsafePointer(to: &address, { pointer in + return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout.size) { + return SCNetworkReachabilityCreateWithAddress(nil, $0) + } + }) else { return nil } + + self.init(reachability: reachability) + } + + private init(reachability: SCNetworkReachability) { + self.reachability = reachability + + // Set the previous flags to an unreserved value to represent unknown status + self.previousFlags = SCNetworkReachabilityFlags(rawValue: 1 << 30) + } + + deinit { + stopListening() + } + + // MARK: - Listening + + /// Starts listening for changes in network reachability status. + /// + /// - returns: `true` if listening was started successfully, `false` otherwise. + @discardableResult + open func startListening() -> Bool { + var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) + context.info = Unmanaged.passUnretained(self).toOpaque() + + let callbackEnabled = SCNetworkReachabilitySetCallback( + reachability, + { (_, flags, info) in + let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() + reachability.notifyListener(flags) + }, + &context + ) + + let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) + + listenerQueue.async { + guard let flags = self.flags else { return } + self.notifyListener(flags) + } + + return callbackEnabled && queueEnabled + } + + /// Stops listening for changes in network reachability status. + open func stopListening() { + SCNetworkReachabilitySetCallback(reachability, nil, nil) + SCNetworkReachabilitySetDispatchQueue(reachability, nil) + } + + // MARK: - Internal - Listener Notification + + func notifyListener(_ flags: SCNetworkReachabilityFlags) { + guard previousFlags != flags else { return } + previousFlags = flags + + listener?(networkReachabilityStatusForFlags(flags)) + } + + // MARK: - Internal - Network Reachability Status + + func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { + guard isNetworkReachable(with: flags) else { return .notReachable } + + var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) + + #if os(iOS) + if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } + #endif + + return networkStatus + } + + func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool { + let isReachable = flags.contains(.reachable) + let needsConnection = flags.contains(.connectionRequired) + let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) + let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired) + + return isReachable && (!needsConnection || canConnectWithoutUserInteraction) + } +} + +// MARK: - + +extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} + +/// Returns whether the two network reachability status values are equal. +/// +/// - parameter lhs: The left-hand side value to compare. +/// - parameter rhs: The right-hand side value to compare. +/// +/// - returns: `true` if the two values are equal, `false` otherwise. +public func ==( + lhs: NetworkReachabilityManager.NetworkReachabilityStatus, + rhs: NetworkReachabilityManager.NetworkReachabilityStatus) + -> Bool +{ + switch (lhs, rhs) { + case (.unknown, .unknown): + return true + case (.notReachable, .notReachable): + return true + case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): + return lhsConnectionType == rhsConnectionType + default: + return false + } +} + +#endif diff --git a/Santander-Test/Pods/Alamofire/Source/Notifications.swift b/Santander-Test/Pods/Alamofire/Source/Notifications.swift new file mode 100644 index 00000000..e1ac31bf --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Notifications.swift @@ -0,0 +1,55 @@ +// +// Notifications.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension Notification.Name { + /// Used as a namespace for all `URLSessionTask` related notifications. + public struct Task { + /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. + public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") + + /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. + public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") + + /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. + public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") + + /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. + public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") + } +} + +// MARK: - + +extension Notification { + /// Used as a namespace for all `Notification` user info dictionary keys. + public struct Key { + /// User info dictionary key representing the `URLSessionTask` associated with the notification. + public static let Task = "org.alamofire.notification.key.task" + + /// User info dictionary key representing the responseData associated with the notification. + public static let ResponseData = "org.alamofire.notification.key.responseData" + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/ParameterEncoding.swift b/Santander-Test/Pods/Alamofire/Source/ParameterEncoding.swift new file mode 100644 index 00000000..6195809c --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/ParameterEncoding.swift @@ -0,0 +1,483 @@ +// +// ParameterEncoding.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// HTTP method definitions. +/// +/// See https://tools.ietf.org/html/rfc7231#section-4.3 +public enum HTTPMethod: String { + case options = "OPTIONS" + case get = "GET" + case head = "HEAD" + case post = "POST" + case put = "PUT" + case patch = "PATCH" + case delete = "DELETE" + case trace = "TRACE" + case connect = "CONNECT" +} + +// MARK: - + +/// A dictionary of parameters to apply to a `URLRequest`. +public typealias Parameters = [String: Any] + +/// A type used to define how a set of parameters are applied to a `URLRequest`. +public protocol ParameterEncoding { + /// Creates a URL request by encoding parameters and applying them onto an existing request. + /// + /// - parameter urlRequest: The request to have parameters applied. + /// - parameter parameters: The parameters to apply. + /// + /// - throws: An `AFError.parameterEncodingFailed` error if encoding fails. + /// + /// - returns: The encoded request. + func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest +} + +// MARK: - + +/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP +/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as +/// the HTTP body depends on the destination of the encoding. +/// +/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to +/// `application/x-www-form-urlencoded; charset=utf-8`. +/// +/// 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. + /// + /// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` + /// requests and sets as the HTTP body for requests with any other HTTP method. + /// - queryString: Sets or appends encoded query string result to existing query string. + /// - httpBody: Sets encoded query string result as the HTTP body of the URL request. + public enum Destination { + case methodDependent, queryString, httpBody + } + + /// Configures how `Array` parameters are encoded. + /// + /// - brackets: An empty set of square brackets is appended to the key for every value. + /// This is the default behavior. + /// - noBrackets: No brackets are appended. The key is encoded as is. + public enum ArrayEncoding { + case brackets, noBrackets + + func encode(key: String) -> String { + switch self { + case .brackets: + return "\(key)[]" + case .noBrackets: + return key + } + } + } + + /// Configures how `Bool` parameters are encoded. + /// + /// - numeric: Encode `true` as `1` and `false` as `0`. This is the default behavior. + /// - literal: Encode `true` and `false` as string literals. + public enum BoolEncoding { + case numeric, 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. + public static var `default`: URLEncoding { return URLEncoding() } + + /// Returns a `URLEncoding` instance with a `.methodDependent` destination. + public static var methodDependent: URLEncoding { return URLEncoding() } + + /// Returns a `URLEncoding` instance with a `.queryString` destination. + public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) } + + /// Returns a `URLEncoding` instance with an `.httpBody` destination. + public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) } + + /// The destination defining where the encoded query string is to be applied to the URL request. + public let destination: Destination + + /// 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 a `URLEncoding` instance using the specified destination. + /// + /// - parameter destination: The destination defining where the encoded query string is to be applied. + /// - parameter arrayEncoding: The encoding to use for `Array` parameters. + /// - parameter boolEncoding: The encoding to use for `Bool` parameters. + /// + /// - returns: The new `URLEncoding` instance. + public init(destination: Destination = .methodDependent, arrayEncoding: ArrayEncoding = .brackets, boolEncoding: BoolEncoding = .numeric) { + self.destination = destination + self.arrayEncoding = arrayEncoding + self.boolEncoding = boolEncoding + } + + // MARK: Encoding + + /// Creates a URL request by encoding parameters and applying them onto an existing request. + /// + /// - parameter urlRequest: The request to have parameters applied. + /// - parameter parameters: The parameters to apply. + /// + /// - throws: An `Error` if the encoding process encounters an error. + /// + /// - returns: The encoded request. + public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let parameters = parameters else { return urlRequest } + + if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) { + guard let url = urlRequest.url else { + throw AFError.parameterEncodingFailed(reason: .missingURL) + } + + if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { + let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) + urlComponents.percentEncodedQuery = percentEncodedQuery + urlRequest.url = urlComponents.url + } + } else { + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") + } + + urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false) + } + + return urlRequest + } + + /// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. + /// + /// - parameter key: The key of the query component. + /// - parameter value: The value of the query component. + /// + /// - returns: The percent-escaped, URL encoded query string components. + public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { + var components: [(String, String)] = [] + + if let dictionary = value as? [String: Any] { + for (nestedKey, value) in dictionary { + components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) + } + } else if let array = value as? [Any] { + for value in array { + components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value) + } + } else if let value = value as? NSNumber { + if value.isBool { + components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue)))) + } else { + components.append((escape(key), escape("\(value)"))) + } + } else if let bool = value as? Bool { + components.append((escape(key), escape(boolEncoding.encode(value: bool)))) + } else { + components.append((escape(key), escape("\(value)"))) + } + + return components + } + + /// Returns a percent-escaped string following RFC 3986 for a query string key or value. + /// + /// RFC 3986 states that the following characters are "reserved" characters. + /// + /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" + /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" + /// + /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow + /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" + /// should be percent-escaped in the query string. + /// + /// - parameter string: The string to be percent-escaped. + /// + /// - returns: The percent-escaped string. + public func escape(_ string: String) -> String { + let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 + let subDelimitersToEncode = "!$&'()*+,;=" + + var allowedCharacterSet = CharacterSet.urlQueryAllowed + allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") + + var escaped = "" + + //========================================================================================================== + // + // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few + // hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no + // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more + // info, please refer to: + // + // - https://github.com/Alamofire/Alamofire/issues/206 + // + //========================================================================================================== + + if #available(iOS 8.3, *) { + escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string + } else { + let batchSize = 50 + var index = string.startIndex + + while index != string.endIndex { + let startIndex = index + let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex + let range = startIndex.. String { + var components: [(String, String)] = [] + + for key in parameters.keys.sorted(by: <) { + let value = parameters[key]! + components += queryComponents(fromKey: key, value: value) + } + return components.map { "\($0)=\($1)" }.joined(separator: "&") + } + + private func encodesParametersInURL(with method: HTTPMethod) -> Bool { + switch destination { + case .queryString: + return true + case .httpBody: + return false + default: + break + } + + switch method { + case .get, .head, .delete: + return true + default: + return false + } + } +} + +// MARK: - + +/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the +/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. +public struct JSONEncoding: ParameterEncoding { + + // MARK: Properties + + /// Returns a `JSONEncoding` instance with default writing options. + public static var `default`: JSONEncoding { return JSONEncoding() } + + /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. + public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) } + + /// The options for writing the parameters as JSON data. + public let options: JSONSerialization.WritingOptions + + // MARK: Initialization + + /// Creates a `JSONEncoding` instance using the specified options. + /// + /// - parameter options: The options for writing the parameters as JSON data. + /// + /// - returns: The new `JSONEncoding` instance. + public init(options: JSONSerialization.WritingOptions = []) { + self.options = options + } + + // MARK: Encoding + + /// Creates a URL request by encoding parameters and applying them onto an existing request. + /// + /// - parameter urlRequest: The request to have parameters applied. + /// - parameter parameters: The parameters to apply. + /// + /// - throws: An `Error` if the encoding process encounters an error. + /// + /// - returns: The encoded request. + public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let parameters = parameters else { return urlRequest } + + do { + let data = try JSONSerialization.data(withJSONObject: parameters, options: options) + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return urlRequest + } + + /// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body. + /// + /// - parameter urlRequest: The request to apply the JSON object to. + /// - parameter jsonObject: The JSON object to apply to the request. + /// + /// - throws: An `Error` if the encoding process encounters an error. + /// + /// - returns: The encoded request. + public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let jsonObject = jsonObject else { return urlRequest } + + do { + let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options) + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) + } + + return urlRequest + } +} + +// MARK: - + +/// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the +/// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header +/// field of an encoded request is set to `application/x-plist`. +public struct PropertyListEncoding: ParameterEncoding { + + // MARK: Properties + + /// Returns a default `PropertyListEncoding` instance. + public static var `default`: PropertyListEncoding { return PropertyListEncoding() } + + /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options. + public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) } + + /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options. + public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) } + + /// The property list serialization format. + public let format: PropertyListSerialization.PropertyListFormat + + /// The options for writing the parameters as plist data. + public let options: PropertyListSerialization.WriteOptions + + // MARK: Initialization + + /// Creates a `PropertyListEncoding` instance using the specified format and options. + /// + /// - parameter format: The property list serialization format. + /// - parameter options: The options for writing the parameters as plist data. + /// + /// - returns: The new `PropertyListEncoding` instance. + public init( + format: PropertyListSerialization.PropertyListFormat = .xml, + options: PropertyListSerialization.WriteOptions = 0) + { + self.format = format + self.options = options + } + + // MARK: Encoding + + /// Creates a URL request by encoding parameters and applying them onto an existing request. + /// + /// - parameter urlRequest: The request to have parameters applied. + /// - parameter parameters: The parameters to apply. + /// + /// - throws: An `Error` if the encoding process encounters an error. + /// + /// - returns: The encoded request. + public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { + var urlRequest = try urlRequest.asURLRequest() + + guard let parameters = parameters else { return urlRequest } + + do { + let data = try PropertyListSerialization.data( + fromPropertyList: parameters, + format: format, + options: options + ) + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") + } + + urlRequest.httpBody = data + } catch { + throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error)) + } + + return urlRequest + } +} + +// MARK: - + +extension NSNumber { + fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) } +} diff --git a/Santander-Test/Pods/Alamofire/Source/Request.swift b/Santander-Test/Pods/Alamofire/Source/Request.swift new file mode 100644 index 00000000..a2efaa0e --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Request.swift @@ -0,0 +1,649 @@ +// +// Request.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary. +public protocol RequestAdapter { + /// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result. + /// + /// - parameter urlRequest: The URL request to adapt. + /// + /// - throws: An `Error` if the adaptation encounters an error. + /// + /// - returns: The adapted `URLRequest`. + func adapt(_ urlRequest: URLRequest) throws -> URLRequest +} + +// MARK: - + +/// A closure executed when the `RequestRetrier` determines whether a `Request` should be retried or not. +public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void + +/// A type that determines whether a request should be retried after being executed by the specified session manager +/// and encountering an error. +public protocol RequestRetrier { + /// Determines whether the `Request` should be retried by calling the `completion` closure. + /// + /// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs + /// to be retried. The one requirement is that the completion closure is called to ensure the request is properly + /// cleaned up after. + /// + /// - parameter manager: The session manager the request was executed on. + /// - parameter request: The request that failed due to the encountered error. + /// - parameter error: The error encountered when executing the request. + /// - parameter completion: The completion closure to be executed when retry decision has been determined. + func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) +} + +// MARK: - + +protocol TaskConvertible { + func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask +} + +/// A dictionary of headers to apply to a `URLRequest`. +public typealias HTTPHeaders = [String: String] + +// MARK: - + +/// Responsible for sending a request and receiving the response and associated data from the server, as well as +/// managing its underlying `URLSessionTask`. +open class Request { + + // MARK: Helper Types + + /// A closure executed when monitoring upload or download progress of a request. + public typealias ProgressHandler = (Progress) -> Void + + enum RequestTask { + case data(TaskConvertible?, URLSessionTask?) + case download(TaskConvertible?, URLSessionTask?) + case upload(TaskConvertible?, URLSessionTask?) + case stream(TaskConvertible?, URLSessionTask?) + } + + // MARK: Properties + + /// The delegate for the underlying task. + open internal(set) var delegate: TaskDelegate { + get { + taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() } + return taskDelegate + } + set { + taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() } + taskDelegate = newValue + } + } + + /// The underlying task. + open var task: URLSessionTask? { return delegate.task } + + /// The session belonging to the underlying task. + public let session: URLSession + + /// The request sent or to be sent to the server. + open var request: URLRequest? { return task?.originalRequest } + + /// The response received from the server, if any. + open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse } + + /// The number of times the request has been retried. + open internal(set) var retryCount: UInt = 0 + + let originalTask: TaskConvertible? + + var startTime: CFAbsoluteTime? + var endTime: CFAbsoluteTime? + + var validations: [() -> Void] = [] + + private var taskDelegate: TaskDelegate + private var taskDelegateLock = NSLock() + + // MARK: Lifecycle + + init(session: URLSession, requestTask: RequestTask, error: Error? = nil) { + self.session = session + + switch requestTask { + case .data(let originalTask, let task): + taskDelegate = DataTaskDelegate(task: task) + self.originalTask = originalTask + case .download(let originalTask, let task): + taskDelegate = DownloadTaskDelegate(task: task) + self.originalTask = originalTask + case .upload(let originalTask, let task): + taskDelegate = UploadTaskDelegate(task: task) + self.originalTask = originalTask + case .stream(let originalTask, let task): + taskDelegate = TaskDelegate(task: task) + self.originalTask = originalTask + } + + delegate.error = error + delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() } + } + + // MARK: Authentication + + /// Associates an HTTP Basic credential with the request. + /// + /// - parameter user: The user. + /// - parameter password: The password. + /// - parameter persistence: The URL credential persistence. `.ForSession` by default. + /// + /// - returns: The request. + @discardableResult + open func authenticate( + user: String, + password: String, + persistence: URLCredential.Persistence = .forSession) + -> Self + { + let credential = URLCredential(user: user, password: password, persistence: persistence) + return authenticate(usingCredential: credential) + } + + /// Associates a specified credential with the request. + /// + /// - parameter credential: The credential. + /// + /// - returns: The request. + @discardableResult + open func authenticate(usingCredential credential: URLCredential) -> Self { + delegate.credential = credential + return self + } + + /// Returns a base64 encoded basic authentication credential as an authorization header tuple. + /// + /// - parameter user: The user. + /// - parameter password: The password. + /// + /// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise. + open class func authorizationHeader(user: String, password: String) -> (key: String, value: String)? { + guard let data = "\(user):\(password)".data(using: .utf8) else { return nil } + + let credential = data.base64EncodedString(options: []) + + return (key: "Authorization", value: "Basic \(credential)") + } + + // MARK: State + + /// Resumes the request. + open func resume() { + guard let task = task else { delegate.queue.isSuspended = false ; return } + + if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() } + + task.resume() + + NotificationCenter.default.post( + name: Notification.Name.Task.DidResume, + object: self, + userInfo: [Notification.Key.Task: task] + ) + } + + /// Suspends the request. + open func suspend() { + guard let task = task else { return } + + task.suspend() + + NotificationCenter.default.post( + name: Notification.Name.Task.DidSuspend, + object: self, + userInfo: [Notification.Key.Task: task] + ) + } + + /// Cancels the request. + open func cancel() { + guard let task = task else { return } + + task.cancel() + + NotificationCenter.default.post( + name: Notification.Name.Task.DidCancel, + object: self, + userInfo: [Notification.Key.Task: task] + ) + } +} + +// MARK: - CustomStringConvertible + +extension Request: CustomStringConvertible { + /// The textual representation used when written to an output stream, which includes the HTTP method and URL, as + /// well as the response status code if a response has been received. + open var description: String { + var components: [String] = [] + + if let HTTPMethod = request?.httpMethod { + components.append(HTTPMethod) + } + + if let urlString = request?.url?.absoluteString { + components.append(urlString) + } + + if let response = response { + components.append("(\(response.statusCode))") + } + + return components.joined(separator: " ") + } +} + +// MARK: - CustomDebugStringConvertible + +extension Request: CustomDebugStringConvertible { + /// The textual representation used when written to an output stream, in the form of a cURL command. + open var debugDescription: String { + return cURLRepresentation() + } + + func cURLRepresentation() -> String { + var components = ["$ curl -v"] + + guard let request = self.request, + let url = request.url, + let host = url.host + else { + return "$ curl command could not be created" + } + + if let httpMethod = request.httpMethod, httpMethod != "GET" { + components.append("-X \(httpMethod)") + } + + if let credentialStorage = self.session.configuration.urlCredentialStorage { + let protectionSpace = URLProtectionSpace( + host: host, + port: url.port ?? 0, + protocol: url.scheme, + realm: host, + authenticationMethod: NSURLAuthenticationMethodHTTPBasic + ) + + if let credentials = credentialStorage.credentials(for: protectionSpace)?.values { + for credential in credentials { + guard let user = credential.user, let password = credential.password else { continue } + components.append("-u \(user):\(password)") + } + } else { + if let credential = delegate.credential, let user = credential.user, let password = credential.password { + components.append("-u \(user):\(password)") + } + } + } + + if session.configuration.httpShouldSetCookies { + if + let cookieStorage = session.configuration.httpCookieStorage, + let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty + { + let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" } + + #if swift(>=3.2) + components.append("-b \"\(string[.. URLSessionTask { + do { + let urlRequest = try self.urlRequest.adapt(using: adapter) + return queue.sync { session.dataTask(with: urlRequest) } + } catch { + throw AdaptError(error: error) + } + } + } + + // MARK: Properties + + /// The request sent or to be sent to the server. + open override var request: URLRequest? { + if let request = super.request { return request } + if let requestable = originalTask as? Requestable { return requestable.urlRequest } + + return nil + } + + /// The progress of fetching the response data from the server for the request. + open var progress: Progress { return dataDelegate.progress } + + var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate } + + // MARK: Stream + + /// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server. + /// + /// This closure returns the bytes most recently received from the server, not including data from previous calls. + /// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is + /// also important to note that the server data in any `Response` object will be `nil`. + /// + /// - parameter closure: The code to be executed periodically during the lifecycle of the request. + /// + /// - returns: The request. + @discardableResult + open func stream(closure: ((Data) -> Void)? = nil) -> Self { + dataDelegate.dataStream = closure + return self + } + + // MARK: Progress + + /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server. + /// + /// - parameter queue: The dispatch queue to execute the closure on. + /// - parameter closure: The code to be executed periodically as data is read from the server. + /// + /// - returns: The request. + @discardableResult + open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self { + dataDelegate.progressHandler = (closure, queue) + return self + } +} + +// MARK: - + +/// Specific type of `Request` that manages an underlying `URLSessionDownloadTask`. +open class DownloadRequest: Request { + + // MARK: Helper Types + + /// A collection of options to be executed prior to moving a downloaded file from the temporary URL to the + /// destination URL. + public struct DownloadOptions: OptionSet { + /// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol. + public let rawValue: UInt + + /// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified. + public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0) + + /// A `DownloadOptions` flag that removes a previous file from the destination URL if specified. + public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1) + + /// Creates a `DownloadFileDestinationOptions` instance with the specified raw value. + /// + /// - parameter rawValue: The raw bitmask value for the option. + /// + /// - returns: A new log level instance. + public init(rawValue: UInt) { + self.rawValue = rawValue + } + } + + /// A closure executed once a download request has successfully completed in order to determine where to move the + /// temporary file written to during the download process. The closure takes two arguments: the temporary file URL + /// and the URL response, and returns a two arguments: the file URL where the temporary file should be moved and + /// the options defining how the file should be moved. + public typealias DownloadFileDestination = ( + _ temporaryURL: URL, + _ response: HTTPURLResponse) + -> (destinationURL: URL, options: DownloadOptions) + + enum Downloadable: TaskConvertible { + case request(URLRequest) + case resumeData(Data) + + func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask { + do { + let task: URLSessionTask + + switch self { + case let .request(urlRequest): + let urlRequest = try urlRequest.adapt(using: adapter) + task = queue.sync { session.downloadTask(with: urlRequest) } + case let .resumeData(resumeData): + task = queue.sync { session.downloadTask(withResumeData: resumeData) } + } + + return task + } catch { + throw AdaptError(error: error) + } + } + } + + // MARK: Properties + + /// The request sent or to be sent to the server. + open override var request: URLRequest? { + if let request = super.request { return request } + + if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable { + return urlRequest + } + + return nil + } + + /// The resume data of the underlying download task if available after a failure. + open var resumeData: Data? { return downloadDelegate.resumeData } + + /// The progress of downloading the response data from the server for the request. + open var progress: Progress { return downloadDelegate.progress } + + var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate } + + // MARK: State + + /// Cancels the request. + open override func cancel() { + downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 } + + NotificationCenter.default.post( + name: Notification.Name.Task.DidCancel, + object: self, + userInfo: [Notification.Key.Task: task as Any] + ) + } + + // MARK: Progress + + /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server. + /// + /// - parameter queue: The dispatch queue to execute the closure on. + /// - parameter closure: The code to be executed periodically as data is read from the server. + /// + /// - returns: The request. + @discardableResult + open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self { + downloadDelegate.progressHandler = (closure, queue) + return self + } + + // MARK: Destination + + /// Creates a download file destination closure which uses the default file manager to move the temporary file to a + /// file URL in the first available directory with the specified search path directory and search path domain mask. + /// + /// - parameter directory: The search path directory. `.DocumentDirectory` by default. + /// - parameter domain: The search path domain mask. `.UserDomainMask` by default. + /// + /// - returns: A download file destination closure. + open class func suggestedDownloadDestination( + for directory: FileManager.SearchPathDirectory = .documentDirectory, + in domain: FileManager.SearchPathDomainMask = .userDomainMask) + -> DownloadFileDestination + { + return { temporaryURL, response in + let directoryURLs = FileManager.default.urls(for: directory, in: domain) + + if !directoryURLs.isEmpty { + return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), []) + } + + return (temporaryURL, []) + } + } +} + +// MARK: - + +/// Specific type of `Request` that manages an underlying `URLSessionUploadTask`. +open class UploadRequest: DataRequest { + + // MARK: Helper Types + + enum Uploadable: TaskConvertible { + case data(Data, URLRequest) + case file(URL, URLRequest) + case stream(InputStream, URLRequest) + + func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask { + do { + let task: URLSessionTask + + switch self { + case let .data(data, urlRequest): + let urlRequest = try urlRequest.adapt(using: adapter) + task = queue.sync { session.uploadTask(with: urlRequest, from: data) } + case let .file(url, urlRequest): + let urlRequest = try urlRequest.adapt(using: adapter) + task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) } + case let .stream(_, urlRequest): + let urlRequest = try urlRequest.adapt(using: adapter) + task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) } + } + + return task + } catch { + throw AdaptError(error: error) + } + } + } + + // MARK: Properties + + /// The request sent or to be sent to the server. + open override var request: URLRequest? { + if let request = super.request { return request } + + guard let uploadable = originalTask as? Uploadable else { return nil } + + switch uploadable { + case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest): + return urlRequest + } + } + + /// The progress of uploading the payload to the server for the upload request. + open var uploadProgress: Progress { return uploadDelegate.uploadProgress } + + var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate } + + // MARK: Upload Progress + + /// Sets a closure to be called periodically during the lifecycle of the `UploadRequest` as data is sent to + /// the server. + /// + /// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress + /// of data being read from the server. + /// + /// - parameter queue: The dispatch queue to execute the closure on. + /// - parameter closure: The code to be executed periodically as data is sent to the server. + /// + /// - returns: The request. + @discardableResult + open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self { + uploadDelegate.uploadProgressHandler = (closure, queue) + return self + } +} + +// MARK: - + +#if !os(watchOS) + +/// Specific type of `Request` that manages an underlying `URLSessionStreamTask`. +@available(iOS 9.0, macOS 10.11, tvOS 9.0, *) +open class StreamRequest: Request { + enum Streamable: TaskConvertible { + case stream(hostName: String, port: Int) + case netService(NetService) + + func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask { + let task: URLSessionTask + + switch self { + case let .stream(hostName, port): + task = queue.sync { session.streamTask(withHostName: hostName, port: port) } + case let .netService(netService): + task = queue.sync { session.streamTask(with: netService) } + } + + return task + } + } +} + +#endif diff --git a/Santander-Test/Pods/Alamofire/Source/Response.swift b/Santander-Test/Pods/Alamofire/Source/Response.swift new file mode 100644 index 00000000..747a471a --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Response.swift @@ -0,0 +1,567 @@ +// +// Response.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Used to store all data associated with an non-serialized response of a data or upload request. +public struct DefaultDataResponse { + /// The URL request sent to the server. + public let request: URLRequest? + + /// The server's response to the URL request. + public let response: HTTPURLResponse? + + /// The data returned by the server. + public let data: Data? + + /// The error encountered while executing or validating the request. + public let error: Error? + + /// The timeline of the complete lifecycle of the request. + public let timeline: Timeline + + var _metrics: AnyObject? + + /// Creates a `DefaultDataResponse` instance from the specified parameters. + /// + /// - Parameters: + /// - request: The URL request sent to the server. + /// - response: The server's response to the URL request. + /// - data: The data returned by the server. + /// - error: The error encountered while executing or validating the request. + /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. + /// - metrics: The task metrics containing the request / response statistics. `nil` by default. + public init( + request: URLRequest?, + response: HTTPURLResponse?, + data: Data?, + error: Error?, + timeline: Timeline = Timeline(), + metrics: AnyObject? = nil) + { + self.request = request + self.response = response + self.data = data + self.error = error + self.timeline = timeline + } +} + +// MARK: - + +/// Used to store all data associated with a serialized response of a data or upload request. +public struct DataResponse { + /// The URL request sent to the server. + public let request: URLRequest? + + /// The server's response to the URL request. + public let response: HTTPURLResponse? + + /// The data returned by the server. + public let data: Data? + + /// The result of response serialization. + public let result: Result + + /// The timeline of the complete lifecycle of the request. + public let timeline: Timeline + + /// Returns the associated value of the result if it is a success, `nil` otherwise. + public var value: Value? { return result.value } + + /// Returns the associated error value if the result if it is a failure, `nil` otherwise. + public var error: Error? { return result.error } + + var _metrics: AnyObject? + + /// Creates a `DataResponse` instance with the specified parameters derived from response serialization. + /// + /// - parameter request: The URL request sent to the server. + /// - parameter response: The server's response to the URL request. + /// - parameter data: The data returned by the server. + /// - parameter result: The result of response serialization. + /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. + /// + /// - returns: The new `DataResponse` instance. + public init( + request: URLRequest?, + response: HTTPURLResponse?, + data: Data?, + result: Result, + timeline: Timeline = Timeline()) + { + self.request = request + self.response = response + self.data = data + self.result = result + self.timeline = timeline + } +} + +// MARK: - + +extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { + /// The textual representation used when written to an output stream, which includes whether the result was a + /// success or failure. + public var description: String { + return result.debugDescription + } + + /// The debug textual representation used when written to an output stream, which includes the URL request, the URL + /// response, the server data, the response serialization result and the timeline. + public var debugDescription: String { + var output: [String] = [] + + output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") + output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") + output.append("[Data]: \(data?.count ?? 0) bytes") + output.append("[Result]: \(result.debugDescription)") + output.append("[Timeline]: \(timeline.debugDescription)") + + return output.joined(separator: "\n") + } +} + +// MARK: - + +extension DataResponse { + /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `map` method with a closure that does not throw. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleInt = possibleData.map { $0.count } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's + /// result is a failure, returns a response wrapping the same failure. + public func map(_ transform: (Value) -> T) -> DataResponse { + var response = DataResponse( + request: request, + response: self.response, + data: data, + result: result.map(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result + /// value as a parameter. + /// + /// Use the `flatMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.flatMap { + /// try JSONSerialization.jsonObject(with: $0) + /// } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's + /// result is a failure, returns the same failure. + public func flatMap(_ transform: (Value) throws -> T) -> DataResponse { + var response = DataResponse( + request: request, + response: self.response, + data: data, + result: result.flatMap(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// 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: (Error) -> E) -> DataResponse { + var response = DataResponse( + request: request, + response: self.response, + data: data, + result: result.mapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `flatMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.flatMapError { + /// 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 flatMapError(_ transform: (Error) throws -> E) -> DataResponse { + var response = DataResponse( + request: request, + response: self.response, + data: data, + result: result.flatMapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } +} + +// MARK: - + +/// Used to store all data associated with an non-serialized response of a download request. +public struct DefaultDownloadResponse { + /// The URL request sent to the server. + public let request: URLRequest? + + /// The server's response to the URL request. + public let response: HTTPURLResponse? + + /// The temporary destination URL of the data returned from the server. + public let temporaryURL: URL? + + /// The final destination URL of the data returned from the server if it was moved. + public let destinationURL: URL? + + /// The resume data generated if the request was cancelled. + public let resumeData: Data? + + /// The error encountered while executing or validating the request. + public let error: Error? + + /// The timeline of the complete lifecycle of the request. + public let timeline: Timeline + + var _metrics: AnyObject? + + /// Creates a `DefaultDownloadResponse` instance from the specified parameters. + /// + /// - Parameters: + /// - request: The URL request sent to the server. + /// - response: The server's response to the URL request. + /// - temporaryURL: The temporary destination URL of the data returned from the server. + /// - destinationURL: The final destination URL of the data returned from the server if it was moved. + /// - resumeData: The resume data generated if the request was cancelled. + /// - error: The error encountered while executing or validating the request. + /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. + /// - metrics: The task metrics containing the request / response statistics. `nil` by default. + public init( + request: URLRequest?, + response: HTTPURLResponse?, + temporaryURL: URL?, + destinationURL: URL?, + resumeData: Data?, + error: Error?, + timeline: Timeline = Timeline(), + metrics: AnyObject? = nil) + { + self.request = request + self.response = response + self.temporaryURL = temporaryURL + self.destinationURL = destinationURL + self.resumeData = resumeData + self.error = error + self.timeline = timeline + } +} + +// MARK: - + +/// Used to store all data associated with a serialized response of a download request. +public struct DownloadResponse { + /// The URL request sent to the server. + public let request: URLRequest? + + /// The server's response to the URL request. + public let response: HTTPURLResponse? + + /// The temporary destination URL of the data returned from the server. + public let temporaryURL: URL? + + /// The final destination URL of the data returned from the server if it was moved. + public let destinationURL: URL? + + /// The resume data generated if the request was cancelled. + public let resumeData: Data? + + /// The result of response serialization. + public let result: Result + + /// The timeline of the complete lifecycle of the request. + public let timeline: Timeline + + /// Returns the associated value of the result if it is a success, `nil` otherwise. + public var value: Value? { return result.value } + + /// Returns the associated error value if the result if it is a failure, `nil` otherwise. + public var error: Error? { return result.error } + + var _metrics: AnyObject? + + /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. + /// + /// - parameter request: The URL request sent to the server. + /// - parameter response: The server's response to the URL request. + /// - parameter temporaryURL: The temporary destination URL of the data returned from the server. + /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved. + /// - parameter resumeData: The resume data generated if the request was cancelled. + /// - parameter result: The result of response serialization. + /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. + /// + /// - returns: The new `DownloadResponse` instance. + public init( + request: URLRequest?, + response: HTTPURLResponse?, + temporaryURL: URL?, + destinationURL: URL?, + resumeData: Data?, + result: Result, + timeline: Timeline = Timeline()) + { + self.request = request + self.response = response + self.temporaryURL = temporaryURL + self.destinationURL = destinationURL + self.resumeData = resumeData + self.result = result + self.timeline = timeline + } +} + +// MARK: - + +extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible { + /// The textual representation used when written to an output stream, which includes whether the result was a + /// success or failure. + public var description: String { + return result.debugDescription + } + + /// The debug textual representation used when written to an output stream, which includes the URL request, the URL + /// response, the temporary and destination URLs, the resume data, the response serialization result and the + /// timeline. + public var debugDescription: String { + var output: [String] = [] + + output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") + output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") + output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")") + output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")") + output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes") + output.append("[Result]: \(result.debugDescription)") + output.append("[Timeline]: \(timeline.debugDescription)") + + return output.joined(separator: "\n") + } +} + +// MARK: - + +extension DownloadResponse { + /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `map` method with a closure that does not throw. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleInt = possibleData.map { $0.count } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's + /// result is a failure, returns a response wrapping the same failure. + public func map(_ transform: (Value) -> T) -> DownloadResponse { + var response = DownloadResponse( + request: request, + response: self.response, + temporaryURL: temporaryURL, + destinationURL: destinationURL, + resumeData: resumeData, + result: result.map(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped + /// result value as a parameter. + /// + /// Use the `flatMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.flatMap { + /// try JSONSerialization.jsonObject(with: $0) + /// } + /// + /// - parameter transform: A closure that takes the success value of the instance's result. + /// + /// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this + /// instance's result is a failure, returns the same failure. + public func flatMap(_ transform: (Value) throws -> T) -> DownloadResponse { + var response = DownloadResponse( + request: request, + response: self.response, + temporaryURL: temporaryURL, + destinationURL: destinationURL, + resumeData: resumeData, + result: result.flatMap(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// 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: (Error) -> E) -> DownloadResponse { + var response = DownloadResponse( + request: request, + response: self.response, + temporaryURL: temporaryURL, + destinationURL: destinationURL, + resumeData: resumeData, + result: result.mapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `flatMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.flatMapError { + /// 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 flatMapError(_ transform: (Error) throws -> E) -> DownloadResponse { + var response = DownloadResponse( + request: request, + response: self.response, + temporaryURL: temporaryURL, + destinationURL: destinationURL, + resumeData: resumeData, + result: result.flatMapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } +} + +// MARK: - + +protocol Response { + /// The task metrics containing the request / response statistics. + var _metrics: AnyObject? { get set } + mutating func add(_ metrics: AnyObject?) +} + +extension Response { + mutating func add(_ metrics: AnyObject?) { + #if !os(watchOS) + guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return } + guard let metrics = metrics as? URLSessionTaskMetrics else { return } + + _metrics = metrics + #endif + } +} + +// MARK: - + +@available(iOS 10.0, macOS 10.12, tvOS 10.0, *) +extension DefaultDataResponse: Response { +#if !os(watchOS) + /// The task metrics containing the request / response statistics. + public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } +#endif +} + +@available(iOS 10.0, macOS 10.12, tvOS 10.0, *) +extension DataResponse: Response { +#if !os(watchOS) + /// The task metrics containing the request / response statistics. + public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } +#endif +} + +@available(iOS 10.0, macOS 10.12, tvOS 10.0, *) +extension DefaultDownloadResponse: Response { +#if !os(watchOS) + /// The task metrics containing the request / response statistics. + public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } +#endif +} + +@available(iOS 10.0, macOS 10.12, tvOS 10.0, *) +extension DownloadResponse: Response { +#if !os(watchOS) + /// The task metrics containing the request / response statistics. + public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } +#endif +} diff --git a/Santander-Test/Pods/Alamofire/Source/ResponseSerialization.swift b/Santander-Test/Pods/Alamofire/Source/ResponseSerialization.swift new file mode 100644 index 00000000..9cc105a8 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/ResponseSerialization.swift @@ -0,0 +1,715 @@ +// +// ResponseSerialization.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// The type in which all data response serializers must conform to in order to serialize a response. +public protocol DataResponseSerializerProtocol { + /// The type of serialized object to be created by this `DataResponseSerializerType`. + associatedtype SerializedObject + + /// A closure used by response handlers that takes a request, response, data and error and returns a result. + var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result { get } +} + +// MARK: - + +/// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object. +public struct DataResponseSerializer: DataResponseSerializerProtocol { + /// The type of serialized object to be created by this `DataResponseSerializer`. + public typealias SerializedObject = Value + + /// A closure used by response handlers that takes a request, response, data and error and returns a result. + public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result + + /// Initializes the `ResponseSerializer` instance with the given serialize response closure. + /// + /// - parameter serializeResponse: The closure used to serialize the response. + /// + /// - returns: The new generic response serializer instance. + public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result) { + self.serializeResponse = serializeResponse + } +} + +// MARK: - + +/// The type in which all download response serializers must conform to in order to serialize a response. +public protocol DownloadResponseSerializerProtocol { + /// The type of serialized object to be created by this `DownloadResponseSerializerType`. + associatedtype SerializedObject + + /// A closure used by response handlers that takes a request, response, url and error and returns a result. + var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result { get } +} + +// MARK: - + +/// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object. +public struct DownloadResponseSerializer: DownloadResponseSerializerProtocol { + /// The type of serialized object to be created by this `DownloadResponseSerializer`. + public typealias SerializedObject = Value + + /// A closure used by response handlers that takes a request, response, url and error and returns a result. + public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result + + /// Initializes the `ResponseSerializer` instance with the given serialize response closure. + /// + /// - parameter serializeResponse: The closure used to serialize the response. + /// + /// - returns: The new generic response serializer instance. + public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result) { + self.serializeResponse = serializeResponse + } +} + +// MARK: - Timeline + +extension Request { + var timeline: Timeline { + let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent() + let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent() + let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime + + return Timeline( + requestStartTime: requestStartTime, + initialResponseTime: initialResponseTime, + requestCompletedTime: requestCompletedTime, + serializationCompletedTime: CFAbsoluteTimeGetCurrent() + ) + } +} + +// MARK: - Default + +extension DataRequest { + /// Adds a handler to be called once the request has finished. + /// + /// - parameter queue: The queue on which the completion handler is dispatched. + /// - parameter completionHandler: The code to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self { + delegate.queue.addOperation { + (queue ?? DispatchQueue.main).async { + var dataResponse = DefaultDataResponse( + request: self.request, + response: self.response, + data: self.delegate.data, + error: self.delegate.error, + timeline: self.timeline + ) + + dataResponse.add(self.delegate.metrics) + + completionHandler(dataResponse) + } + } + + return self + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter queue: The queue on which the completion handler is dispatched. + /// - parameter responseSerializer: The response serializer responsible for serializing the request, response, + /// and data. + /// - parameter completionHandler: The code to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func response( + queue: DispatchQueue? = nil, + responseSerializer: T, + completionHandler: @escaping (DataResponse) -> Void) + -> Self + { + delegate.queue.addOperation { + let result = responseSerializer.serializeResponse( + self.request, + self.response, + self.delegate.data, + self.delegate.error + ) + + var dataResponse = DataResponse( + request: self.request, + response: self.response, + data: self.delegate.data, + result: result, + timeline: self.timeline + ) + + dataResponse.add(self.delegate.metrics) + + (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) } + } + + return self + } +} + +extension DownloadRequest { + /// Adds a handler to be called once the request has finished. + /// + /// - parameter queue: The queue on which the completion handler is dispatched. + /// - parameter completionHandler: The code to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func response( + queue: DispatchQueue? = nil, + completionHandler: @escaping (DefaultDownloadResponse) -> Void) + -> Self + { + delegate.queue.addOperation { + (queue ?? DispatchQueue.main).async { + var downloadResponse = DefaultDownloadResponse( + request: self.request, + response: self.response, + temporaryURL: self.downloadDelegate.temporaryURL, + destinationURL: self.downloadDelegate.destinationURL, + resumeData: self.downloadDelegate.resumeData, + error: self.downloadDelegate.error, + timeline: self.timeline + ) + + downloadResponse.add(self.delegate.metrics) + + completionHandler(downloadResponse) + } + } + + return self + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter queue: The queue on which the completion handler is dispatched. + /// - parameter responseSerializer: The response serializer responsible for serializing the request, response, + /// and data contained in the destination url. + /// - parameter completionHandler: The code to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func response( + queue: DispatchQueue? = nil, + responseSerializer: T, + completionHandler: @escaping (DownloadResponse) -> Void) + -> Self + { + delegate.queue.addOperation { + let result = responseSerializer.serializeResponse( + self.request, + self.response, + self.downloadDelegate.fileURL, + self.downloadDelegate.error + ) + + var downloadResponse = DownloadResponse( + request: self.request, + response: self.response, + temporaryURL: self.downloadDelegate.temporaryURL, + destinationURL: self.downloadDelegate.destinationURL, + resumeData: self.downloadDelegate.resumeData, + result: result, + timeline: self.timeline + ) + + downloadResponse.add(self.delegate.metrics) + + (queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) } + } + + return self + } +} + +// MARK: - Data + +extension Request { + /// Returns a result data type that contains the response data as-is. + /// + /// - parameter response: The response from the server. + /// - parameter data: The data returned from the server. + /// - parameter error: The error already encountered if it exists. + /// + /// - returns: The result data type. + public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result { + guard error == nil else { return .failure(error!) } + + if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) } + + guard let validData = data else { + return .failure(AFError.responseSerializationFailed(reason: .inputDataNil)) + } + + return .success(validData) + } +} + +extension DataRequest { + /// Creates a response serializer that returns the associated data as-is. + /// + /// - returns: A data response serializer. + public static func dataResponseSerializer() -> DataResponseSerializer { + return DataResponseSerializer { _, response, data, error in + return Request.serializeResponseData(response: response, data: data, error: error) + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter completionHandler: The code to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responseData( + queue: DispatchQueue? = nil, + completionHandler: @escaping (DataResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DataRequest.dataResponseSerializer(), + completionHandler: completionHandler + ) + } +} + +extension DownloadRequest { + /// Creates a response serializer that returns the associated data as-is. + /// + /// - returns: A data response serializer. + public static func dataResponseSerializer() -> DownloadResponseSerializer { + return DownloadResponseSerializer { _, response, fileURL, error in + guard error == nil else { return .failure(error!) } + + guard let fileURL = fileURL else { + return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) + } + + do { + let data = try Data(contentsOf: fileURL) + return Request.serializeResponseData(response: response, data: data, error: error) + } catch { + return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) + } + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter completionHandler: The code to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responseData( + queue: DispatchQueue? = nil, + completionHandler: @escaping (DownloadResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DownloadRequest.dataResponseSerializer(), + completionHandler: completionHandler + ) + } +} + +// MARK: - String + +extension Request { + /// Returns a result string type initialized from the response data with the specified string encoding. + /// + /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server + /// response, falling back to the default HTTP default character set, ISO-8859-1. + /// - parameter response: The response from the server. + /// - parameter data: The data returned from the server. + /// - parameter error: The error already encountered if it exists. + /// + /// - returns: The result data type. + public static func serializeResponseString( + encoding: String.Encoding?, + response: HTTPURLResponse?, + data: Data?, + error: Error?) + -> Result + { + guard error == nil else { return .failure(error!) } + + if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") } + + guard let validData = data else { + return .failure(AFError.responseSerializationFailed(reason: .inputDataNil)) + } + + var convertedEncoding = encoding + + if let encodingName = response?.textEncodingName as CFString?, convertedEncoding == nil { + convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding( + CFStringConvertIANACharSetNameToEncoding(encodingName)) + ) + } + + let actualEncoding = convertedEncoding ?? .isoLatin1 + + if let string = String(data: validData, encoding: actualEncoding) { + return .success(string) + } else { + return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding))) + } + } +} + +extension DataRequest { + /// Creates a response serializer that returns a result string type initialized from the response data with + /// the specified string encoding. + /// + /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server + /// response, falling back to the default HTTP default character set, ISO-8859-1. + /// + /// - returns: A string response serializer. + public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer { + return DataResponseSerializer { _, response, data, error in + return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error) + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the + /// server response, falling back to the default HTTP default character set, + /// ISO-8859-1. + /// - parameter completionHandler: A closure to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responseString( + queue: DispatchQueue? = nil, + encoding: String.Encoding? = nil, + completionHandler: @escaping (DataResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding), + completionHandler: completionHandler + ) + } +} + +extension DownloadRequest { + /// Creates a response serializer that returns a result string type initialized from the response data with + /// the specified string encoding. + /// + /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server + /// response, falling back to the default HTTP default character set, ISO-8859-1. + /// + /// - returns: A string response serializer. + public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DownloadResponseSerializer { + return DownloadResponseSerializer { _, response, fileURL, error in + guard error == nil else { return .failure(error!) } + + guard let fileURL = fileURL else { + return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) + } + + do { + let data = try Data(contentsOf: fileURL) + return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error) + } catch { + return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) + } + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the + /// server response, falling back to the default HTTP default character set, + /// ISO-8859-1. + /// - parameter completionHandler: A closure to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responseString( + queue: DispatchQueue? = nil, + encoding: String.Encoding? = nil, + completionHandler: @escaping (DownloadResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DownloadRequest.stringResponseSerializer(encoding: encoding), + completionHandler: completionHandler + ) + } +} + +// MARK: - JSON + +extension Request { + /// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization` + /// with the specified reading options. + /// + /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. + /// - parameter response: The response from the server. + /// - parameter data: The data returned from the server. + /// - parameter error: The error already encountered if it exists. + /// + /// - returns: The result data type. + public static func serializeResponseJSON( + options: JSONSerialization.ReadingOptions, + response: HTTPURLResponse?, + data: Data?, + error: Error?) + -> Result + { + guard error == nil else { return .failure(error!) } + + if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) } + + guard let validData = data, validData.count > 0 else { + return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)) + } + + do { + let json = try JSONSerialization.jsonObject(with: validData, options: options) + return .success(json) + } catch { + return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))) + } + } +} + +extension DataRequest { + /// Creates a response serializer that returns a JSON object result type constructed from the response data using + /// `JSONSerialization` with the specified reading options. + /// + /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. + /// + /// - returns: A JSON object response serializer. + public static func jsonResponseSerializer( + options: JSONSerialization.ReadingOptions = .allowFragments) + -> DataResponseSerializer + { + return DataResponseSerializer { _, response, data, error in + return Request.serializeResponseJSON(options: options, response: response, data: data, error: error) + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. + /// - parameter completionHandler: A closure to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responseJSON( + queue: DispatchQueue? = nil, + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: @escaping (DataResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DataRequest.jsonResponseSerializer(options: options), + completionHandler: completionHandler + ) + } +} + +extension DownloadRequest { + /// Creates a response serializer that returns a JSON object result type constructed from the response data using + /// `JSONSerialization` with the specified reading options. + /// + /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. + /// + /// - returns: A JSON object response serializer. + public static func jsonResponseSerializer( + options: JSONSerialization.ReadingOptions = .allowFragments) + -> DownloadResponseSerializer + { + return DownloadResponseSerializer { _, response, fileURL, error in + guard error == nil else { return .failure(error!) } + + guard let fileURL = fileURL else { + return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) + } + + do { + let data = try Data(contentsOf: fileURL) + return Request.serializeResponseJSON(options: options, response: response, data: data, error: error) + } catch { + return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) + } + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. + /// - parameter completionHandler: A closure to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responseJSON( + queue: DispatchQueue? = nil, + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: @escaping (DownloadResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DownloadRequest.jsonResponseSerializer(options: options), + completionHandler: completionHandler + ) + } +} + +// MARK: - Property List + +extension Request { + /// Returns a plist object contained in a result type constructed from the response data using + /// `PropertyListSerialization` with the specified reading options. + /// + /// - parameter options: The property list reading options. Defaults to `[]`. + /// - parameter response: The response from the server. + /// - parameter data: The data returned from the server. + /// - parameter error: The error already encountered if it exists. + /// + /// - returns: The result data type. + public static func serializeResponsePropertyList( + options: PropertyListSerialization.ReadOptions, + response: HTTPURLResponse?, + data: Data?, + error: Error?) + -> Result + { + guard error == nil else { return .failure(error!) } + + if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) } + + guard let validData = data, validData.count > 0 else { + return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)) + } + + do { + let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil) + return .success(plist) + } catch { + return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error))) + } + } +} + +extension DataRequest { + /// Creates a response serializer that returns an object constructed from the response data using + /// `PropertyListSerialization` with the specified reading options. + /// + /// - parameter options: The property list reading options. Defaults to `[]`. + /// + /// - returns: A property list object response serializer. + public static func propertyListResponseSerializer( + options: PropertyListSerialization.ReadOptions = []) + -> DataResponseSerializer + { + return DataResponseSerializer { _, response, data, error in + return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error) + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter options: The property list reading options. Defaults to `[]`. + /// - parameter completionHandler: A closure to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responsePropertyList( + queue: DispatchQueue? = nil, + options: PropertyListSerialization.ReadOptions = [], + completionHandler: @escaping (DataResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DataRequest.propertyListResponseSerializer(options: options), + completionHandler: completionHandler + ) + } +} + +extension DownloadRequest { + /// Creates a response serializer that returns an object constructed from the response data using + /// `PropertyListSerialization` with the specified reading options. + /// + /// - parameter options: The property list reading options. Defaults to `[]`. + /// + /// - returns: A property list object response serializer. + public static func propertyListResponseSerializer( + options: PropertyListSerialization.ReadOptions = []) + -> DownloadResponseSerializer + { + return DownloadResponseSerializer { _, response, fileURL, error in + guard error == nil else { return .failure(error!) } + + guard let fileURL = fileURL else { + return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) + } + + do { + let data = try Data(contentsOf: fileURL) + return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error) + } catch { + return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) + } + } + } + + /// Adds a handler to be called once the request has finished. + /// + /// - parameter options: The property list reading options. Defaults to `[]`. + /// - parameter completionHandler: A closure to be executed once the request has finished. + /// + /// - returns: The request. + @discardableResult + public func responsePropertyList( + queue: DispatchQueue? = nil, + options: PropertyListSerialization.ReadOptions = [], + completionHandler: @escaping (DownloadResponse) -> Void) + -> Self + { + return response( + queue: queue, + responseSerializer: DownloadRequest.propertyListResponseSerializer(options: options), + completionHandler: completionHandler + ) + } +} + +/// A set of HTTP response status code that do not contain response data. +private let emptyDataStatusCodes: Set = [204, 205] diff --git a/Santander-Test/Pods/Alamofire/Source/Result.swift b/Santander-Test/Pods/Alamofire/Source/Result.swift new file mode 100644 index 00000000..e0928089 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Result.swift @@ -0,0 +1,300 @@ +// +// Result.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Used to represent whether a request was successful or encountered an error. +/// +/// - success: The request and all post processing operations were successful resulting in the serialization of the +/// provided associated value. +/// +/// - failure: The request encountered an error resulting in a failure. The associated values are the original data +/// provided by the server as well as the error that caused the failure. +public enum Result { + case success(Value) + case failure(Error) + + /// Returns `true` if the result is a success, `false` otherwise. + public var isSuccess: Bool { + switch self { + case .success: + return true + case .failure: + return false + } + } + + /// Returns `true` if the result is a failure, `false` otherwise. + public var isFailure: Bool { + return !isSuccess + } + + /// Returns the associated value if the result is a success, `nil` otherwise. + public var value: Value? { + switch self { + case .success(let value): + return value + case .failure: + return nil + } + } + + /// Returns the associated error value if the result is a failure, `nil` otherwise. + public var error: Error? { + switch self { + case .success: + return nil + case .failure(let error): + return error + } + } +} + +// MARK: - CustomStringConvertible + +extension Result: CustomStringConvertible { + /// The textual representation used when written to an output stream, which includes whether the result was a + /// success or failure. + public var description: String { + switch self { + case .success: + return "SUCCESS" + case .failure: + return "FAILURE" + } + } +} + +// MARK: - CustomDebugStringConvertible + +extension Result: CustomDebugStringConvertible { + /// The debug textual representation used when written to an output stream, which includes whether the result was a + /// success or failure in addition to the value or error. + public var debugDescription: String { + switch self { + case .success(let value): + return "SUCCESS: \(value)" + case .failure(let error): + return "FAILURE: \(error)" + } + } +} + +// MARK: - Functional APIs + +extension Result { + /// Creates a `Result` instance from the result of a closure. + /// + /// A failure result is created when the closure throws, and a success result is created when the closure + /// succeeds without throwing an error. + /// + /// func someString() throws -> String { ... } + /// + /// let result = Result(value: { + /// return try someString() + /// }) + /// + /// // The type of result is Result + /// + /// The trailing closure syntax is also supported: + /// + /// let result = Result { try someString() } + /// + /// - parameter value: The closure to execute and create the result for. + public init(value: () throws -> Value) { + do { + self = try .success(value()) + } catch { + self = .failure(error) + } + } + + /// Returns the success value, or throws the failure error. + /// + /// let possibleString: Result = .success("success") + /// try print(possibleString.unwrap()) + /// // Prints "success" + /// + /// let noString: Result = .failure(error) + /// try print(noString.unwrap()) + /// // Throws error + public func unwrap() throws -> Value { + switch self { + case .success(let value): + return value + case .failure(let error): + throw error + } + } + + /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. + /// + /// Use the `map` method with a closure that does not throw. For example: + /// + /// let possibleData: Result = .success(Data()) + /// let possibleInt = possibleData.map { $0.count } + /// try print(possibleInt.unwrap()) + /// // Prints "0" + /// + /// let noData: Result = .failure(error) + /// let noInt = noData.map { $0.count } + /// try print(noInt.unwrap()) + /// // Throws error + /// + /// - parameter transform: A closure that takes the success value of the `Result` instance. + /// + /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the + /// same failure. + public func map(_ transform: (Value) -> T) -> Result { + switch self { + case .success(let value): + return .success(transform(value)) + case .failure(let error): + return .failure(error) + } + } + + /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. + /// + /// Use the `flatMap` method with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.flatMap { + /// try JSONSerialization.jsonObject(with: $0) + /// } + /// + /// - parameter transform: A closure that takes the success value of the instance. + /// + /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the + /// same failure. + public func flatMap(_ transform: (Value) throws -> T) -> Result { + switch self { + case .success(let value): + do { + return try .success(transform(value)) + } catch { + return .failure(error) + } + case .failure(let error): + return .failure(error) + } + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `mapError` function with a closure that does not throw. For example: + /// + /// let possibleData: Result = .failure(someError) + /// let withMyError: Result = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns + /// the same instance. + public func mapError(_ transform: (Error) -> T) -> Result { + switch self { + case .failure(let error): + return .failure(transform(error)) + case .success: + return self + } + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `flatMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.flatMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns + /// the same instance. + public func flatMapError(_ transform: (Error) throws -> T) -> Result { + switch self { + case .failure(let error): + do { + return try .failure(transform(error)) + } catch { + return .failure(error) + } + case .success: + return self + } + } + + /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. + /// + /// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A closure that takes the success value of this instance. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func withValue(_ closure: (Value) throws -> Void) rethrows -> Result { + if case let .success(value) = self { try closure(value) } + + return self + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A closure that takes the success value of this instance. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func withError(_ closure: (Error) throws -> Void) rethrows -> Result { + if case let .failure(error) = self { try closure(error) } + + return self + } + + /// Evaluates the specified closure when the `Result` is a success. + /// + /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A `Void` closure. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func ifSuccess(_ closure: () throws -> Void) rethrows -> Result { + if isSuccess { try closure() } + + return self + } + + /// Evaluates the specified closure when the `Result` is a failure. + /// + /// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A `Void` closure. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func ifFailure(_ closure: () throws -> Void) rethrows -> Result { + if isFailure { try closure() } + + return self + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/ServerTrustPolicy.swift b/Santander-Test/Pods/Alamofire/Source/ServerTrustPolicy.swift new file mode 100644 index 00000000..dea099e2 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/ServerTrustPolicy.swift @@ -0,0 +1,307 @@ +// +// ServerTrustPolicy.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. +open class ServerTrustPolicyManager { + /// The dictionary of policies mapped to a particular host. + public let policies: [String: ServerTrustPolicy] + + /// Initializes the `ServerTrustPolicyManager` instance with the given policies. + /// + /// Since different servers and web services can have different leaf certificates, intermediate and even root + /// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This + /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key + /// pinning for host3 and disabling evaluation for host4. + /// + /// - parameter policies: A dictionary of all policies mapped to a particular host. + /// + /// - returns: The new `ServerTrustPolicyManager` instance. + public init(policies: [String: ServerTrustPolicy]) { + self.policies = policies + } + + /// Returns the `ServerTrustPolicy` for the given host if applicable. + /// + /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override + /// this method and implement more complex mapping implementations such as wildcards. + /// + /// - parameter host: The host to use when searching for a matching policy. + /// + /// - returns: The server trust policy for the given host if found. + open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { + return policies[host] + } +} + +// MARK: - + +extension URLSession { + private struct AssociatedKeys { + static var managerKey = "URLSession.ServerTrustPolicyManager" + } + + var serverTrustPolicyManager: ServerTrustPolicyManager? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager + } + set (manager) { + objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} + +// MARK: - ServerTrustPolicy + +/// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when +/// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust +/// with a given set of criteria to determine whether the server trust is valid and the connection should be made. +/// +/// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other +/// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged +/// to route all communication over an HTTPS connection with pinning enabled. +/// +/// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to +/// validate the host provided by the challenge. Applications are encouraged to always +/// validate the host in production environments to guarantee the validity of the server's +/// certificate chain. +/// +/// - performRevokedEvaluation: Uses the default and revoked server trust evaluations allowing you to control whether to +/// validate the host provided by the challenge as well as specify the revocation flags for +/// testing for revoked certificates. Apple platforms did not start testing for revoked +/// certificates automatically until iOS 10.1, macOS 10.12 and tvOS 10.1 which is +/// demonstrated in our TLS tests. Applications are encouraged to always validate the host +/// in production environments to guarantee the validity of the server's certificate chain. +/// +/// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is +/// considered valid if one of the pinned certificates match one of the server certificates. +/// By validating both the certificate chain and host, certificate pinning provides a very +/// secure form of server trust validation mitigating most, if not all, MITM attacks. +/// Applications are encouraged to always validate the host and require a valid certificate +/// chain in production environments. +/// +/// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered +/// valid if one of the pinned public keys match one of the server certificate public keys. +/// By validating both the certificate chain and host, public key pinning provides a very +/// secure form of server trust validation mitigating most, if not all, MITM attacks. +/// Applications are encouraged to always validate the host and require a valid certificate +/// chain in production environments. +/// +/// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. +/// +/// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust. +public enum ServerTrustPolicy { + case performDefaultEvaluation(validateHost: Bool) + case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags) + case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) + case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) + case disableEvaluation + case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) + + // MARK: - Bundle Location + + /// Returns all certificates within the given bundle with a `.cer` file extension. + /// + /// - parameter bundle: The bundle to search for all `.cer` files. + /// + /// - returns: All certificates within the given bundle. + public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] { + var certificates: [SecCertificate] = [] + + let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in + bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil) + }.joined()) + + for path in paths { + if + let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, + let certificate = SecCertificateCreateWithData(nil, certificateData) + { + certificates.append(certificate) + } + } + + return certificates + } + + /// Returns all public keys within the given bundle with a `.cer` file extension. + /// + /// - parameter bundle: The bundle to search for all `*.cer` files. + /// + /// - returns: All public keys within the given bundle. + public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] { + var publicKeys: [SecKey] = [] + + for certificate in certificates(in: bundle) { + if let publicKey = publicKey(for: certificate) { + publicKeys.append(publicKey) + } + } + + return publicKeys + } + + // MARK: - Evaluation + + /// Evaluates whether the server trust is valid for the given host. + /// + /// - parameter serverTrust: The server trust to evaluate. + /// - parameter host: The host of the challenge protection space. + /// + /// - returns: Whether the server trust is valid. + public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool { + var serverTrustIsValid = false + + switch self { + case let .performDefaultEvaluation(validateHost): + let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + SecTrustSetPolicies(serverTrust, policy) + + serverTrustIsValid = trustIsValid(serverTrust) + case let .performRevokedEvaluation(validateHost, revocationFlags): + let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + let revokedPolicy = SecPolicyCreateRevocation(revocationFlags) + SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef) + + serverTrustIsValid = trustIsValid(serverTrust) + case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost): + if validateCertificateChain { + let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + SecTrustSetPolicies(serverTrust, policy) + + SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray) + SecTrustSetAnchorCertificatesOnly(serverTrust, true) + + serverTrustIsValid = trustIsValid(serverTrust) + } else { + let serverCertificatesDataArray = certificateData(for: serverTrust) + let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates) + + outerLoop: for serverCertificateData in serverCertificatesDataArray { + for pinnedCertificateData in pinnedCertificatesDataArray { + if serverCertificateData == pinnedCertificateData { + serverTrustIsValid = true + break outerLoop + } + } + } + } + case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): + var certificateChainEvaluationPassed = true + + if validateCertificateChain { + let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + SecTrustSetPolicies(serverTrust, policy) + + certificateChainEvaluationPassed = trustIsValid(serverTrust) + } + + if certificateChainEvaluationPassed { + outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] { + for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { + if serverPublicKey.isEqual(pinnedPublicKey) { + serverTrustIsValid = true + break outerLoop + } + } + } + } + case .disableEvaluation: + serverTrustIsValid = true + case let .customEvaluation(closure): + serverTrustIsValid = closure(serverTrust, host) + } + + return serverTrustIsValid + } + + // MARK: - Private - Trust Validation + + private func trustIsValid(_ trust: SecTrust) -> Bool { + var isValid = false + + var result = SecTrustResultType.invalid + let status = SecTrustEvaluate(trust, &result) + + if status == errSecSuccess { + let unspecified = SecTrustResultType.unspecified + let proceed = SecTrustResultType.proceed + + + isValid = result == unspecified || result == proceed + } + + return isValid + } + + // MARK: - Private - Certificate Data + + private func certificateData(for trust: SecTrust) -> [Data] { + var certificates: [SecCertificate] = [] + + for index in 0.. [Data] { + return certificates.map { SecCertificateCopyData($0) as Data } + } + + // MARK: - Private - Public Key Extraction + + private static func publicKeys(for trust: SecTrust) -> [SecKey] { + var publicKeys: [SecKey] = [] + + for index in 0.. SecKey? { + var publicKey: SecKey? + + let policy = SecPolicyCreateBasicX509() + var trust: SecTrust? + let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) + + if let trust = trust, trustCreationStatus == errSecSuccess { + publicKey = SecTrustCopyPublicKey(trust) + } + + return publicKey + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/SessionDelegate.swift b/Santander-Test/Pods/Alamofire/Source/SessionDelegate.swift new file mode 100644 index 00000000..4964f1ee --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/SessionDelegate.swift @@ -0,0 +1,725 @@ +// +// SessionDelegate.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Responsible for handling all delegate callbacks for the underlying session. +open class SessionDelegate: NSObject { + + // MARK: URLSessionDelegate Overrides + + /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`. + open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? + + /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`. + open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? + + /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`. + open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? + + /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`. + open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)? + + // MARK: URLSessionTaskDelegate Overrides + + /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`. + open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? + + /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and + /// requires the caller to call the `completionHandler`. + open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)? + + /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`. + open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? + + /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and + /// requires the caller to call the `completionHandler`. + open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? + + /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`. + open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? + + /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and + /// requires the caller to call the `completionHandler`. + open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)? + + /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`. + open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? + + /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`. + open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)? + + // MARK: URLSessionDataDelegate Overrides + + /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`. + open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? + + /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and + /// requires caller to call the `completionHandler`. + open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)? + + /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`. + open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? + + /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`. + open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? + + /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`. + open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? + + /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and + /// requires caller to call the `completionHandler`. + open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)? + + // MARK: URLSessionDownloadDelegate Overrides + + /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`. + open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)? + + /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`. + open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? + + /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`. + open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? + + // MARK: URLSessionStreamDelegate Overrides + +#if !os(watchOS) + + /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`. + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? { + get { + return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void + } + set { + _streamTaskReadClosed = newValue + } + } + + /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`. + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? { + get { + return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void + } + set { + _streamTaskWriteClosed = newValue + } + } + + /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`. + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? { + get { + return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void + } + set { + _streamTaskBetterRouteDiscovered = newValue + } + } + + /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`. + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? { + get { + return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void + } + set { + _streamTaskDidBecomeInputStream = newValue + } + } + + var _streamTaskReadClosed: Any? + var _streamTaskWriteClosed: Any? + var _streamTaskBetterRouteDiscovered: Any? + var _streamTaskDidBecomeInputStream: Any? + +#endif + + // MARK: Properties + + var retrier: RequestRetrier? + weak var sessionManager: SessionManager? + + var requests: [Int: Request] = [:] + private let lock = NSLock() + + /// Access the task delegate for the specified task in a thread-safe manner. + open subscript(task: URLSessionTask) -> Request? { + get { + lock.lock() ; defer { lock.unlock() } + return requests[task.taskIdentifier] + } + set { + lock.lock() ; defer { lock.unlock() } + requests[task.taskIdentifier] = newValue + } + } + + // MARK: Lifecycle + + /// Initializes the `SessionDelegate` instance. + /// + /// - returns: The new `SessionDelegate` instance. + public override init() { + super.init() + } + + // MARK: NSObject Overrides + + /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond + /// to a specified message. + /// + /// - parameter selector: A selector that identifies a message. + /// + /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`. + open override func responds(to selector: Selector) -> Bool { + #if !os(macOS) + if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) { + return sessionDidFinishEventsForBackgroundURLSession != nil + } + #endif + + #if !os(watchOS) + if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) { + switch selector { + case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)): + return streamTaskReadClosed != nil + case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)): + return streamTaskWriteClosed != nil + case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)): + return streamTaskBetterRouteDiscovered != nil + case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)): + return streamTaskDidBecomeInputAndOutputStreams != nil + default: + break + } + } + #endif + + switch selector { + case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)): + return sessionDidBecomeInvalidWithError != nil + case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)): + return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil) + case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)): + return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil) + case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)): + return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil) + default: + return type(of: self).instancesRespond(to: selector) + } + } +} + +// MARK: - URLSessionDelegate + +extension SessionDelegate: URLSessionDelegate { + /// Tells the delegate that the session has been invalidated. + /// + /// - parameter session: The session object that was invalidated. + /// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit. + open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + sessionDidBecomeInvalidWithError?(session, error) + } + + /// Requests credentials from the delegate in response to a session-level authentication request from the + /// remote server. + /// + /// - parameter session: The session containing the task that requested authentication. + /// - parameter challenge: An object that contains the request for authentication. + /// - parameter completionHandler: A handler that your delegate method must call providing the disposition + /// and credential. + open func urlSession( + _ session: URLSession, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) + { + guard sessionDidReceiveChallengeWithCompletion == nil else { + sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler) + return + } + + var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling + var credential: URLCredential? + + if let sessionDidReceiveChallenge = sessionDidReceiveChallenge { + (disposition, credential) = sessionDidReceiveChallenge(session, challenge) + } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { + let host = challenge.protectionSpace.host + + if + let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), + let serverTrust = challenge.protectionSpace.serverTrust + { + if serverTrustPolicy.evaluate(serverTrust, forHost: host) { + disposition = .useCredential + credential = URLCredential(trust: serverTrust) + } else { + disposition = .cancelAuthenticationChallenge + } + } + } + + completionHandler(disposition, credential) + } + +#if !os(macOS) + + /// Tells the delegate that all messages enqueued for a session have been delivered. + /// + /// - parameter session: The session that no longer has any outstanding requests. + open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { + sessionDidFinishEventsForBackgroundURLSession?(session) + } + +#endif +} + +// MARK: - URLSessionTaskDelegate + +extension SessionDelegate: URLSessionTaskDelegate { + /// Tells the delegate that the remote server requested an HTTP redirect. + /// + /// - parameter session: The session containing the task whose request resulted in a redirect. + /// - parameter task: The task whose request resulted in a redirect. + /// - parameter response: An object containing the server’s response to the original request. + /// - parameter request: A URL request object filled out with the new location. + /// - parameter completionHandler: A closure that your handler should call with either the value of the request + /// parameter, a modified URL request object, or NULL to refuse the redirect and + /// return the body of the redirect response. + open func urlSession( + _ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: @escaping (URLRequest?) -> Void) + { + guard taskWillPerformHTTPRedirectionWithCompletion == nil else { + taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler) + return + } + + var redirectRequest: URLRequest? = request + + if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { + redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) + } + + completionHandler(redirectRequest) + } + + /// Requests credentials from the delegate in response to an authentication request from the remote server. + /// + /// - parameter session: The session containing the task whose request requires authentication. + /// - parameter task: The task whose request requires authentication. + /// - parameter challenge: An object that contains the request for authentication. + /// - parameter completionHandler: A handler that your delegate method must call providing the disposition + /// and credential. + open func urlSession( + _ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) + { + guard taskDidReceiveChallengeWithCompletion == nil else { + taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler) + return + } + + if let taskDidReceiveChallenge = taskDidReceiveChallenge { + let result = taskDidReceiveChallenge(session, task, challenge) + completionHandler(result.0, result.1) + } else if let delegate = self[task]?.delegate { + delegate.urlSession( + session, + task: task, + didReceive: challenge, + completionHandler: completionHandler + ) + } else { + urlSession(session, didReceive: challenge, completionHandler: completionHandler) + } + } + + /// Tells the delegate when a task requires a new request body stream to send to the remote server. + /// + /// - parameter session: The session containing the task that needs a new body stream. + /// - parameter task: The task that needs a new body stream. + /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream. + open func urlSession( + _ session: URLSession, + task: URLSessionTask, + needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) + { + guard taskNeedNewBodyStreamWithCompletion == nil else { + taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler) + return + } + + if let taskNeedNewBodyStream = taskNeedNewBodyStream { + completionHandler(taskNeedNewBodyStream(session, task)) + } else if let delegate = self[task]?.delegate { + delegate.urlSession(session, task: task, needNewBodyStream: completionHandler) + } + } + + /// Periodically informs the delegate of the progress of sending body content to the server. + /// + /// - parameter session: The session containing the data task. + /// - parameter task: The data task. + /// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called. + /// - parameter totalBytesSent: The total number of bytes sent so far. + /// - parameter totalBytesExpectedToSend: The expected length of the body data. + open func urlSession( + _ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) + { + if let taskDidSendBodyData = taskDidSendBodyData { + taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) + } else if let delegate = self[task]?.delegate as? UploadTaskDelegate { + delegate.URLSession( + session, + task: task, + didSendBodyData: bytesSent, + totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend + ) + } + } + +#if !os(watchOS) + + /// Tells the delegate that the session finished collecting metrics for the task. + /// + /// - parameter session: The session collecting the metrics. + /// - parameter task: The task whose metrics have been collected. + /// - parameter metrics: The collected metrics. + @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) + @objc(URLSession:task:didFinishCollectingMetrics:) + open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { + self[task]?.delegate.metrics = metrics + } + +#endif + + /// Tells the delegate that the task finished transferring data. + /// + /// - parameter session: The session containing the task whose request finished transferring data. + /// - parameter task: The task whose request finished transferring data. + /// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil. + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + /// Executed after it is determined that the request is not going to be retried + let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in + guard let strongSelf = self else { return } + + strongSelf.taskDidComplete?(session, task, error) + + strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error) + + var userInfo: [String: Any] = [Notification.Key.Task: task] + + if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data { + userInfo[Notification.Key.ResponseData] = data + } + + NotificationCenter.default.post( + name: Notification.Name.Task.DidComplete, + object: strongSelf, + userInfo: userInfo + ) + + strongSelf[task] = nil + } + + guard let request = self[task], let sessionManager = sessionManager else { + completeTask(session, task, error) + return + } + + // Run all validations on the request before checking if an error occurred + request.validations.forEach { $0() } + + // Determine whether an error has occurred + var error: Error? = error + + if request.delegate.error != nil { + error = request.delegate.error + } + + /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request + /// should be retried. Otherwise, complete the task by notifying the task delegate. + if let retrier = retrier, let error = error { + retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in + guard shouldRetry else { completeTask(session, task, error) ; return } + + DispatchQueue.utility.after(timeDelay) { [weak self] in + guard let strongSelf = self else { return } + + let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false + + if retrySucceeded, let task = request.task { + strongSelf[task] = request + return + } else { + completeTask(session, task, error) + } + } + } + } else { + completeTask(session, task, error) + } + } +} + +// MARK: - URLSessionDataDelegate + +extension SessionDelegate: URLSessionDataDelegate { + /// Tells the delegate that the data task received the initial reply (headers) from the server. + /// + /// - parameter session: The session containing the data task that received an initial reply. + /// - parameter dataTask: The data task that received an initial reply. + /// - parameter response: A URL response object populated with headers. + /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a + /// constant to indicate whether the transfer should continue as a data task or + /// should become a download task. + open func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didReceive response: URLResponse, + completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) + { + guard dataTaskDidReceiveResponseWithCompletion == nil else { + dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler) + return + } + + var disposition: URLSession.ResponseDisposition = .allow + + if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { + disposition = dataTaskDidReceiveResponse(session, dataTask, response) + } + + completionHandler(disposition) + } + + /// Tells the delegate that the data task was changed to a download task. + /// + /// - parameter session: The session containing the task that was replaced by a download task. + /// - parameter dataTask: The data task that was replaced by a download task. + /// - parameter downloadTask: The new download task that replaced the data task. + open func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didBecome downloadTask: URLSessionDownloadTask) + { + if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask { + dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask) + } else { + self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask) + } + } + + /// Tells the delegate that the data task has received some of the expected data. + /// + /// - parameter session: The session containing the data task that provided data. + /// - parameter dataTask: The data task that provided data. + /// - parameter data: A data object containing the transferred data. + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + if let dataTaskDidReceiveData = dataTaskDidReceiveData { + dataTaskDidReceiveData(session, dataTask, data) + } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate { + delegate.urlSession(session, dataTask: dataTask, didReceive: data) + } + } + + /// Asks the delegate whether the data (or upload) task should store the response in the cache. + /// + /// - parameter session: The session containing the data (or upload) task. + /// - parameter dataTask: The data (or upload) task. + /// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current + /// caching policy and the values of certain received headers, such as the Pragma + /// and Cache-Control headers. + /// - parameter completionHandler: A block that your handler must call, providing either the original proposed + /// response, a modified version of that response, or NULL to prevent caching the + /// response. If your delegate implements this method, it must call this completion + /// handler; otherwise, your app leaks memory. + open func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse, + completionHandler: @escaping (CachedURLResponse?) -> Void) + { + guard dataTaskWillCacheResponseWithCompletion == nil else { + dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler) + return + } + + if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { + completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse)) + } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate { + delegate.urlSession( + session, + dataTask: dataTask, + willCacheResponse: proposedResponse, + completionHandler: completionHandler + ) + } else { + completionHandler(proposedResponse) + } + } +} + +// MARK: - URLSessionDownloadDelegate + +extension SessionDelegate: URLSessionDownloadDelegate { + /// Tells the delegate that a download task has finished downloading. + /// + /// - parameter session: The session containing the download task that finished. + /// - parameter downloadTask: The download task that finished. + /// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either + /// open the file for reading or move it to a permanent location in your app’s sandbox + /// container directory before returning from this delegate method. + open func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) + { + if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { + downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) + } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { + delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) + } + } + + /// Periodically informs the delegate about the download’s progress. + /// + /// - parameter session: The session containing the download task. + /// - parameter downloadTask: The download task. + /// - parameter bytesWritten: The number of bytes transferred since the last time this delegate + /// method was called. + /// - parameter totalBytesWritten: The total number of bytes transferred so far. + /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length + /// header. If this header was not provided, the value is + /// `NSURLSessionTransferSizeUnknown`. + open func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) + { + if let downloadTaskDidWriteData = downloadTaskDidWriteData { + downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) + } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { + delegate.urlSession( + session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite + ) + } + } + + /// Tells the delegate that the download task has resumed downloading. + /// + /// - parameter session: The session containing the download task that finished. + /// - parameter downloadTask: The download task that resumed. See explanation in the discussion. + /// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the + /// existing content, then this value is zero. Otherwise, this value is an + /// integer representing the number of bytes on disk that do not need to be + /// retrieved again. + /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header. + /// If this header was not provided, the value is NSURLSessionTransferSizeUnknown. + open func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) + { + if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { + downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) + } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { + delegate.urlSession( + session, + downloadTask: downloadTask, + didResumeAtOffset: fileOffset, + expectedTotalBytes: expectedTotalBytes + ) + } + } +} + +// MARK: - URLSessionStreamDelegate + +#if !os(watchOS) + +@available(iOS 9.0, macOS 10.11, tvOS 9.0, *) +extension SessionDelegate: URLSessionStreamDelegate { + /// Tells the delegate that the read side of the connection has been closed. + /// + /// - parameter session: The session. + /// - parameter streamTask: The stream task. + open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) { + streamTaskReadClosed?(session, streamTask) + } + + /// Tells the delegate that the write side of the connection has been closed. + /// + /// - parameter session: The session. + /// - parameter streamTask: The stream task. + open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) { + streamTaskWriteClosed?(session, streamTask) + } + + /// Tells the delegate that the system has determined that a better route to the host is available. + /// + /// - parameter session: The session. + /// - parameter streamTask: The stream task. + open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) { + streamTaskBetterRouteDiscovered?(session, streamTask) + } + + /// Tells the delegate that the stream task has been completed and provides the unopened stream objects. + /// + /// - parameter session: The session. + /// - parameter streamTask: The stream task. + /// - parameter inputStream: The new input stream. + /// - parameter outputStream: The new output stream. + open func urlSession( + _ session: URLSession, + streamTask: URLSessionStreamTask, + didBecome inputStream: InputStream, + outputStream: OutputStream) + { + streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream) + } +} + +#endif diff --git a/Santander-Test/Pods/Alamofire/Source/SessionManager.swift b/Santander-Test/Pods/Alamofire/Source/SessionManager.swift new file mode 100644 index 00000000..02c36a76 --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/SessionManager.swift @@ -0,0 +1,899 @@ +// +// SessionManager.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`. +open class SessionManager { + + // MARK: - Helper Types + + /// Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as + /// associated values. + /// + /// - Success: Represents a successful `MultipartFormData` encoding and contains the new `UploadRequest` along with + /// streaming information. + /// - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding + /// error. + public enum MultipartFormDataEncodingResult { + case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?) + case failure(Error) + } + + // MARK: - Properties + + /// A default instance of `SessionManager`, used by top-level Alamofire request methods, and suitable for use + /// directly for any ad hoc requests. + public static let `default`: SessionManager = { + let configuration = URLSessionConfiguration.default + configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders + + return SessionManager(configuration: configuration) + }() + + /// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers. + public static let defaultHTTPHeaders: HTTPHeaders = { + // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3 + let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5" + + // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5 + let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in + let quality = 1.0 - (Double(index) * 0.1) + return "\(languageCode);q=\(quality)" + }.joined(separator: ", ") + + // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3 + // Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0` + let userAgent: String = { + if let info = Bundle.main.infoDictionary { + let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown" + let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown" + let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown" + let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown" + + let osNameVersion: String = { + let version = ProcessInfo.processInfo.operatingSystemVersion + let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)" + + let osName: String = { + #if os(iOS) + return "iOS" + #elseif os(watchOS) + return "watchOS" + #elseif os(tvOS) + return "tvOS" + #elseif os(macOS) + return "OS X" + #elseif os(Linux) + return "Linux" + #else + return "Unknown" + #endif + }() + + return "\(osName) \(versionString)" + }() + + let alamofireVersion: String = { + guard + let afInfo = Bundle(for: SessionManager.self).infoDictionary, + let build = afInfo["CFBundleShortVersionString"] + else { return "Unknown" } + + return "Alamofire/\(build)" + }() + + return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)" + } + + return "Alamofire" + }() + + return [ + "Accept-Encoding": acceptEncoding, + "Accept-Language": acceptLanguage, + "User-Agent": userAgent + ] + }() + + /// Default memory threshold used when encoding `MultipartFormData` in bytes. + public static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000 + + /// The underlying session. + public let session: URLSession + + /// The session delegate handling all the task and session delegate callbacks. + public let delegate: SessionDelegate + + /// Whether to start requests immediately after being constructed. `true` by default. + open var startRequestsImmediately: Bool = true + + /// The request adapter called each time a new request is created. + open var adapter: RequestAdapter? + + /// The request retrier called each time a request encounters an error to determine whether to retry the request. + open var retrier: RequestRetrier? { + get { return delegate.retrier } + set { delegate.retrier = newValue } + } + + /// The background completion handler closure provided by the UIApplicationDelegate + /// `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background + /// completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation + /// will automatically call the handler. + /// + /// If you need to handle your own events before the handler is called, then you need to override the + /// SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished. + /// + /// `nil` by default. + open var backgroundCompletionHandler: (() -> Void)? + + let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString) + + // MARK: - Lifecycle + + /// Creates an instance with the specified `configuration`, `delegate` and `serverTrustPolicyManager`. + /// + /// - parameter configuration: The configuration used to construct the managed session. + /// `URLSessionConfiguration.default` by default. + /// - parameter delegate: The delegate used when initializing the session. `SessionDelegate()` by + /// default. + /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust + /// challenges. `nil` by default. + /// + /// - returns: The new `SessionManager` instance. + public init( + configuration: URLSessionConfiguration = URLSessionConfiguration.default, + delegate: SessionDelegate = SessionDelegate(), + serverTrustPolicyManager: ServerTrustPolicyManager? = nil) + { + self.delegate = delegate + self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) + + commonInit(serverTrustPolicyManager: serverTrustPolicyManager) + } + + /// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`. + /// + /// - parameter session: The URL session. + /// - parameter delegate: The delegate of the URL session. Must equal the URL session's delegate. + /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust + /// challenges. `nil` by default. + /// + /// - returns: The new `SessionManager` instance if the URL session's delegate matches; `nil` otherwise. + public init?( + session: URLSession, + delegate: SessionDelegate, + serverTrustPolicyManager: ServerTrustPolicyManager? = nil) + { + guard delegate === session.delegate else { return nil } + + self.delegate = delegate + self.session = session + + commonInit(serverTrustPolicyManager: serverTrustPolicyManager) + } + + private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) { + session.serverTrustPolicyManager = serverTrustPolicyManager + + delegate.sessionManager = self + + delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in + guard let strongSelf = self else { return } + DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() } + } + } + + deinit { + session.invalidateAndCancel() + } + + // MARK: - Data Request + + /// Creates a `DataRequest` to retrieve the contents of the specified `url`, `method`, `parameters`, `encoding` + /// and `headers`. + /// + /// - parameter url: The URL. + /// - parameter method: The HTTP method. `.get` by default. + /// - parameter parameters: The parameters. `nil` by default. + /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. + /// - parameter headers: The HTTP headers. `nil` by default. + /// + /// - returns: The created `DataRequest`. + @discardableResult + open func request( + _ url: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil) + -> DataRequest + { + var originalRequest: URLRequest? + + do { + originalRequest = try URLRequest(url: url, method: method, headers: headers) + let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters) + return request(encodedURLRequest) + } catch { + return request(originalRequest, failedWith: error) + } + } + + /// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter urlRequest: The URL request. + /// + /// - returns: The created `DataRequest`. + @discardableResult + open func request(_ urlRequest: URLRequestConvertible) -> DataRequest { + var originalRequest: URLRequest? + + do { + originalRequest = try urlRequest.asURLRequest() + let originalTask = DataRequest.Requestable(urlRequest: originalRequest!) + + let task = try originalTask.task(session: session, adapter: adapter, queue: queue) + let request = DataRequest(session: session, requestTask: .data(originalTask, task)) + + delegate[task] = request + + if startRequestsImmediately { request.resume() } + + return request + } catch { + return request(originalRequest, failedWith: error) + } + } + + // MARK: Private - Request Implementation + + private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest { + var requestTask: Request.RequestTask = .data(nil, nil) + + if let urlRequest = urlRequest { + let originalTask = DataRequest.Requestable(urlRequest: urlRequest) + requestTask = .data(originalTask, nil) + } + + let underlyingError = error.underlyingAdaptError ?? error + let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError) + + if let retrier = retrier, error is AdaptError { + allowRetrier(retrier, toRetry: request, with: underlyingError) + } else { + if startRequestsImmediately { request.resume() } + } + + return request + } + + // MARK: - Download Request + + // MARK: URL Request + + /// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`, + /// `headers` and save them to the `destination`. + /// + /// If `destination` is not specified, the contents will remain in the temporary location determined by the + /// underlying URL session. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter url: The URL. + /// - parameter method: The HTTP method. `.get` by default. + /// - parameter parameters: The parameters. `nil` by default. + /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. + /// - parameter headers: The HTTP headers. `nil` by default. + /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. + /// + /// - returns: The created `DownloadRequest`. + @discardableResult + open func download( + _ url: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + to destination: DownloadRequest.DownloadFileDestination? = nil) + -> DownloadRequest + { + do { + let urlRequest = try URLRequest(url: url, method: method, headers: headers) + let encodedURLRequest = try encoding.encode(urlRequest, with: parameters) + return download(encodedURLRequest, to: destination) + } catch { + return download(nil, to: destination, failedWith: error) + } + } + + /// Creates a `DownloadRequest` to retrieve the contents of a URL based on the specified `urlRequest` and save + /// them to the `destination`. + /// + /// If `destination` is not specified, the contents will remain in the temporary location determined by the + /// underlying URL session. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter urlRequest: The URL request + /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. + /// + /// - returns: The created `DownloadRequest`. + @discardableResult + open func download( + _ urlRequest: URLRequestConvertible, + to destination: DownloadRequest.DownloadFileDestination? = nil) + -> DownloadRequest + { + do { + let urlRequest = try urlRequest.asURLRequest() + return download(.request(urlRequest), to: destination) + } catch { + return download(nil, to: destination, failedWith: error) + } + } + + // MARK: Resume Data + + /// Creates a `DownloadRequest` from the `resumeData` produced from a previous request cancellation to retrieve + /// the contents of the original request and save them to the `destination`. + /// + /// If `destination` is not specified, the contents will remain in the temporary location determined by the + /// underlying URL session. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken + /// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the + /// data is written incorrectly and will always fail to resume the download. For more information about the bug and + /// possible workarounds, please refer to the following Stack Overflow post: + /// + /// - http://stackoverflow.com/a/39347461/1342462 + /// + /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask` + /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for + /// additional information. + /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. + /// + /// - returns: The created `DownloadRequest`. + @discardableResult + open func download( + resumingWith resumeData: Data, + to destination: DownloadRequest.DownloadFileDestination? = nil) + -> DownloadRequest + { + return download(.resumeData(resumeData), to: destination) + } + + // MARK: Private - Download Implementation + + private func download( + _ downloadable: DownloadRequest.Downloadable, + to destination: DownloadRequest.DownloadFileDestination?) + -> DownloadRequest + { + do { + let task = try downloadable.task(session: session, adapter: adapter, queue: queue) + let download = DownloadRequest(session: session, requestTask: .download(downloadable, task)) + + download.downloadDelegate.destination = destination + + delegate[task] = download + + if startRequestsImmediately { download.resume() } + + return download + } catch { + return download(downloadable, to: destination, failedWith: error) + } + } + + private func download( + _ downloadable: DownloadRequest.Downloadable?, + to destination: DownloadRequest.DownloadFileDestination?, + failedWith error: Error) + -> DownloadRequest + { + var downloadTask: Request.RequestTask = .download(nil, nil) + + if let downloadable = downloadable { + downloadTask = .download(downloadable, nil) + } + + let underlyingError = error.underlyingAdaptError ?? error + + let download = DownloadRequest(session: session, requestTask: downloadTask, error: underlyingError) + download.downloadDelegate.destination = destination + + if let retrier = retrier, error is AdaptError { + allowRetrier(retrier, toRetry: download, with: underlyingError) + } else { + if startRequestsImmediately { download.resume() } + } + + return download + } + + // MARK: - Upload Request + + // MARK: File + + /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `file`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter file: The file to upload. + /// - parameter url: The URL. + /// - parameter method: The HTTP method. `.post` by default. + /// - parameter headers: The HTTP headers. `nil` by default. + /// + /// - returns: The created `UploadRequest`. + @discardableResult + open func upload( + _ fileURL: URL, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil) + -> UploadRequest + { + do { + let urlRequest = try URLRequest(url: url, method: method, headers: headers) + return upload(fileURL, with: urlRequest) + } catch { + return upload(nil, failedWith: error) + } + } + + /// Creates a `UploadRequest` from the specified `urlRequest` for uploading the `file`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter file: The file to upload. + /// - parameter urlRequest: The URL request. + /// + /// - returns: The created `UploadRequest`. + @discardableResult + open func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest { + do { + let urlRequest = try urlRequest.asURLRequest() + return upload(.file(fileURL, urlRequest)) + } catch { + return upload(nil, failedWith: error) + } + } + + // MARK: Data + + /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `data`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter data: The data to upload. + /// - parameter url: The URL. + /// - parameter method: The HTTP method. `.post` by default. + /// - parameter headers: The HTTP headers. `nil` by default. + /// + /// - returns: The created `UploadRequest`. + @discardableResult + open func upload( + _ data: Data, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil) + -> UploadRequest + { + do { + let urlRequest = try URLRequest(url: url, method: method, headers: headers) + return upload(data, with: urlRequest) + } catch { + return upload(nil, failedWith: error) + } + } + + /// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `data`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter data: The data to upload. + /// - parameter urlRequest: The URL request. + /// + /// - returns: The created `UploadRequest`. + @discardableResult + open func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest { + do { + let urlRequest = try urlRequest.asURLRequest() + return upload(.data(data, urlRequest)) + } catch { + return upload(nil, failedWith: error) + } + } + + // MARK: InputStream + + /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `stream`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter stream: The stream to upload. + /// - parameter url: The URL. + /// - parameter method: The HTTP method. `.post` by default. + /// - parameter headers: The HTTP headers. `nil` by default. + /// + /// - returns: The created `UploadRequest`. + @discardableResult + open func upload( + _ stream: InputStream, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil) + -> UploadRequest + { + do { + let urlRequest = try URLRequest(url: url, method: method, headers: headers) + return upload(stream, with: urlRequest) + } catch { + return upload(nil, failedWith: error) + } + } + + /// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `stream`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter stream: The stream to upload. + /// - parameter urlRequest: The URL request. + /// + /// - returns: The created `UploadRequest`. + @discardableResult + open func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest { + do { + let urlRequest = try urlRequest.asURLRequest() + return upload(.stream(stream, urlRequest)) + } catch { + return upload(nil, failedWith: error) + } + } + + // MARK: MultipartFormData + + /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new + /// `UploadRequest` using the `url`, `method` and `headers`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative + /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + /// used for larger payloads such as video content. + /// + /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + /// technique was used. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. + /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. + /// `multipartFormDataEncodingMemoryThreshold` by default. + /// - parameter url: The URL. + /// - parameter method: The HTTP method. `.post` by default. + /// - parameter headers: The HTTP headers. `nil` by default. + /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. + open func upload( + multipartFormData: @escaping (MultipartFormData) -> Void, + usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, + to url: URLConvertible, + method: HTTPMethod = .post, + headers: HTTPHeaders? = nil, + queue: DispatchQueue? = nil, + encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) + { + do { + let urlRequest = try URLRequest(url: url, method: method, headers: headers) + + return upload( + multipartFormData: multipartFormData, + usingThreshold: encodingMemoryThreshold, + with: urlRequest, + queue: queue, + encodingCompletion: encodingCompletion + ) + } catch { + (queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) } + } + } + + /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new + /// `UploadRequest` using the `urlRequest`. + /// + /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative + /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + /// used for larger payloads such as video content. + /// + /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + /// technique was used. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. + /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. + /// `multipartFormDataEncodingMemoryThreshold` by default. + /// - parameter urlRequest: The URL request. + /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. + open func upload( + multipartFormData: @escaping (MultipartFormData) -> Void, + usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, + with urlRequest: URLRequestConvertible, + queue: DispatchQueue? = nil, + encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) + { + DispatchQueue.global(qos: .utility).async { + let formData = MultipartFormData() + multipartFormData(formData) + + var tempFileURL: URL? + + do { + var urlRequestWithContentType = try urlRequest.asURLRequest() + urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type") + + let isBackgroundSession = self.session.configuration.identifier != nil + + if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession { + let data = try formData.encode() + + let encodingResult = MultipartFormDataEncodingResult.success( + request: self.upload(data, with: urlRequestWithContentType), + streamingFromDisk: false, + streamFileURL: nil + ) + + (queue ?? DispatchQueue.main).async { encodingCompletion?(encodingResult) } + } else { + let fileManager = FileManager.default + let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()) + let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") + let fileName = UUID().uuidString + let fileURL = directoryURL.appendingPathComponent(fileName) + + tempFileURL = fileURL + + var directoryError: Error? + + // Create directory inside serial queue to ensure two threads don't do this in parallel + self.queue.sync { + do { + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + } catch { + directoryError = error + } + } + + if let directoryError = directoryError { throw directoryError } + + try formData.writeEncodedData(to: fileURL) + + let upload = self.upload(fileURL, with: urlRequestWithContentType) + + // Cleanup the temp file once the upload is complete + upload.delegate.queue.addOperation { + do { + try FileManager.default.removeItem(at: fileURL) + } catch { + // No-op + } + } + + (queue ?? DispatchQueue.main).async { + let encodingResult = MultipartFormDataEncodingResult.success( + request: upload, + streamingFromDisk: true, + streamFileURL: fileURL + ) + + encodingCompletion?(encodingResult) + } + } + } catch { + // Cleanup the temp file in the event that the multipart form data encoding failed + if let tempFileURL = tempFileURL { + do { + try FileManager.default.removeItem(at: tempFileURL) + } catch { + // No-op + } + } + + (queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) } + } + } + } + + // MARK: Private - Upload Implementation + + private func upload(_ uploadable: UploadRequest.Uploadable) -> UploadRequest { + do { + let task = try uploadable.task(session: session, adapter: adapter, queue: queue) + let upload = UploadRequest(session: session, requestTask: .upload(uploadable, task)) + + if case let .stream(inputStream, _) = uploadable { + upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream } + } + + delegate[task] = upload + + if startRequestsImmediately { upload.resume() } + + return upload + } catch { + return upload(uploadable, failedWith: error) + } + } + + private func upload(_ uploadable: UploadRequest.Uploadable?, failedWith error: Error) -> UploadRequest { + var uploadTask: Request.RequestTask = .upload(nil, nil) + + if let uploadable = uploadable { + uploadTask = .upload(uploadable, nil) + } + + let underlyingError = error.underlyingAdaptError ?? error + let upload = UploadRequest(session: session, requestTask: uploadTask, error: underlyingError) + + if let retrier = retrier, error is AdaptError { + allowRetrier(retrier, toRetry: upload, with: underlyingError) + } else { + if startRequestsImmediately { upload.resume() } + } + + return upload + } + +#if !os(watchOS) + + // MARK: - Stream Request + + // MARK: Hostname and Port + + /// Creates a `StreamRequest` for bidirectional streaming using the `hostname` and `port`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter hostName: The hostname of the server to connect to. + /// - parameter port: The port of the server to connect to. + /// + /// - returns: The created `StreamRequest`. + @discardableResult + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + open func stream(withHostName hostName: String, port: Int) -> StreamRequest { + return stream(.stream(hostName: hostName, port: port)) + } + + // MARK: NetService + + /// Creates a `StreamRequest` for bidirectional streaming using the `netService`. + /// + /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + /// + /// - parameter netService: The net service used to identify the endpoint. + /// + /// - returns: The created `StreamRequest`. + @discardableResult + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + open func stream(with netService: NetService) -> StreamRequest { + return stream(.netService(netService)) + } + + // MARK: Private - Stream Implementation + + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + private func stream(_ streamable: StreamRequest.Streamable) -> StreamRequest { + do { + let task = try streamable.task(session: session, adapter: adapter, queue: queue) + let request = StreamRequest(session: session, requestTask: .stream(streamable, task)) + + delegate[task] = request + + if startRequestsImmediately { request.resume() } + + return request + } catch { + return stream(failedWith: error) + } + } + + @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) + private func stream(failedWith error: Error) -> StreamRequest { + let stream = StreamRequest(session: session, requestTask: .stream(nil, nil), error: error) + if startRequestsImmediately { stream.resume() } + return stream + } + +#endif + + // MARK: - Internal - Retry Request + + func retry(_ request: Request) -> Bool { + guard let originalTask = request.originalTask else { return false } + + do { + let task = try originalTask.task(session: session, adapter: adapter, queue: queue) + + if let originalTask = request.task { + delegate[originalTask] = nil // removes the old request to avoid endless growth + } + + request.delegate.task = task // resets all task delegate data + + request.retryCount += 1 + request.startTime = CFAbsoluteTimeGetCurrent() + request.endTime = nil + + task.resume() + + return true + } catch { + request.delegate.error = error.underlyingAdaptError ?? error + return false + } + } + + private func allowRetrier(_ retrier: RequestRetrier, toRetry request: Request, with error: Error) { + DispatchQueue.utility.async { [weak self] in + guard let strongSelf = self else { return } + + retrier.should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in + guard let strongSelf = self else { return } + + guard shouldRetry else { + if strongSelf.startRequestsImmediately { request.resume() } + return + } + + DispatchQueue.utility.after(timeDelay) { + guard let strongSelf = self else { return } + + let retrySucceeded = strongSelf.retry(request) + + if retrySucceeded, let task = request.task { + strongSelf.delegate[task] = request + } else { + if strongSelf.startRequestsImmediately { request.resume() } + } + } + } + } + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/TaskDelegate.swift b/Santander-Test/Pods/Alamofire/Source/TaskDelegate.swift new file mode 100644 index 00000000..5705737e --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/TaskDelegate.swift @@ -0,0 +1,466 @@ +// +// TaskDelegate.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as +/// executing all operations attached to the serial operation queue upon task completion. +open class TaskDelegate: NSObject { + + // MARK: Properties + + /// The serial operation queue used to execute all operations after the task completes. + public let queue: OperationQueue + + /// The data returned by the server. + public var data: Data? { return nil } + + /// The error generated throughout the lifecyle of the task. + public var error: Error? + + var task: URLSessionTask? { + set { + taskLock.lock(); defer { taskLock.unlock() } + _task = newValue + } + get { + taskLock.lock(); defer { taskLock.unlock() } + return _task + } + } + + var initialResponseTime: CFAbsoluteTime? + var credential: URLCredential? + var metrics: AnyObject? // URLSessionTaskMetrics + + private var _task: URLSessionTask? { + didSet { reset() } + } + + private let taskLock = NSLock() + + // MARK: Lifecycle + + init(task: URLSessionTask?) { + _task = task + + self.queue = { + let operationQueue = OperationQueue() + + operationQueue.maxConcurrentOperationCount = 1 + operationQueue.isSuspended = true + operationQueue.qualityOfService = .utility + + return operationQueue + }() + } + + func reset() { + error = nil + initialResponseTime = nil + } + + // MARK: URLSessionTaskDelegate + + var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? + var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? + var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? + var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)? + + @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) + func urlSession( + _ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: @escaping (URLRequest?) -> Void) + { + var redirectRequest: URLRequest? = request + + if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { + redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) + } + + completionHandler(redirectRequest) + } + + @objc(URLSession:task:didReceiveChallenge:completionHandler:) + func urlSession( + _ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) + { + var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling + var credential: URLCredential? + + if let taskDidReceiveChallenge = taskDidReceiveChallenge { + (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) + } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { + let host = challenge.protectionSpace.host + + if + let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), + let serverTrust = challenge.protectionSpace.serverTrust + { + if serverTrustPolicy.evaluate(serverTrust, forHost: host) { + disposition = .useCredential + credential = URLCredential(trust: serverTrust) + } else { + disposition = .cancelAuthenticationChallenge + } + } + } else { + if challenge.previousFailureCount > 0 { + disposition = .rejectProtectionSpace + } else { + credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) + + if credential != nil { + disposition = .useCredential + } + } + } + + completionHandler(disposition, credential) + } + + @objc(URLSession:task:needNewBodyStream:) + func urlSession( + _ session: URLSession, + task: URLSessionTask, + needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) + { + var bodyStream: InputStream? + + if let taskNeedNewBodyStream = taskNeedNewBodyStream { + bodyStream = taskNeedNewBodyStream(session, task) + } + + completionHandler(bodyStream) + } + + @objc(URLSession:task:didCompleteWithError:) + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + if let taskDidCompleteWithError = taskDidCompleteWithError { + taskDidCompleteWithError(session, task, error) + } else { + if let error = error { + if self.error == nil { self.error = error } + + if + let downloadDelegate = self as? DownloadTaskDelegate, + let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data + { + downloadDelegate.resumeData = resumeData + } + } + + queue.isSuspended = false + } + } +} + +// MARK: - + +class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { + + // MARK: Properties + + var dataTask: URLSessionDataTask { return task as! URLSessionDataTask } + + override var data: Data? { + if dataStream != nil { + return nil + } else { + return mutableData + } + } + + var progress: Progress + var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? + + var dataStream: ((_ data: Data) -> Void)? + + private var totalBytesReceived: Int64 = 0 + private var mutableData: Data + + private var expectedContentLength: Int64? + + // MARK: Lifecycle + + override init(task: URLSessionTask?) { + mutableData = Data() + progress = Progress(totalUnitCount: 0) + + super.init(task: task) + } + + override func reset() { + super.reset() + + progress = Progress(totalUnitCount: 0) + totalBytesReceived = 0 + mutableData = Data() + expectedContentLength = nil + } + + // MARK: URLSessionDataDelegate + + var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? + var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? + var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? + var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didReceive response: URLResponse, + completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) + { + var disposition: URLSession.ResponseDisposition = .allow + + expectedContentLength = response.expectedContentLength + + if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { + disposition = dataTaskDidReceiveResponse(session, dataTask, response) + } + + completionHandler(disposition) + } + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didBecome downloadTask: URLSessionDownloadTask) + { + dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) + } + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } + + if let dataTaskDidReceiveData = dataTaskDidReceiveData { + dataTaskDidReceiveData(session, dataTask, data) + } else { + if let dataStream = dataStream { + dataStream(data) + } else { + mutableData.append(data) + } + + let bytesReceived = Int64(data.count) + totalBytesReceived += bytesReceived + let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown + + progress.totalUnitCount = totalBytesExpected + progress.completedUnitCount = totalBytesReceived + + if let progressHandler = progressHandler { + progressHandler.queue.async { progressHandler.closure(self.progress) } + } + } + } + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse, + completionHandler: @escaping (CachedURLResponse?) -> Void) + { + var cachedResponse: CachedURLResponse? = proposedResponse + + if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { + cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) + } + + completionHandler(cachedResponse) + } +} + +// MARK: - + +class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { + + // MARK: Properties + + var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask } + + var progress: Progress + var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? + + var resumeData: Data? + override var data: Data? { return resumeData } + + var destination: DownloadRequest.DownloadFileDestination? + + var temporaryURL: URL? + var destinationURL: URL? + + var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL } + + // MARK: Lifecycle + + override init(task: URLSessionTask?) { + progress = Progress(totalUnitCount: 0) + super.init(task: task) + } + + override func reset() { + super.reset() + + progress = Progress(totalUnitCount: 0) + resumeData = nil + } + + // MARK: URLSessionDownloadDelegate + + var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)? + var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? + var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) + { + temporaryURL = location + + guard + let destination = destination, + let response = downloadTask.response as? HTTPURLResponse + else { return } + + let result = destination(location, response) + let destinationURL = result.destinationURL + let options = result.options + + self.destinationURL = destinationURL + + do { + if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) { + try FileManager.default.removeItem(at: destinationURL) + } + + if options.contains(.createIntermediateDirectories) { + let directory = destinationURL.deletingLastPathComponent() + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + } + + try FileManager.default.moveItem(at: location, to: destinationURL) + } catch { + self.error = error + } + } + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) + { + if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } + + if let downloadTaskDidWriteData = downloadTaskDidWriteData { + downloadTaskDidWriteData( + session, + downloadTask, + bytesWritten, + totalBytesWritten, + totalBytesExpectedToWrite + ) + } else { + progress.totalUnitCount = totalBytesExpectedToWrite + progress.completedUnitCount = totalBytesWritten + + if let progressHandler = progressHandler { + progressHandler.queue.async { progressHandler.closure(self.progress) } + } + } + } + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) + { + if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { + downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) + } else { + progress.totalUnitCount = expectedTotalBytes + progress.completedUnitCount = fileOffset + } + } +} + +// MARK: - + +class UploadTaskDelegate: DataTaskDelegate { + + // MARK: Properties + + var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask } + + var uploadProgress: Progress + var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? + + // MARK: Lifecycle + + override init(task: URLSessionTask?) { + uploadProgress = Progress(totalUnitCount: 0) + super.init(task: task) + } + + override func reset() { + super.reset() + uploadProgress = Progress(totalUnitCount: 0) + } + + // MARK: URLSessionTaskDelegate + + var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? + + func URLSession( + _ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) + { + if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } + + if let taskDidSendBodyData = taskDidSendBodyData { + taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) + } else { + uploadProgress.totalUnitCount = totalBytesExpectedToSend + uploadProgress.completedUnitCount = totalBytesSent + + if let uploadProgressHandler = uploadProgressHandler { + uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) } + } + } + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/Timeline.swift b/Santander-Test/Pods/Alamofire/Source/Timeline.swift new file mode 100644 index 00000000..596c1bdc --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Timeline.swift @@ -0,0 +1,136 @@ +// +// Timeline.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. +public struct Timeline { + /// The time the request was initialized. + public let requestStartTime: CFAbsoluteTime + + /// The time the first bytes were received from or sent to the server. + public let initialResponseTime: CFAbsoluteTime + + /// The time when the request was completed. + public let requestCompletedTime: CFAbsoluteTime + + /// The time when the response serialization was completed. + public let serializationCompletedTime: CFAbsoluteTime + + /// The time interval in seconds from the time the request started to the initial response from the server. + public let latency: TimeInterval + + /// The time interval in seconds from the time the request started to the time the request completed. + public let requestDuration: TimeInterval + + /// The time interval in seconds from the time the request completed to the time response serialization completed. + public let serializationDuration: TimeInterval + + /// The time interval in seconds from the time the request started to the time response serialization completed. + public let totalDuration: TimeInterval + + /// Creates a new `Timeline` instance with the specified request times. + /// + /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. + /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server. + /// Defaults to `0.0`. + /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. + /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults + /// to `0.0`. + /// + /// - returns: The new `Timeline` instance. + public init( + requestStartTime: CFAbsoluteTime = 0.0, + initialResponseTime: CFAbsoluteTime = 0.0, + requestCompletedTime: CFAbsoluteTime = 0.0, + serializationCompletedTime: CFAbsoluteTime = 0.0) + { + self.requestStartTime = requestStartTime + self.initialResponseTime = initialResponseTime + self.requestCompletedTime = requestCompletedTime + self.serializationCompletedTime = serializationCompletedTime + + self.latency = initialResponseTime - requestStartTime + self.requestDuration = requestCompletedTime - requestStartTime + self.serializationDuration = serializationCompletedTime - requestCompletedTime + self.totalDuration = serializationCompletedTime - requestStartTime + } +} + +// MARK: - CustomStringConvertible + +extension Timeline: CustomStringConvertible { + /// The textual representation used when written to an output stream, which includes the latency, the request + /// duration and the total duration. + public var description: String { + let latency = String(format: "%.3f", self.latency) + let requestDuration = String(format: "%.3f", self.requestDuration) + let serializationDuration = String(format: "%.3f", self.serializationDuration) + let totalDuration = String(format: "%.3f", self.totalDuration) + + // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is + // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. + let timings = [ + "\"Latency\": " + latency + " secs", + "\"Request Duration\": " + requestDuration + " secs", + "\"Serialization Duration\": " + serializationDuration + " secs", + "\"Total Duration\": " + totalDuration + " secs" + ] + + return "Timeline: { " + timings.joined(separator: ", ") + " }" + } +} + +// MARK: - CustomDebugStringConvertible + +extension Timeline: CustomDebugStringConvertible { + /// The textual representation used when written to an output stream, which includes the request start time, the + /// initial response time, the request completed time, the serialization completed time, the latency, the request + /// duration and the total duration. + public var debugDescription: String { + let requestStartTime = String(format: "%.3f", self.requestStartTime) + let initialResponseTime = String(format: "%.3f", self.initialResponseTime) + let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime) + let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime) + let latency = String(format: "%.3f", self.latency) + let requestDuration = String(format: "%.3f", self.requestDuration) + let serializationDuration = String(format: "%.3f", self.serializationDuration) + let totalDuration = String(format: "%.3f", self.totalDuration) + + // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is + // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. + let timings = [ + "\"Request Start Time\": " + requestStartTime, + "\"Initial Response Time\": " + initialResponseTime, + "\"Request Completed Time\": " + requestCompletedTime, + "\"Serialization Completed Time\": " + serializationCompletedTime, + "\"Latency\": " + latency + " secs", + "\"Request Duration\": " + requestDuration + " secs", + "\"Serialization Duration\": " + serializationDuration + " secs", + "\"Total Duration\": " + totalDuration + " secs" + ] + + return "Timeline: { " + timings.joined(separator: ", ") + " }" + } +} diff --git a/Santander-Test/Pods/Alamofire/Source/Validation.swift b/Santander-Test/Pods/Alamofire/Source/Validation.swift new file mode 100644 index 00000000..5640789d --- /dev/null +++ b/Santander-Test/Pods/Alamofire/Source/Validation.swift @@ -0,0 +1,315 @@ +// +// Validation.swift +// +// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +extension Request { + + // MARK: Helper Types + + fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason + + /// Used to represent whether validation was successful or encountered an error resulting in a failure. + /// + /// - success: The validation was successful. + /// - failure: The validation failed encountering the provided error. + public enum ValidationResult { + case success + case failure(Error) + } + + fileprivate struct MIMEType { + let type: String + let subtype: String + + var isWildcard: Bool { return type == "*" && subtype == "*" } + + init?(_ string: String) { + let components: [String] = { + let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) + + #if swift(>=3.2) + let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] + #else + let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) + #endif + + return split.components(separatedBy: "/") + }() + + if let type = components.first, let subtype = components.last { + self.type = type + self.subtype = subtype + } else { + return nil + } + } + + func matches(_ mime: MIMEType) -> Bool { + switch (type, subtype) { + case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): + return true + default: + return false + } + } + } + + // MARK: Properties + + fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) } + + fileprivate var acceptableContentTypes: [String] { + if let accept = request?.value(forHTTPHeaderField: "Accept") { + return accept.components(separatedBy: ",") + } + + return ["*/*"] + } + + // MARK: Status Code + + fileprivate func validate( + statusCode acceptableStatusCodes: S, + response: HTTPURLResponse) + -> ValidationResult + where S.Iterator.Element == Int + { + if acceptableStatusCodes.contains(response.statusCode) { + return .success + } else { + let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) + return .failure(AFError.responseValidationFailed(reason: reason)) + } + } + + // MARK: Content Type + + fileprivate func validate( + contentType acceptableContentTypes: S, + response: HTTPURLResponse, + data: Data?) + -> ValidationResult + where S.Iterator.Element == String + { + guard let data = data, data.count > 0 else { return .success } + + guard + let responseContentType = response.mimeType, + let responseMIMEType = MIMEType(responseContentType) + else { + for contentType in acceptableContentTypes { + if let mimeType = MIMEType(contentType), mimeType.isWildcard { + return .success + } + } + + let error: AFError = { + let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) + return AFError.responseValidationFailed(reason: reason) + }() + + return .failure(error) + } + + for contentType in acceptableContentTypes { + if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { + return .success + } + } + + let error: AFError = { + let reason: ErrorReason = .unacceptableContentType( + acceptableContentTypes: Array(acceptableContentTypes), + responseContentType: responseContentType + ) + + return AFError.responseValidationFailed(reason: reason) + }() + + return .failure(error) + } +} + +// MARK: - + +extension DataRequest { + /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the + /// request was valid. + public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult + + /// Validates the request, using the specified closure. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter validation: A closure to validate the request. + /// + /// - returns: The request. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validationExecution: () -> Void = { [unowned self] in + if + let response = self.response, + self.delegate.error == nil, + case let .failure(error) = validation(self.request, response, self.delegate.data) + { + self.delegate.error = error + } + } + + validations.append(validationExecution) + + return self + } + + /// Validates that the response has a status code in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter range: The range of acceptable status codes. + /// + /// - returns: The request. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + return validate { [unowned self] _, response, _ in + return self.validate(statusCode: acceptableStatusCodes, response: response) + } + } + + /// Validates that the response has a content type in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. + /// + /// - returns: The request. + @discardableResult + public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { + return validate { [unowned self] _, response, data in + return self.validate(contentType: acceptableContentTypes, response: response, data: data) + } + } + + /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content + /// type matches any specified in the Accept HTTP header field. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - returns: The request. + @discardableResult + public func validate() -> Self { + return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) + } +} + +// MARK: - + +extension DownloadRequest { + /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a + /// destination URL, and returns whether the request was valid. + public typealias Validation = ( + _ request: URLRequest?, + _ response: HTTPURLResponse, + _ temporaryURL: URL?, + _ destinationURL: URL?) + -> ValidationResult + + /// Validates the request, using the specified closure. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter validation: A closure to validate the request. + /// + /// - returns: The request. + @discardableResult + public func validate(_ validation: @escaping Validation) -> Self { + let validationExecution: () -> Void = { [unowned self] in + let request = self.request + let temporaryURL = self.downloadDelegate.temporaryURL + let destinationURL = self.downloadDelegate.destinationURL + + if + let response = self.response, + self.delegate.error == nil, + case let .failure(error) = validation(request, response, temporaryURL, destinationURL) + { + self.delegate.error = error + } + } + + validations.append(validationExecution) + + return self + } + + /// Validates that the response has a status code in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter range: The range of acceptable status codes. + /// + /// - returns: The request. + @discardableResult + public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { + return validate { [unowned self] _, response, _, _ in + return self.validate(statusCode: acceptableStatusCodes, response: response) + } + } + + /// Validates that the response has a content type in the specified sequence. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. + /// + /// - returns: The request. + @discardableResult + public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { + return validate { [unowned self] _, response, _, _ in + let fileURL = self.downloadDelegate.fileURL + + guard let validFileURL = fileURL else { + return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) + } + + do { + let data = try Data(contentsOf: validFileURL) + return self.validate(contentType: acceptableContentTypes, response: response, data: data) + } catch { + return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) + } + } + } + + /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content + /// type matches any specified in the Accept HTTP header field. + /// + /// If validation fails, subsequent calls to response handlers will have an associated error. + /// + /// - returns: The request. + @discardableResult + public func validate() -> Self { + return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift new file mode 100644 index 00000000..8f7e8ab3 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift @@ -0,0 +1,73 @@ +// +// IQNSArray+Sort.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import UIKit + +/** +UIView.subviews sorting category. +*/ +internal extension Array { + + ///-------------- + /// MARK: Sorting + ///-------------- + + /** + Returns the array by sorting the UIView's by their tag property. + */ + internal func sortedArrayByTag() -> [Element] { + + return sorted(by: { (obj1 : Element, obj2 : Element) -> Bool in + + let view1 = obj1 as! UIView + let view2 = obj2 as! UIView + + return (view1.tag < view2.tag) + }) + } + + /** + Returns the array by sorting the UIView's by their tag property. + */ + internal func sortedArrayByPosition() -> [Element] { + + return sorted(by: { (obj1 : Element, obj2 : Element) -> Bool in + + let view1 = obj1 as! UIView + let view2 = obj2 as! UIView + + let x1 = view1.frame.minX + let y1 = view1.frame.minY + let x2 = view2.frame.minX + let y2 = view2.frame.minY + + if y1 != y2 { + return y1 < y2 + } else { + return x1 < x2 + } + }) + } +} + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift new file mode 100644 index 00000000..236221b4 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift @@ -0,0 +1,65 @@ +// +// IQUIScrollView+Additions.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import UIKit + +private var kIQShouldIgnoreScrollingAdjustment = "kIQShouldIgnoreScrollingAdjustment" +private var kIQShouldRestoreScrollViewContentOffset = "kIQShouldRestoreScrollViewContentOffset" + +public extension UIScrollView { + + /** + If YES, then scrollview will ignore scrolling (simply not scroll it) for adjusting textfield position. Default is NO. + */ + @objc public var shouldIgnoreScrollingAdjustment: Bool { + get { + + if let aValue = objc_getAssociatedObject(self, &kIQShouldIgnoreScrollingAdjustment) as? Bool { + return aValue + } else { + return false + } + } + set(newValue) { + objc_setAssociatedObject(self, &kIQShouldIgnoreScrollingAdjustment, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + /** + To set customized distance from keyboard for textField/textView. Can't be less than zero + */ + @objc public var shouldRestoreScrollViewContentOffset: Bool { + get { + + if let aValue = objc_getAssociatedObject(self, &kIQShouldRestoreScrollViewContentOffset) as? Bool { + return aValue + } else { + return false + } + } + set(newValue) { + objc_setAssociatedObject(self, &kIQShouldRestoreScrollViewContentOffset, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift new file mode 100644 index 00000000..91352711 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift @@ -0,0 +1,110 @@ +// +// IQUITextFieldView+Additions.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import UIKit + +/** +Uses default keyboard distance for textField. +*/ +public let kIQUseDefaultKeyboardDistance = CGFloat.greatestFiniteMagnitude + +private var kIQKeyboardDistanceFromTextField = "kIQKeyboardDistanceFromTextField" +//private var kIQKeyboardEnableMode = "kIQKeyboardEnableMode" +private var kIQKeyboardShouldResignOnTouchOutsideMode = "kIQKeyboardShouldResignOnTouchOutsideMode" +private var kIQIgnoreSwitchingByNextPrevious = "kIQIgnoreSwitchingByNextPrevious" + +/** +UIView category for managing UITextField/UITextView +*/ +public extension UIView { + + /** + To set customized distance from keyboard for textField/textView. Can't be less than zero + */ + @objc public var keyboardDistanceFromTextField: CGFloat { + get { + + if let aValue = objc_getAssociatedObject(self, &kIQKeyboardDistanceFromTextField) as? CGFloat { + return aValue + } else { + return kIQUseDefaultKeyboardDistance + } + } + set(newValue) { + objc_setAssociatedObject(self, &kIQKeyboardDistanceFromTextField, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + /** + If shouldIgnoreSwitchingByNextPrevious is true then library will ignore this textField/textView while moving to other textField/textView using keyboard toolbar next previous buttons. Default is false + */ + @objc public var ignoreSwitchingByNextPrevious: Bool { + get { + + if let aValue = objc_getAssociatedObject(self, &kIQIgnoreSwitchingByNextPrevious) as? Bool { + return aValue + } else { + return false + } + } + set(newValue) { + objc_setAssociatedObject(self, &kIQIgnoreSwitchingByNextPrevious, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + +// /** +// Override Enable/disable managing distance between keyboard and textField behaviour for this particular textField. +// */ +// @objc public var enableMode: IQEnableMode { +// get { +// +// if let savedMode = objc_getAssociatedObject(self, &kIQKeyboardEnableMode) as? IQEnableMode { +// return savedMode +// } else { +// return .Default +// } +// } +// set(newValue) { +// objc_setAssociatedObject(self, &kIQKeyboardEnableMode, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) +// } +// } + + /** + Override resigns Keyboard on touching outside of UITextField/View behaviour for this particular textField. + */ + @objc public var shouldResignOnTouchOutsideMode: IQEnableMode { + get { + + if let savedMode = objc_getAssociatedObject(self, &kIQKeyboardShouldResignOnTouchOutsideMode) as? IQEnableMode { + return savedMode + } else { + return .Default + } + } + set(newValue) { + objc_setAssociatedObject(self, &kIQKeyboardShouldResignOnTouchOutsideMode, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift new file mode 100644 index 00000000..fa79685d --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift @@ -0,0 +1,340 @@ +// +// IQUIView+Hierarchy.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import UIKit + +/** +UIView hierarchy category. +*/ +public extension UIView { + + ///---------------------- + /// MARK: viewControllers + ///---------------------- + + /** + Returns the UIViewController object that manages the receiver. + */ + @objc public func viewContainingController()->UIViewController? { + + var nextResponder: UIResponder? = self + + repeat { + nextResponder = nextResponder?.next + + if let viewController = nextResponder as? UIViewController { + return viewController + } + + } while nextResponder != nil + + return nil + } + + /** + Returns the topMost UIViewController object in hierarchy. + */ + @objc public func topMostController()->UIViewController? { + + var controllersHierarchy = [UIViewController]() + + if var topController = window?.rootViewController { + controllersHierarchy.append(topController) + + while let presented = topController.presentedViewController { + + topController = presented + + controllersHierarchy.append(presented) + } + + var matchController :UIResponder? = viewContainingController() + + while matchController != nil && controllersHierarchy.contains(matchController as! UIViewController) == false { + + repeat { + matchController = matchController?.next + + } while matchController != nil && matchController is UIViewController == false + } + + return matchController as? UIViewController + + } else { + return viewContainingController() + } + } + + /** + Returns the UIViewController object that is actually the parent of this object. Most of the time it's the viewController object which actually contains it, but result may be different if it's viewController is added as childViewController of another viewController. + */ + @objc public func parentContainerViewController()->UIViewController? { + + var matchController = viewContainingController() + var parentContainerViewController : UIViewController? + + if var navController = matchController?.navigationController { + + while let parentNav = navController.navigationController { + navController = parentNav + } + + var parentController : UIViewController = navController + + while let parent = parentController.parent, + (parent.isKind(of: UINavigationController.self) == false && + parent.isKind(of: UITabBarController.self) == false && + parent.isKind(of: UISplitViewController.self) == false) { + + parentController = parent + } + + if navController == parentController { + parentContainerViewController = navController.topViewController + } else { + parentContainerViewController = parentController + } + } + else if let tabController = matchController?.tabBarController { + + if let navController = tabController.selectedViewController as? UINavigationController { + parentContainerViewController = navController.topViewController + } else { + parentContainerViewController = tabController.selectedViewController + } + } else { + while let parentController = matchController?.parent, + (parentController.isKind(of: UINavigationController.self) == false && + parentController.isKind(of: UITabBarController.self) == false && + parentController.isKind(of: UISplitViewController.self) == false) { + + matchController = parentController + } + + parentContainerViewController = matchController + } + + let finalController = parentContainerViewController?.parentIQContainerViewController() ?? parentContainerViewController + + return finalController + + } + + ///----------------------------------- + /// MARK: Superviews/Subviews/Siglings + ///----------------------------------- + + /** + Returns the superView of provided class type. + */ + @objc public func superviewOfClassType(_ classType:UIView.Type)->UIView? { + + var superView = superview + + while let unwrappedSuperView = superView { + + if unwrappedSuperView.isKind(of: classType) { + + //If it's UIScrollView, then validating for special cases + if unwrappedSuperView.isKind(of: UIScrollView.self) { + + let classNameString = NSStringFromClass(type(of:unwrappedSuperView.self)) + + // If it's not UITableViewWrapperView class, this is internal class which is actually manage in UITableview. The speciality of this class is that it's superview is UITableView. + // If it's not UITableViewCellScrollView class, this is internal class which is actually manage in UITableviewCell. The speciality of this class is that it's superview is UITableViewCell. + //If it's not _UIQueuingScrollView class, actually we validate for _ prefix which usually used by Apple internal classes + if unwrappedSuperView.superview?.isKind(of: UITableView.self) == false && + unwrappedSuperView.superview?.isKind(of: UITableViewCell.self) == false && + classNameString.hasPrefix("_") == false { + return superView + } + } + else { + return superView + } + } + + superView = unwrappedSuperView.superview + } + + return nil + } + + /** + Returns all siblings of the receiver which canBecomeFirstResponder. + */ + internal func responderSiblings()->[UIView] { + + //Array of (UITextField/UITextView's). + var tempTextFields = [UIView]() + + // Getting all siblings + if let siblings = superview?.subviews { + + for textField in siblings { + + if (textField == self || textField.ignoreSwitchingByNextPrevious == false) && textField._IQcanBecomeFirstResponder() == true { + tempTextFields.append(textField) + } + } + } + + return tempTextFields + } + + /** + Returns all deep subViews of the receiver which canBecomeFirstResponder. + */ + internal func deepResponderViews()->[UIView] { + + //Array of (UITextField/UITextView's). + var textfields = [UIView]() + + for textField in subviews { + + if (textField == self || textField.ignoreSwitchingByNextPrevious == false) && textField._IQcanBecomeFirstResponder() == true { + textfields.append(textField) + } + + //Sometimes there are hidden or disabled views and textField inside them still recorded, so we added some more validations here (Bug ID: #458) + //Uncommented else (Bug ID: #625) + if textField.subviews.count != 0 && isUserInteractionEnabled == true && isHidden == false && alpha != 0.0 { + for deepView in textField.deepResponderViews() { + textfields.append(deepView) + } + } + } + + //subviews are returning in opposite order. Sorting according the frames 'y'. + return textfields.sorted(by: { (view1 : UIView, view2 : UIView) -> Bool in + + let frame1 = view1.convert(view1.bounds, to: self) + let frame2 = view2.convert(view2.bounds, to: self) + + let x1 = frame1.minX + let y1 = frame1.minY + let x2 = frame2.minX + let y2 = frame2.minY + + if y1 != y2 { + return y1 < y2 + } else { + return x1 < x2 + } + }) + } + + private func _IQcanBecomeFirstResponder() -> Bool { + + var _IQcanBecomeFirstResponder = false + + // Setting toolbar to keyboard. + if let textField = self as? UITextField { + _IQcanBecomeFirstResponder = textField.isEnabled + } else if let textView = self as? UITextView { + _IQcanBecomeFirstResponder = textView.isEditable + } + + if _IQcanBecomeFirstResponder == true { + _IQcanBecomeFirstResponder = isUserInteractionEnabled == true && isHidden == false && alpha != 0.0 && isAlertViewTextField() == false && textFieldSearchBar() == nil + } + + return _IQcanBecomeFirstResponder + } + + ///------------------------- + /// MARK: Special TextFields + ///------------------------- + + /** + Returns searchBar if receiver object is UISearchBarTextField, otherwise return nil. + */ + internal func textFieldSearchBar()-> UISearchBar? { + + var responder : UIResponder? = self.next + + while let bar = responder { + + if let searchBar = bar as? UISearchBar { + return searchBar + } else if bar is UIViewController { + break + } + + responder = bar.next + } + + return nil + } + + /** + Returns YES if the receiver object is UIAlertSheetTextField, otherwise return NO. + */ + internal func isAlertViewTextField()->Bool { + + var alertViewController : UIResponder? = viewContainingController() + + var isAlertViewTextField = false + + while let controller = alertViewController, isAlertViewTextField == false { + + if controller.isKind(of: UIAlertController.self) { + isAlertViewTextField = true + break + } + + alertViewController = controller.next + } + + return isAlertViewTextField + } + + private func depth()->Int { + var depth : Int = 0 + + if let superView = superview { + depth = superView.depth()+1 + } + + return depth + } + +} + +public extension UIViewController { + + func parentIQContainerViewController() -> UIViewController? { + return self + } +} + +extension NSObject { + + internal func _IQDescription() -> String { + return "<\(self) \(Unmanaged.passUnretained(self).toOpaque())>" + } +} + + + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift new file mode 100644 index 00000000..ab30451f --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift @@ -0,0 +1,48 @@ +// +// IQUIViewController+Additions.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + + +private var kIQLayoutGuideConstraint = "kIQLayoutGuideConstraint" + + +public extension UIViewController { + + /** + To set customized distance from keyboard for textField/textView. Can't be less than zero + + @deprecated Due to change in core-logic of handling distance between textField and keyboard distance, this layout contraint tweak is no longer needed and things will just work out of the box regardless of constraint pinned with safeArea/layoutGuide/superview + */ + @available(*,deprecated, message: "Due to change in core-logic of handling distance between textField and keyboard distance, this layout contraint tweak is no longer needed and things will just work out of the box regardless of constraint pinned with safeArea/layoutGuide/superview.") + @IBOutlet @objc public var IQLayoutGuideConstraint: NSLayoutConstraint? { + get { + + return objc_getAssociatedObject(self, &kIQLayoutGuideConstraint) as? NSLayoutConstraint + } + + set(newValue) { + objc_setAssociatedObject(self, &kIQLayoutGuideConstraint, newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift new file mode 100644 index 00000000..0cc146a5 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift @@ -0,0 +1,152 @@ +// +// IQKeyboardManagerConstants.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import Foundation + +///----------------------------------- +/// MARK: IQAutoToolbarManageBehaviour +///----------------------------------- + +/** +`IQAutoToolbarBySubviews` +Creates Toolbar according to subview's hirarchy of Textfield's in view. + +`IQAutoToolbarByTag` +Creates Toolbar according to tag property of TextField's. + +`IQAutoToolbarByPosition` +Creates Toolbar according to the y,x position of textField in it's superview coordinate. +*/ +@objc public enum IQAutoToolbarManageBehaviour : Int { + case bySubviews + case byTag + case byPosition +} + +/** + `IQPreviousNextDisplayModeDefault` + Show NextPrevious when there are more than 1 textField otherwise hide. + + `IQPreviousNextDisplayModeAlwaysHide` + Do not show NextPrevious buttons in any case. + + `IQPreviousNextDisplayModeAlwaysShow` + Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled. + */ +@objc public enum IQPreviousNextDisplayMode : Int { + case Default + case alwaysHide + case alwaysShow +} + +/** + `IQEnableModeDefault` + Pick default settings. + + `IQEnableModeEnabled` + setting is enabled. + + `IQEnableModeDisabled` + setting is disabled. + */ +@objc public enum IQEnableMode : Int { + case Default + case enabled + case disabled +} + +/* + + /---------------------------------------------------------------------------------------------------\ + \---------------------------------------------------------------------------------------------------/ + | iOS Notification Mechanism | + /---------------------------------------------------------------------------------------------------\ + \---------------------------------------------------------------------------------------------------/ + + + ------------------------------------------------------------ + When UITextField become first responder + ------------------------------------------------------------ + - UITextFieldTextDidBeginEditingNotification (UITextField) + - UIKeyboardWillShowNotification + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + When UITextView become first responder + ------------------------------------------------------------ + - UIKeyboardWillShowNotification + - UITextViewTextDidBeginEditingNotification (UITextView) + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + When switching focus from UITextField to another UITextField + ------------------------------------------------------------ + - UITextFieldTextDidEndEditingNotification (UITextField1) + - UITextFieldTextDidBeginEditingNotification (UITextField2) + - UIKeyboardWillShowNotification + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + When switching focus from UITextView to another UITextView + ------------------------------------------------------------ + - UITextViewTextDidEndEditingNotification : (UITextView1) + - UIKeyboardWillShowNotification + - UITextViewTextDidBeginEditingNotification : (UITextView2) + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + When switching focus from UITextField to UITextView + ------------------------------------------------------------ + - UITextFieldTextDidEndEditingNotification (UITextField) + - UIKeyboardWillShowNotification + - UITextViewTextDidBeginEditingNotification (UITextView) + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + When switching focus from UITextView to UITextField + ------------------------------------------------------------ + - UITextViewTextDidEndEditingNotification (UITextView) + - UITextFieldTextDidBeginEditingNotification (UITextField) + - UIKeyboardWillShowNotification + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + When opening/closing UIKeyboard Predictive bar + ------------------------------------------------------------ + - UIKeyboardWillShowNotification + - UIKeyboardDidShowNotification + + ------------------------------------------------------------ + On orientation change + ------------------------------------------------------------ + - UIApplicationWillChangeStatusBarOrientationNotification + - UIKeyboardWillHideNotification + - UIKeyboardDidHideNotification + - UIApplicationDidChangeStatusBarOrientationNotification + - UIKeyboardWillShowNotification + - UIKeyboardDidShowNotification + - UIKeyboardWillShowNotification + - UIKeyboardDidShowNotification + + */ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift new file mode 100644 index 00000000..41b69c17 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift @@ -0,0 +1,27 @@ +// +// IQKeyboardManagerConstantsInternal.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import Foundation + + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift new file mode 100644 index 00000000..6475e75b --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift @@ -0,0 +1,2076 @@ +// +// IQKeyboardManager.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import Foundation +import CoreGraphics +import UIKit +import QuartzCore + +///--------------------- +/// MARK: IQToolbar tags +///--------------------- + +/** +Codeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more. A generic version of KeyboardManagement. https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html +*/ + +public class IQKeyboardManager: NSObject, UIGestureRecognizerDelegate { + + /** + Default tag for toolbar with Done button -1002. + */ + private static let kIQDoneButtonToolbarTag = -1002 + + /** + Default tag for toolbar with Previous/Next buttons -1005. + */ + private static let kIQPreviousNextButtonToolbarTag = -1005 + + /** + Invalid point value. + */ + private static let kIQCGPointInvalid = CGPoint.init(x: CGFloat.greatestFiniteMagnitude, y: CGFloat.greatestFiniteMagnitude) + + ///--------------------------- + /// MARK: UIKeyboard handling + ///--------------------------- + + /** + Registered classes list with library. + */ + private var registeredClasses = [UIView.Type]() + + /** + Enable/disable managing distance between keyboard and textField. Default is YES(Enabled when class loads in `+(void)load` method). + */ + @objc public var enable = false { + + didSet { + //If not enable, enable it. + if enable == true && + oldValue == false { + //If keyboard is currently showing. Sending a fake notification for keyboardWillHide to retain view's original position. + if let notification = _kbShowNotification { + keyboardWillShow(notification) + } + showLog("Enabled") + } else if enable == false && + oldValue == true { //If not disable, desable it. + keyboardWillHide(nil) + showLog("Disabled") + } + } + } + + private func privateIsEnabled()-> Bool { + + var isEnabled = enable + +// let enableMode = _textFieldView?.enableMode +// +// if enableMode == .enabled { +// isEnabled = true +// } else if enableMode == .disabled { +// isEnabled = false +// } else { + + if let textFieldViewController = _textFieldView?.viewContainingController() { + + if isEnabled == false { + + //If viewController is kind of enable viewController class, then assuming it's enabled. + for enabledClass in enabledDistanceHandlingClasses { + + if textFieldViewController.isKind(of: enabledClass) { + isEnabled = true + break + } + } + } + + if isEnabled == true { + + //If viewController is kind of disabled viewController class, then assuming it's disabled. + for disabledClass in disabledDistanceHandlingClasses { + + if textFieldViewController.isKind(of: disabledClass) { + isEnabled = false + break + } + } + + //Special Controllers + if isEnabled == true { + + let classNameString = NSStringFromClass(type(of:textFieldViewController.self)) + + //_UIAlertControllerTextFieldViewController + if (classNameString.contains("UIAlertController") && classNameString.hasSuffix("TextFieldViewController")) { + isEnabled = false + } + } + } + } +// } + + return isEnabled + } + + /** + To set keyboard distance from textField. can't be less than zero. Default is 10.0. + */ + @objc public var keyboardDistanceFromTextField: CGFloat { + + set { + _privateKeyboardDistanceFromTextField = max(0, newValue) + showLog("keyboardDistanceFromTextField: \(_privateKeyboardDistanceFromTextField)") + } + get { + return _privateKeyboardDistanceFromTextField + } + } + + /** + Boolean to know if keyboard is showing. + */ + @objc public var keyboardShowing: Bool { + + return _privateIsKeyboardShowing + } + + /** + moved distance to the top used to maintain distance between keyboard and textField. Most of the time this will be a positive value. + */ + @objc public var movedDistance: CGFloat { + + return _privateMovedDistance + } + + /** + Returns the default singleton instance. + */ + @objc public class var shared: IQKeyboardManager { + struct Static { + //Singleton instance. Initializing keyboard manger. + static let kbManager = IQKeyboardManager() + } + + /** @return Returns the default singleton instance. */ + return Static.kbManager + } + + ///------------------------- + /// MARK: IQToolbar handling + ///------------------------- + + /** + Automatic add the IQToolbar functionality. Default is YES. + */ + @objc public var enableAutoToolbar = true { + + didSet { + + privateIsEnableAutoToolbar() ? addToolbarIfRequired() : removeToolbarIfRequired() + + let enableToolbar = enableAutoToolbar ? "Yes" : "NO" + + showLog("enableAutoToolbar: \(enableToolbar)") + } + } + + private func privateIsEnableAutoToolbar() -> Bool { + + var enableToolbar = enableAutoToolbar + + if let textFieldViewController = _textFieldView?.viewContainingController() { + + if enableToolbar == false { + + //If found any toolbar enabled classes then return. + for enabledClass in enabledToolbarClasses { + + if textFieldViewController.isKind(of: enabledClass) { + enableToolbar = true + break + } + } + } + + if enableToolbar == true { + + //If found any toolbar disabled classes then return. + for disabledClass in disabledToolbarClasses { + + if textFieldViewController.isKind(of: disabledClass) { + enableToolbar = false + break + } + } + + //Special Controllers + if enableToolbar == true { + + let classNameString = NSStringFromClass(type(of:textFieldViewController.self)) + + //_UIAlertControllerTextFieldViewController + if (classNameString.contains("UIAlertController") && classNameString.hasSuffix("TextFieldViewController")) { + enableToolbar = false + } + } + } + } + + return enableToolbar + } + + /** + /** + IQAutoToolbarBySubviews: Creates Toolbar according to subview's hirarchy of Textfield's in view. + IQAutoToolbarByTag: Creates Toolbar according to tag property of TextField's. + IQAutoToolbarByPosition: Creates Toolbar according to the y,x position of textField in it's superview coordinate. + + Default is IQAutoToolbarBySubviews. + */ + AutoToolbar managing behaviour. Default is IQAutoToolbarBySubviews. + */ + @objc public var toolbarManageBehaviour = IQAutoToolbarManageBehaviour.bySubviews + + /** + If YES, then uses textField's tintColor property for IQToolbar, otherwise tint color is black. Default is NO. + */ + @objc public var shouldToolbarUsesTextFieldTintColor = false + + /** + This is used for toolbar.tintColor when textfield.keyboardAppearance is UIKeyboardAppearanceDefault. If shouldToolbarUsesTextFieldTintColor is YES then this property is ignored. Default is nil and uses black color. + */ + @objc public var toolbarTintColor : UIColor? + + /** + This is used for toolbar.barTintColor. Default is nil and uses white color. + */ + @objc public var toolbarBarTintColor : UIColor? + + /** + IQPreviousNextDisplayModeDefault: Show NextPrevious when there are more than 1 textField otherwise hide. + IQPreviousNextDisplayModeAlwaysHide: Do not show NextPrevious buttons in any case. + IQPreviousNextDisplayModeAlwaysShow: Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled. + */ + @objc public var previousNextDisplayMode = IQPreviousNextDisplayMode.Default + + /** + Toolbar previous/next/done button icon, If nothing is provided then check toolbarDoneBarButtonItemText to draw done button. + */ + @objc public var toolbarPreviousBarButtonItemImage : UIImage? + @objc public var toolbarNextBarButtonItemImage : UIImage? + @objc public var toolbarDoneBarButtonItemImage : UIImage? + + /** + Toolbar previous/next/done button text, If nothing is provided then system default 'UIBarButtonSystemItemDone' will be used. + */ + @objc public var toolbarPreviousBarButtonItemText : String? + @objc public var toolbarNextBarButtonItemText : String? + @objc public var toolbarDoneBarButtonItemText : String? + + /** + If YES, then it add the textField's placeholder text on IQToolbar. Default is YES. + */ + @objc public var shouldShowToolbarPlaceholder = true + + /** + Placeholder Font. Default is nil. + */ + @objc public var placeholderFont: UIFont? + + /** + Placeholder Color. Default is nil. Which means lightGray + */ + @objc public var placeholderColor: UIColor? + + /** + Placeholder Button Color when it's treated as button. Default is nil. Which means iOS Blue for light toolbar and Yellow for dark toolbar + */ + @objc public var placeholderButtonColor: UIColor? + + + ///-------------------------- + /// MARK: UITextView handling + ///-------------------------- + + /** used to adjust contentInset of UITextView. */ + private var startingTextViewContentInsets = UIEdgeInsets() + + /** used to adjust scrollIndicatorInsets of UITextView. */ + private var startingTextViewScrollIndicatorInsets = UIEdgeInsets() + + /** used with textView to detect a textFieldView contentInset is changed or not. (Bug ID: #92)*/ + private var isTextViewContentInsetChanged = false + + + ///--------------------------------------- + /// MARK: UIKeyboard appearance overriding + ///--------------------------------------- + + /** + Override the keyboardAppearance for all textField/textView. Default is NO. + */ + @objc public var overrideKeyboardAppearance = false + + /** + If overrideKeyboardAppearance is YES, then all the textField keyboardAppearance is set using this property. + */ + @objc public var keyboardAppearance = UIKeyboardAppearance.default + + + ///----------------------------------------------------------- + /// MARK: UITextField/UITextView Next/Previous/Resign handling + ///----------------------------------------------------------- + + + /** + Resigns Keyboard on touching outside of UITextField/View. Default is NO. + */ + @objc public var shouldResignOnTouchOutside = false { + + didSet { + resignFirstResponderGesture.isEnabled = privateShouldResignOnTouchOutside() + + let shouldResign = shouldResignOnTouchOutside ? "Yes" : "NO" + + showLog("shouldResignOnTouchOutside: \(shouldResign)") + } + } + + /** TapGesture to resign keyboard on view's touch. It's a readonly property and exposed only for adding/removing dependencies if your added gesture does have collision with this one */ + @objc lazy public var resignFirstResponderGesture: UITapGestureRecognizer = { + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapRecognized(_:))) + tapGesture.cancelsTouchesInView = false + tapGesture.delegate = self + + return tapGesture + }() + + /*******************************************/ + + private func privateShouldResignOnTouchOutside() -> Bool { + + var shouldResign = shouldResignOnTouchOutside + + let enableMode = _textFieldView?.shouldResignOnTouchOutsideMode + + if enableMode == .enabled { + shouldResign = true + } else if enableMode == .disabled { + shouldResign = false + } else { + if let textFieldViewController = _textFieldView?.viewContainingController() { + + if shouldResign == false { + + //If viewController is kind of enable viewController class, then assuming shouldResignOnTouchOutside is enabled. + for enabledClass in enabledTouchResignedClasses { + + if textFieldViewController.isKind(of: enabledClass) { + shouldResign = true + break + } + } + } + + if shouldResign == true { + + //If viewController is kind of disable viewController class, then assuming shouldResignOnTouchOutside is disable. + for disabledClass in disabledTouchResignedClasses { + + if textFieldViewController.isKind(of: disabledClass) { + shouldResign = false + break + } + } + + //Special Controllers + if shouldResign == true { + + let classNameString = NSStringFromClass(type(of:textFieldViewController.self)) + + //_UIAlertControllerTextFieldViewController + if (classNameString.contains("UIAlertController") && classNameString.hasSuffix("TextFieldViewController")) { + shouldResign = false + } + } + } + } + } + + return shouldResign + } + + /** + Resigns currently first responder field. + */ + @objc @discardableResult public func resignFirstResponder()-> Bool { + + if let textFieldRetain = _textFieldView { + + //Resigning first responder + let isResignFirstResponder = textFieldRetain.resignFirstResponder() + + // If it refuses then becoming it as first responder again. (Bug ID: #96) + if isResignFirstResponder == false { + //If it refuses to resign then becoming it first responder again for getting notifications callback. + textFieldRetain.becomeFirstResponder() + + showLog("Refuses to resign first responder: \(String(describing: textFieldRetain._IQDescription()))") + } + + return isResignFirstResponder + } + + return false + } + + /** + Returns YES if can navigate to previous responder textField/textView, otherwise NO. + */ + @objc public var canGoPrevious: Bool { + //Getting all responder view's. + if let textFields = responderViews() { + if let textFieldRetain = _textFieldView { + + //Getting index of current textField. + if let index = textFields.index(of: textFieldRetain) { + + //If it is not first textField. then it's previous object canBecomeFirstResponder. + if index > 0 { + return true + } + } + } + } + return false + } + + /** + Returns YES if can navigate to next responder textField/textView, otherwise NO. + */ + @objc public var canGoNext: Bool { + //Getting all responder view's. + if let textFields = responderViews() { + if let textFieldRetain = _textFieldView { + //Getting index of current textField. + if let index = textFields.index(of: textFieldRetain) { + + //If it is not first textField. then it's previous object canBecomeFirstResponder. + if index < textFields.count-1 { + return true + } + } + } + } + return false + } + + /** + Navigate to previous responder textField/textView. + */ + @objc @discardableResult public func goPrevious()-> Bool { + + //Getting all responder view's. + if let textFieldRetain = _textFieldView { + if let textFields = responderViews() { + //Getting index of current textField. + if let index = textFields.index(of: textFieldRetain) { + + //If it is not first textField. then it's previous object becomeFirstResponder. + if index > 0 { + + let nextTextField = textFields[index-1] + + let isAcceptAsFirstResponder = nextTextField.becomeFirstResponder() + + // If it refuses then becoming previous textFieldView as first responder again. (Bug ID: #96) + if isAcceptAsFirstResponder == false { + //If next field refuses to become first responder then restoring old textField as first responder. + textFieldRetain.becomeFirstResponder() + + showLog("Refuses to become first responder: \(nextTextField._IQDescription())") + } + + return isAcceptAsFirstResponder + } + } + } + } + + return false + } + + /** + Navigate to next responder textField/textView. + */ + @objc @discardableResult public func goNext()-> Bool { + + //Getting all responder view's. + if let textFieldRetain = _textFieldView { + if let textFields = responderViews() { + //Getting index of current textField. + if let index = textFields.index(of: textFieldRetain) { + //If it is not last textField. then it's next object becomeFirstResponder. + if index < textFields.count-1 { + + let nextTextField = textFields[index+1] + + let isAcceptAsFirstResponder = nextTextField.becomeFirstResponder() + + // If it refuses then becoming previous textFieldView as first responder again. (Bug ID: #96) + if isAcceptAsFirstResponder == false { + //If next field refuses to become first responder then restoring old textField as first responder. + textFieldRetain.becomeFirstResponder() + + showLog("Refuses to become first responder: \(nextTextField._IQDescription())") + } + + return isAcceptAsFirstResponder + } + } + } + } + + return false + } + + /** previousAction. */ + @objc internal func previousAction (_ barButton : IQBarButtonItem) { + + //If user wants to play input Click sound. + if shouldPlayInputClicks == true { + //Play Input Click Sound. + UIDevice.current.playInputClick() + } + + if canGoPrevious == true { + + if let textFieldRetain = _textFieldView { + let isAcceptAsFirstResponder = goPrevious() + + var invocation = barButton.invocation + //Handling search bar special case + do { + if let searchBar = textFieldRetain.textFieldSearchBar() { + invocation = searchBar.keyboardToolbar.previousBarButton.invocation + } + } + + if isAcceptAsFirstResponder { + invocation?.invoke(from: textFieldRetain) + } + } + } + } + + /** nextAction. */ + @objc internal func nextAction (_ barButton : IQBarButtonItem) { + + //If user wants to play input Click sound. + if shouldPlayInputClicks == true { + //Play Input Click Sound. + UIDevice.current.playInputClick() + } + + if canGoNext == true { + + if let textFieldRetain = _textFieldView { + let isAcceptAsFirstResponder = goNext() + + var invocation = barButton.invocation + //Handling search bar special case + do { + if let searchBar = textFieldRetain.textFieldSearchBar() { + invocation = searchBar.keyboardToolbar.nextBarButton.invocation + } + } + + if isAcceptAsFirstResponder { + invocation?.invoke(from: textFieldRetain) + } + } + } + } + + /** doneAction. Resigning current textField. */ + @objc internal func doneAction (_ barButton : IQBarButtonItem) { + + //If user wants to play input Click sound. + if shouldPlayInputClicks == true { + //Play Input Click Sound. + UIDevice.current.playInputClick() + } + + if let textFieldRetain = _textFieldView { + //Resign textFieldView. + let isResignedFirstResponder = resignFirstResponder() + + var invocation = barButton.invocation + //Handling search bar special case + do { + if let searchBar = textFieldRetain.textFieldSearchBar() { + invocation = searchBar.keyboardToolbar.doneBarButton.invocation + } + } + + if isResignedFirstResponder { + invocation?.invoke(from: textFieldRetain) + } + } + } + + /** Resigning on tap gesture. (Enhancement ID: #14)*/ + @objc internal func tapRecognized(_ gesture: UITapGestureRecognizer) { + + if gesture.state == .ended { + + //Resigning currently responder textField. + _ = resignFirstResponder() + } + } + + /** Note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES. */ + @objc public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return false + } + + /** To not detect touch events in a subclass of UIControl, these may have added their own selector for specific work */ + @objc public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + // Should not recognize gesture if the clicked view is either UIControl or UINavigationBar(=4.2) + private var _animationCurve : UIView.AnimationOptions = .curveEaseOut + #else + private var _animationCurve : UIViewAnimationOptions = .curveEaseOut + #endif + + /*******************************************/ + + /** Boolean to maintain keyboard is showing or it is hide. To solve rootViewController.view.frame calculations. */ + private var _privateIsKeyboardShowing = false + + private var _privateMovedDistance : CGFloat = 0.0 + + /** To use with keyboardDistanceFromTextField. */ + private var _privateKeyboardDistanceFromTextField: CGFloat = 10.0 + + /** To know if we have any pending request to adjust view position. */ + private var _privateHasPendingAdjustRequest = false + + /**************************************************************************************/ + + ///-------------------------------------- + /// MARK: Initialization/Deinitialization + ///-------------------------------------- + + /* Singleton Object Initialization. */ + override init() { + + super.init() + + self.registerAllNotifications() + + //Creating gesture for @shouldResignOnTouchOutside. (Enhancement ID: #14) + resignFirstResponderGesture.isEnabled = shouldResignOnTouchOutside + + //Loading IQToolbar, IQTitleBarButtonItem, IQBarButtonItem to fix first time keyboard appearance delay (Bug ID: #550) + //If you experience exception breakpoint issue at below line then try these solutions https://stackoverflow.com/questions/27375640/all-exception-break-point-is-stopping-for-no-reason-on-simulator + let textField = UITextField() + textField.addDoneOnKeyboardWithTarget(nil, action: #selector(self.doneAction(_:))) + textField.addPreviousNextDoneOnKeyboardWithTarget(nil, previousAction: #selector(self.previousAction(_:)), nextAction: #selector(self.nextAction(_:)), doneAction: #selector(self.doneAction(_:))) + + disabledDistanceHandlingClasses.append(UITableViewController.self) + disabledDistanceHandlingClasses.append(UIAlertController.self) + disabledToolbarClasses.append(UIAlertController.self) + disabledTouchResignedClasses.append(UIAlertController.self) + toolbarPreviousNextAllowedClasses.append(UITableView.self) + toolbarPreviousNextAllowedClasses.append(UICollectionView.self) + toolbarPreviousNextAllowedClasses.append(IQPreviousNextView.self) + touchResignedGestureIgnoreClasses.append(UIControl.self) + touchResignedGestureIgnoreClasses.append(UINavigationBar.self) + } + + /** Override +load method to enable KeyboardManager when class loader load IQKeyboardManager. Enabling when app starts (No need to write any code) */ + /** It doesn't work from Swift 1.2 */ +// override public class func load() { +// super.load() +// +// //Enabling IQKeyboardManager. +// IQKeyboardManager.shared.enable = true +// } + + deinit { + // Disable the keyboard manager. + enable = false + + //Removing notification observers on dealloc. + NotificationCenter.default.removeObserver(self) + } + + /** Getting keyWindow. */ + private func keyWindow() -> UIWindow? { + + if let keyWindow = _textFieldView?.window { + return keyWindow + } else { + + struct Static { + /** @abstract Save keyWindow object for reuse. + @discussion Sometimes [[UIApplication sharedApplication] keyWindow] is returning nil between the app. */ + static weak var keyWindow : UIWindow? + } + + //If original key window is not nil and the cached keywindow is also not original keywindow then changing keywindow. + if let originalKeyWindow = UIApplication.shared.keyWindow, + (Static.keyWindow == nil || Static.keyWindow != originalKeyWindow) { + Static.keyWindow = originalKeyWindow + } + + //Return KeyWindow + return Static.keyWindow + } + } + + ///----------------------- + /// MARK: Helper Functions + ///----------------------- + + private func optimizedAdjustPosition() { + if _privateHasPendingAdjustRequest == false { + _privateHasPendingAdjustRequest = true + OperationQueue.main.addOperation { + self.adjustPosition() + self._privateHasPendingAdjustRequest = false + } + } + } + + /* Adjusting RootViewController's frame according to interface orientation. */ + private func adjustPosition() { + + // We are unable to get textField object while keyboard showing on UIWebView's textField. (Bug ID: #11) + if _privateHasPendingAdjustRequest == true, + let textFieldView = _textFieldView, + let rootController = textFieldView.parentContainerViewController(), + let window = keyWindow(), + let textFieldViewRectInWindow = textFieldView.superview?.convert(textFieldView.frame, to: window), + let textFieldViewRectInRootSuperview = textFieldView.superview?.convert(textFieldView.frame, to: rootController.view?.superview) + { + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + // Getting RootViewOrigin. + var rootViewOrigin = rootController.view.frame.origin + + //Maintain keyboardDistanceFromTextField + var specialKeyboardDistanceFromTextField = textFieldView.keyboardDistanceFromTextField + + if let searchBar = textFieldView.textFieldSearchBar() { + + specialKeyboardDistanceFromTextField = searchBar.keyboardDistanceFromTextField + } + + let newKeyboardDistanceFromTextField = (specialKeyboardDistanceFromTextField == kIQUseDefaultKeyboardDistance) ? keyboardDistanceFromTextField : specialKeyboardDistanceFromTextField + var kbSize = _kbSize + kbSize.height += newKeyboardDistanceFromTextField + + let navigationBarAreaHeight : CGFloat = UIApplication.shared.statusBarFrame.height + ( rootController.navigationController?.navigationBar.frame.height ?? 0) + let layoutAreaHeight : CGFloat = rootController.view.layoutMargins.bottom + + let topLayoutGuide : CGFloat = max(navigationBarAreaHeight, layoutAreaHeight) + 5 + let bottomLayoutGuide : CGFloat = (textFieldView is UITextView) ? 0 : rootController.view.layoutMargins.bottom //Validation of textView for case where there is a tab bar at the bottom or running on iPhone X and textView is at the bottom. + + // Move positive = textField is hidden. + // Move negative = textField is showing. + // Calculating move position. + var move : CGFloat = min(textFieldViewRectInRootSuperview.minY-(topLayoutGuide), textFieldViewRectInWindow.maxY-(window.frame.height-kbSize.height)+bottomLayoutGuide) + + showLog("Need to move: \(move)") + + var superScrollView : UIScrollView? = nil + var superView = textFieldView.superviewOfClassType(UIScrollView.self) as? UIScrollView + + //Getting UIScrollView whose scrolling is enabled. // (Bug ID: #285) + while let view = superView { + + if (view.isScrollEnabled && view.shouldIgnoreScrollingAdjustment == false) { + superScrollView = view + break + } + else { + // Getting it's superScrollView. // (Enhancement ID: #21, #24) + superView = view.superviewOfClassType(UIScrollView.self) as? UIScrollView + } + } + + //If there was a lastScrollView. // (Bug ID: #34) + if let lastScrollView = _lastScrollView { + //If we can't find current superScrollView, then setting lastScrollView to it's original form. + if superScrollView == nil { + + showLog("Restoring \(lastScrollView._IQDescription()) contentInset to : \(_startingContentInsets) and contentOffset to : \(_startingContentOffset)") + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + lastScrollView.contentInset = self._startingContentInsets + lastScrollView.scrollIndicatorInsets = self._startingScrollIndicatorInsets + }) { (animated:Bool) -> Void in } + + if lastScrollView.shouldRestoreScrollViewContentOffset == true { + lastScrollView.setContentOffset(_startingContentOffset, animated: UIView.areAnimationsEnabled) + } + + _startingContentInsets = UIEdgeInsets() + _startingScrollIndicatorInsets = UIEdgeInsets() + _startingContentOffset = CGPoint.zero + _lastScrollView = nil + } else if superScrollView != lastScrollView { //If both scrollView's are different, then reset lastScrollView to it's original frame and setting current scrollView as last scrollView. + + showLog("Restoring \(lastScrollView._IQDescription()) contentInset to : \(_startingContentInsets) and contentOffset to : \(_startingContentOffset)") + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + lastScrollView.contentInset = self._startingContentInsets + lastScrollView.scrollIndicatorInsets = self._startingScrollIndicatorInsets + }) { (animated:Bool) -> Void in } + + if lastScrollView.shouldRestoreScrollViewContentOffset == true { + lastScrollView.setContentOffset(_startingContentOffset, animated: UIView.areAnimationsEnabled) + } + + _lastScrollView = superScrollView + if let scrollView = superScrollView { + _startingContentInsets = scrollView.contentInset + _startingScrollIndicatorInsets = scrollView.scrollIndicatorInsets + _startingContentOffset = scrollView.contentOffset + } + + showLog("Saving New \(lastScrollView._IQDescription()) contentInset : \(_startingContentInsets) and contentOffset : \(_startingContentOffset)") + } + //Else the case where superScrollView == lastScrollView means we are on same scrollView after switching to different textField. So doing nothing, going ahead + } else if let unwrappedSuperScrollView = superScrollView { //If there was no lastScrollView and we found a current scrollView. then setting it as lastScrollView. + _lastScrollView = unwrappedSuperScrollView + _startingContentInsets = unwrappedSuperScrollView.contentInset + _startingScrollIndicatorInsets = unwrappedSuperScrollView.scrollIndicatorInsets + _startingContentOffset = unwrappedSuperScrollView.contentOffset + + showLog("Saving \(unwrappedSuperScrollView._IQDescription()) contentInset : \(_startingContentInsets) and contentOffset : \(_startingContentOffset)") + } + + // Special case for ScrollView. + // If we found lastScrollView then setting it's contentOffset to show textField. + if let lastScrollView = _lastScrollView { + //Saving + var lastView = textFieldView + var superScrollView = _lastScrollView + + while let scrollView = superScrollView { + + //Looping in upper hierarchy until we don't found any scrollView in it's upper hirarchy till UIWindow object. + if move > 0 ? (move > (-scrollView.contentOffset.y - scrollView.contentInset.top)) : scrollView.contentOffset.y>0 { + + var tempScrollView = scrollView.superviewOfClassType(UIScrollView.self) as? UIScrollView + var nextScrollView : UIScrollView? = nil + while let view = tempScrollView { + + if (view.isScrollEnabled && view.shouldIgnoreScrollingAdjustment == false) { + nextScrollView = view + break + } else { + tempScrollView = view.superviewOfClassType(UIScrollView.self) as? UIScrollView + } + } + + //Getting lastViewRect. + if let lastViewRect = lastView.superview?.convert(lastView.frame, to: scrollView) { + + //Calculating the expected Y offset from move and scrollView's contentOffset. + var shouldOffsetY = scrollView.contentOffset.y - min(scrollView.contentOffset.y,-move) + + //Rearranging the expected Y offset according to the view. + shouldOffsetY = min(shouldOffsetY, lastViewRect.origin.y) + + //[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type + //nextScrollView == nil If processing scrollView is last scrollView in upper hierarchy (there is no other scrollView upper hierrchy.) + //[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type + //shouldOffsetY >= 0 shouldOffsetY must be greater than in order to keep distance from navigationBar (Bug ID: #92) + if textFieldView is UITextView == true && + nextScrollView == nil && + shouldOffsetY >= 0 { + + // Converting Rectangle according to window bounds. + if let currentTextFieldViewRect = textFieldView.superview?.convert(textFieldView.frame, to: window) { + + //Calculating expected fix distance which needs to be managed from navigation bar + let expectedFixDistance = currentTextFieldViewRect.minY - topLayoutGuide + + //Now if expectedOffsetY (superScrollView.contentOffset.y + expectedFixDistance) is lower than current shouldOffsetY, which means we're in a position where navigationBar up and hide, then reducing shouldOffsetY with expectedOffsetY (superScrollView.contentOffset.y + expectedFixDistance) + shouldOffsetY = min(shouldOffsetY, scrollView.contentOffset.y + expectedFixDistance) + + //Setting move to 0 because now we don't want to move any view anymore (All will be managed by our contentInset logic. + move = 0 + } + else { + //Subtracting the Y offset from the move variable, because we are going to change scrollView's contentOffset.y to shouldOffsetY. + move -= (shouldOffsetY-scrollView.contentOffset.y) + } + } + else + { + //Subtracting the Y offset from the move variable, because we are going to change scrollView's contentOffset.y to shouldOffsetY. + move -= (shouldOffsetY-scrollView.contentOffset.y) + } + + //Getting problem while using `setContentOffset:animated:`, So I used animation API. + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + self.showLog("Adjusting \(scrollView.contentOffset.y-shouldOffsetY) to \(scrollView._IQDescription()) ContentOffset") + + self.showLog("Remaining Move: \(move)") + + scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: shouldOffsetY) + }) { (animated:Bool) -> Void in } + } + + // Getting next lastView & superScrollView. + lastView = scrollView + superScrollView = nextScrollView + } else { + break + } + } + + //Updating contentInset + if let lastScrollViewRect = lastScrollView.superview?.convert(lastScrollView.frame, to: window) { + + let bottom : CGFloat = (kbSize.height-newKeyboardDistanceFromTextField)-(window.frame.height-lastScrollViewRect.maxY) + + // Update the insets so that the scroll vew doesn't shift incorrectly when the offset is near the bottom of the scroll view. + var movedInsets = lastScrollView.contentInset + + movedInsets.bottom = max(_startingContentInsets.bottom, bottom) + + showLog("\(lastScrollView._IQDescription()) old ContentInset : \(lastScrollView.contentInset)") + + //Getting problem while using `setContentOffset:animated:`, So I used animation API. + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + lastScrollView.contentInset = movedInsets + + var newInset = lastScrollView.scrollIndicatorInsets + newInset.bottom = movedInsets.bottom + lastScrollView.scrollIndicatorInsets = newInset + + }) { (animated:Bool) -> Void in } + + showLog("\(lastScrollView._IQDescription()) new ContentInset : \(lastScrollView.contentInset)") + } + } + //Going ahead. No else if. + + //Special case for UITextView(Readjusting textView.contentInset when textView hight is too big to fit on screen) + //_lastScrollView If not having inside any scrollView, (now contentInset manages the full screen textView. + //[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type + if let textView = textFieldView as? UITextView { + +// CGRect rootSuperViewFrameInWindow = [_rootViewController.view.superview convertRect:_rootViewController.view.superview.bounds toView:keyWindow]; +// +// CGFloat keyboardOverlapping = CGRectGetMaxY(rootSuperViewFrameInWindow) - keyboardYPosition; +// +// CGFloat textViewHeight = MIN(CGRectGetHeight(_textFieldView.frame), (CGRectGetHeight(rootSuperViewFrameInWindow)-topLayoutGuide-keyboardOverlapping)); + + let keyboardYPosition = window.frame.height - (kbSize.height-newKeyboardDistanceFromTextField) + var rootSuperViewFrameInWindow = window.frame + if let rootSuperview = rootController.view.superview { + rootSuperViewFrameInWindow = rootSuperview.convert(rootSuperview.bounds, to: window) + } + + let keyboardOverlapping = rootSuperViewFrameInWindow.maxY - keyboardYPosition + + let textViewHeight = min(textView.frame.height, rootSuperViewFrameInWindow.height-topLayoutGuide-keyboardOverlapping) + + if (textView.frame.size.height-textView.contentInset.bottom>textViewHeight) + { + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + self.showLog("\(textFieldView._IQDescription()) Old UITextView.contentInset : \(textView.contentInset)") + + //_isTextViewContentInsetChanged, If frame is not change by library in past, then saving user textView properties (Bug ID: #92) + if (self.isTextViewContentInsetChanged == false) + { + self.startingTextViewContentInsets = textView.contentInset + self.startingTextViewScrollIndicatorInsets = textView.scrollIndicatorInsets + } + + var newContentInset = textView.contentInset + newContentInset.bottom = textView.frame.size.height-textViewHeight + textView.contentInset = newContentInset + textView.scrollIndicatorInsets = newContentInset + self.isTextViewContentInsetChanged = true + + self.showLog("\(textFieldView._IQDescription()) Old UITextView.contentInset : \(textView.contentInset)") + + + }, completion: { (finished) -> Void in }) + } + } + + // +Positive or zero. + if move >= 0 { + + rootViewOrigin.y -= move + + rootViewOrigin.y = max(rootViewOrigin.y, min(0, -(kbSize.height-newKeyboardDistanceFromTextField))) + + showLog("Moving Upward") + // Setting adjusted rootViewRect + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + var rect = rootController.view.frame + rect.origin = rootViewOrigin + rootController.view.frame = rect + + //Animating content if needed (Bug ID: #204) + if self.layoutIfNeededOnUpdate == true { + //Animating content (Bug ID: #160) + rootController.view.setNeedsLayout() + rootController.view.layoutIfNeeded() + } + + self.showLog("Set \(String(describing: rootController._IQDescription())) origin to : \(rootViewOrigin)") + + }) { (finished) -> Void in } + + _privateMovedDistance = (_topViewBeginOrigin.y-rootViewOrigin.y) + } else { // -Negative + let disturbDistance : CGFloat = rootViewOrigin.y-_topViewBeginOrigin.y + + // disturbDistance Negative = frame disturbed. + // disturbDistance positive = frame not disturbed. + if disturbDistance <= 0 { + + rootViewOrigin.y -= max(move, disturbDistance) + + showLog("Moving Downward") + // Setting adjusted rootViewRect + // Setting adjusted rootViewRect + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + var rect = rootController.view.frame + rect.origin = rootViewOrigin + rootController.view.frame = rect + + //Animating content if needed (Bug ID: #204) + if self.layoutIfNeededOnUpdate == true { + //Animating content (Bug ID: #160) + rootController.view.setNeedsLayout() + rootController.view.layoutIfNeeded() + } + + self.showLog("Set \(String(describing: rootController._IQDescription())) origin to : \(rootViewOrigin)") + + }) { (finished) -> Void in } + + _privateMovedDistance = (_topViewBeginOrigin.y-rootViewOrigin.y) + } + } + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + } + + private func restorePosition() { + + _privateHasPendingAdjustRequest = false + + // Setting rootViewController frame to it's original position. // (Bug ID: #18) + if _topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) == false { + + if let rootViewController = _rootViewController { + + //Used UIViewAnimationOptionBeginFromCurrentState to minimize strange animations. + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + self.showLog("Restoring \(rootViewController._IQDescription()) origin to : \(self._topViewBeginOrigin)") + + // Setting it's new frame + var rect = rootViewController.view.frame + rect.origin = self._topViewBeginOrigin + rootViewController.view.frame = rect + + self._privateMovedDistance = 0 + + if rootViewController.navigationController?.interactivePopGestureRecognizer?.state == .began { + self._rootViewControllerWhilePopGestureRecognizerActive = rootViewController + self._topViewBeginOriginWhilePopGestureRecognizerActive = self._topViewBeginOrigin + } + + //Animating content if needed (Bug ID: #204) + if self.layoutIfNeededOnUpdate == true { + //Animating content (Bug ID: #160) + rootViewController.view.setNeedsLayout() + rootViewController.view.layoutIfNeeded() + } + }) { (finished) -> Void in } + + _rootViewController = nil + } + } + } + + ///--------------------- + /// MARK: Public Methods + ///--------------------- + + /* Refreshes textField/textView position if any external changes is explicitly made by user. */ + @objc public func reloadLayoutIfNeeded() -> Void { + + if privateIsEnabled() == true { + if _privateIsKeyboardShowing == true, + _topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) == false, + let textFieldView = _textFieldView, + textFieldView.isAlertViewTextField() == false { + optimizedAdjustPosition() + } + } + } + + ///------------------------------- + /// MARK: UIKeyboard Notifications + ///------------------------------- + + /* UIKeyboardWillShowNotification. */ + @objc internal func keyboardWillShow(_ notification : Notification?) -> Void { + + _kbShowNotification = notification + + // Boolean to know keyboard is showing/hiding + _privateIsKeyboardShowing = true + + let oldKBSize = _kbSize + + if let info = notification?.userInfo { + + #if swift(>=4.2) + let curveUserInfoKey = UIResponder.keyboardAnimationCurveUserInfoKey + let durationUserInfoKey = UIResponder.keyboardAnimationDurationUserInfoKey + let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey + #else + let curveUserInfoKey = UIKeyboardAnimationCurveUserInfoKey + let durationUserInfoKey = UIKeyboardAnimationDurationUserInfoKey + let frameEndUserInfoKey = UIKeyboardFrameEndUserInfoKey + #endif + + // Getting keyboard animation. + if let curve = info[curveUserInfoKey] as? UInt { + _animationCurve = .init(rawValue: curve) + } else { + _animationCurve = .curveEaseOut + } + + // Getting keyboard animation duration + if let duration = info[durationUserInfoKey] as? TimeInterval { + + //Saving animation duration + if duration != 0.0 { + _animationDuration = duration + } + } else { + _animationDuration = 0.25 + } + + // Getting UIKeyboardSize. + if let kbFrame = info[frameEndUserInfoKey] as? CGRect { + + let screenSize = UIScreen.main.bounds + + //Calculating actual keyboard displayed size, keyboard frame may be different when hardware keyboard is attached (Bug ID: #469) (Bug ID: #381) + let intersectRect = kbFrame.intersection(screenSize) + + if intersectRect.isNull { + _kbSize = CGSize(width: screenSize.size.width, height: 0) + } else { + _kbSize = intersectRect.size + } + + showLog("UIKeyboard Size : \(_kbSize)") + } + } + + if privateIsEnabled() == false { + return + } + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + // (Bug ID: #5) + if let textFieldView = _textFieldView, _topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) == true { + + // keyboard is not showing(At the beginning only). We should save rootViewRect. + _rootViewController = textFieldView.parentContainerViewController() + if let controller = _rootViewController { + + if _rootViewControllerWhilePopGestureRecognizerActive == controller { + _topViewBeginOrigin = _topViewBeginOriginWhilePopGestureRecognizerActive + } else { + _topViewBeginOrigin = controller.view.frame.origin + } + + _rootViewControllerWhilePopGestureRecognizerActive = nil + _topViewBeginOriginWhilePopGestureRecognizerActive = IQKeyboardManager.kIQCGPointInvalid + + self.showLog("Saving \(controller._IQDescription()) beginning origin : \(self._topViewBeginOrigin)") + } + } + + //If last restored keyboard size is different(any orientation accure), then refresh. otherwise not. + if _kbSize.equalTo(oldKBSize) == false { + + //If _textFieldView is inside UITableViewController then let UITableViewController to handle it (Bug ID: #37) (Bug ID: #76) See note:- https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html If it is UIAlertView textField then do not affect anything (Bug ID: #70). + + if _privateIsKeyboardShowing == true, + let textFieldView = _textFieldView, + textFieldView.isAlertViewTextField() == false { + + // keyboard is already showing. adjust position. + optimizedAdjustPosition() + } + } + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + /* UIKeyboardDidShowNotification. */ + @objc internal func keyboardDidShow(_ notification : Notification?) -> Void { + + if privateIsEnabled() == false { + return + } + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + if let textFieldView = _textFieldView, + let parentController = textFieldView.parentContainerViewController(), (parentController.modalPresentationStyle == UIModalPresentationStyle.formSheet || parentController.modalPresentationStyle == UIModalPresentationStyle.pageSheet), + textFieldView.isAlertViewTextField() == false { + + self.optimizedAdjustPosition() + } + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + /* UIKeyboardWillHideNotification. So setting rootViewController to it's default frame. */ + @objc internal func keyboardWillHide(_ notification : Notification?) -> Void { + + //If it's not a fake notification generated by [self setEnable:NO]. + if notification != nil { + _kbShowNotification = nil + } + + // Boolean to know keyboard is showing/hiding + _privateIsKeyboardShowing = false + + if let info = notification?.userInfo { + + #if swift(>=4.2) + let durationUserInfoKey = UIResponder.keyboardAnimationDurationUserInfoKey + #else + let durationUserInfoKey = UIKeyboardAnimationDurationUserInfoKey + #endif + + // Getting keyboard animation duration + if let duration = info[durationUserInfoKey] as? TimeInterval { + if duration != 0 { + // Setitng keyboard animation duration + _animationDuration = duration + } + } + } + + //If not enabled then do nothing. + if privateIsEnabled() == false { + return + } + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + //Commented due to #56. Added all the conditions below to handle UIWebView's textFields. (Bug ID: #56) + // We are unable to get textField object while keyboard showing on UIWebView's textField. (Bug ID: #11) + // if (_textFieldView == nil) return + + //Restoring the contentOffset of the lastScrollView + if let lastScrollView = _lastScrollView { + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + lastScrollView.contentInset = self._startingContentInsets + lastScrollView.scrollIndicatorInsets = self._startingScrollIndicatorInsets + + if lastScrollView.shouldRestoreScrollViewContentOffset == true { + lastScrollView.contentOffset = self._startingContentOffset + } + + self.showLog("Restoring \(lastScrollView._IQDescription()) contentInset to : \(self._startingContentInsets) and contentOffset to : \(self._startingContentOffset)") + + // TODO: restore scrollView state + // This is temporary solution. Have to implement the save and restore scrollView state + var superScrollView : UIScrollView? = lastScrollView + + while let scrollView = superScrollView { + + let contentSize = CGSize(width: max(scrollView.contentSize.width, scrollView.frame.width), height: max(scrollView.contentSize.height, scrollView.frame.height)) + + let minimumY = contentSize.height - scrollView.frame.height + + if minimumY < scrollView.contentOffset.y { + scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: minimumY) + + self.showLog("Restoring \(scrollView._IQDescription()) contentOffset to : \(self._startingContentOffset)") + } + + superScrollView = scrollView.superviewOfClassType(UIScrollView.self) as? UIScrollView + } + }) { (finished) -> Void in } + } + + restorePosition() + + //Reset all values + _lastScrollView = nil + _kbSize = CGSize.zero + _startingContentInsets = UIEdgeInsets() + _startingScrollIndicatorInsets = UIEdgeInsets() + _startingContentOffset = CGPoint.zero + // topViewBeginRect = CGRectZero //Commented due to #82 + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + @objc internal func keyboardDidHide(_ notification:Notification) { + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + _topViewBeginOrigin = IQKeyboardManager.kIQCGPointInvalid + + _kbSize = CGSize.zero + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + ///------------------------------------------- + /// MARK: UITextField/UITextView Notifications + ///------------------------------------------- + + /** UITextFieldTextDidBeginEditingNotification, UITextViewTextDidBeginEditingNotification. Fetching UITextFieldView object. */ + @objc internal func textFieldViewDidBeginEditing(_ notification:Notification) { + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + // Getting object + _textFieldView = notification.object as? UIView + + if overrideKeyboardAppearance == true { + + if let textFieldView = _textFieldView as? UITextField { + //If keyboard appearance is not like the provided appearance + if textFieldView.keyboardAppearance != keyboardAppearance { + //Setting textField keyboard appearance and reloading inputViews. + textFieldView.keyboardAppearance = keyboardAppearance + textFieldView.reloadInputViews() + } + } else if let textFieldView = _textFieldView as? UITextView { + //If keyboard appearance is not like the provided appearance + if textFieldView.keyboardAppearance != keyboardAppearance { + //Setting textField keyboard appearance and reloading inputViews. + textFieldView.keyboardAppearance = keyboardAppearance + textFieldView.reloadInputViews() + } + } + } + + //If autoToolbar enable, then add toolbar on all the UITextField/UITextView's if required. + if privateIsEnableAutoToolbar() == true { + + //UITextView special case. Keyboard Notification is firing before textView notification so we need to resign it first and then again set it as first responder to add toolbar on it. + if let textView = _textFieldView as? UITextView, + textView.inputAccessoryView == nil { + + UIView.animate(withDuration: 0.00001, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + self.addToolbarIfRequired() + + }, completion: { (finished) -> Void in + + //On textView toolbar didn't appear on first time, so forcing textView to reload it's inputViews. + textView.reloadInputViews() + }) + } else { + //Adding toolbar + addToolbarIfRequired() + } + } else { + removeToolbarIfRequired() + } + + resignFirstResponderGesture.isEnabled = privateShouldResignOnTouchOutside() + _textFieldView?.window?.addGestureRecognizer(resignFirstResponderGesture) // (Enhancement ID: #14) + + if privateIsEnabled() == true { + if _topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) == true { // (Bug ID: #5) + + _rootViewController = _textFieldView?.parentContainerViewController() + + if let controller = _rootViewController { + + if _rootViewControllerWhilePopGestureRecognizerActive == controller { + _topViewBeginOrigin = _topViewBeginOriginWhilePopGestureRecognizerActive + } else { + _topViewBeginOrigin = controller.view.frame.origin + } + + _rootViewControllerWhilePopGestureRecognizerActive = nil + _topViewBeginOriginWhilePopGestureRecognizerActive = IQKeyboardManager.kIQCGPointInvalid + + self.showLog("Saving \(controller._IQDescription()) beginning origin : \(self._topViewBeginOrigin)") + } + } + + //If _textFieldView is inside ignored responder then do nothing. (Bug ID: #37, #74, #76) + //See notes:- https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html If it is UIAlertView textField then do not affect anything (Bug ID: #70). + if _privateIsKeyboardShowing == true, + let textFieldView = _textFieldView, + textFieldView.isAlertViewTextField() == false { + + // keyboard is already showing. adjust position. + optimizedAdjustPosition() + } + } + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + /** UITextFieldTextDidEndEditingNotification, UITextViewTextDidEndEditingNotification. Removing fetched object. */ + @objc internal func textFieldViewDidEndEditing(_ notification:Notification) { + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + //Removing gesture recognizer (Enhancement ID: #14) + _textFieldView?.window?.removeGestureRecognizer(resignFirstResponderGesture) + + // We check if there's a change in original frame or not. + + if let textView = _textFieldView as? UITextView { + + if isTextViewContentInsetChanged == true { + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + self.isTextViewContentInsetChanged = false + + self.showLog("Restoring \(textView._IQDescription()) textView.contentInset to : \(self.startingTextViewContentInsets)") + + //Setting textField to it's initial contentInset + textView.contentInset = self.startingTextViewContentInsets + textView.scrollIndicatorInsets = self.startingTextViewScrollIndicatorInsets + + }, completion: { (finished) -> Void in }) + } + } + + //Setting object to nil + _textFieldView = nil + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + ///--------------------------------------- + /// MARK: UIStatusBar Notification methods + ///--------------------------------------- + + /** UIApplicationWillChangeStatusBarOrientationNotification. Need to set the textView to it's original position. If any frame changes made. (Bug ID: #92)*/ + @objc internal func willChangeStatusBarOrientation(_ notification:Notification) { + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + //If textViewContentInsetChanged is saved then restore it. + if let textView = _textFieldView as? UITextView { + + if isTextViewContentInsetChanged == true { + + UIView.animate(withDuration: _animationDuration, delay: 0, options: _animationCurve.union(.beginFromCurrentState), animations: { () -> Void in + + self.isTextViewContentInsetChanged = false + + self.showLog("Restoring \(textView._IQDescription()) textView.contentInset to : \(self.startingTextViewContentInsets)") + + //Setting textField to it's initial contentInset + textView.contentInset = self.startingTextViewContentInsets + textView.scrollIndicatorInsets = self.startingTextViewScrollIndicatorInsets + + }, completion: { (finished) -> Void in }) + } + } + + restorePosition() + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + ///------------------ + /// MARK: AutoToolbar + ///------------------ + + /** Get all UITextField/UITextView siblings of textFieldView. */ + private func responderViews()-> [UIView]? { + + var superConsideredView : UIView? + + //If find any consider responderView in it's upper hierarchy then will get deepResponderView. + for disabledClass in toolbarPreviousNextAllowedClasses { + + superConsideredView = _textFieldView?.superviewOfClassType(disabledClass) + + if superConsideredView != nil { + break + } + } + + //If there is a superConsideredView in view's hierarchy, then fetching all it's subview that responds. No sorting for superConsideredView, it's by subView position. (Enhancement ID: #22) + if let view = superConsideredView { + return view.deepResponderViews() + } else { //Otherwise fetching all the siblings + + if let textFields = _textFieldView?.responderSiblings() { + + //Sorting textFields according to behaviour + switch toolbarManageBehaviour { + //If autoToolbar behaviour is bySubviews, then returning it. + case IQAutoToolbarManageBehaviour.bySubviews: return textFields + + //If autoToolbar behaviour is by tag, then sorting it according to tag property. + case IQAutoToolbarManageBehaviour.byTag: return textFields.sortedArrayByTag() + + //If autoToolbar behaviour is by tag, then sorting it according to tag property. + case IQAutoToolbarManageBehaviour.byPosition: return textFields.sortedArrayByPosition() + } + } else { + return nil + } + } + } + + /** Add toolbar if it is required to add on textFields and it's siblings. */ + private func addToolbarIfRequired() { + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + // Getting all the sibling textFields. + if let siblings = responderViews(), !siblings.isEmpty { + + showLog("Found \(siblings.count) responder sibling(s)") + + if let textField = _textFieldView { + //Either there is no inputAccessoryView or if accessoryView is not appropriate for current situation(There is Previous/Next/Done toolbar). + //setInputAccessoryView: check (Bug ID: #307) + if textField.responds(to: #selector(setter: UITextField.inputAccessoryView)) { + + if textField.inputAccessoryView == nil || + textField.inputAccessoryView?.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag || + textField.inputAccessoryView?.tag == IQKeyboardManager.kIQDoneButtonToolbarTag { + + let rightConfiguration : IQBarButtonItemConfiguration + + if let doneBarButtonItemImage = toolbarDoneBarButtonItemImage { + rightConfiguration = IQBarButtonItemConfiguration(image: doneBarButtonItemImage, action: #selector(self.doneAction(_:))) + } else if let doneBarButtonItemText = toolbarDoneBarButtonItemText { + rightConfiguration = IQBarButtonItemConfiguration(title: doneBarButtonItemText, action: #selector(self.doneAction(_:))) + } else { + rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: #selector(self.doneAction(_:))) + } + + // If only one object is found, then adding only Done button. + if (siblings.count == 1 && previousNextDisplayMode == .Default) || previousNextDisplayMode == .alwaysHide { + + textField.addKeyboardToolbarWithTarget(target: self, titleText: (shouldShowToolbarPlaceholder ? textField.drawingToolbarPlaceholder : nil), rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: nil, nextBarButtonConfiguration: nil) + + textField.inputAccessoryView?.tag = IQKeyboardManager.kIQDoneButtonToolbarTag // (Bug ID: #78) + + } else if (siblings.count > 1 && previousNextDisplayMode == .Default) || previousNextDisplayMode == .alwaysShow { + + let prevConfiguration : IQBarButtonItemConfiguration + + if let doneBarButtonItemImage = toolbarPreviousBarButtonItemImage { + prevConfiguration = IQBarButtonItemConfiguration(image: doneBarButtonItemImage, action: #selector(self.previousAction(_:))) + } else if let doneBarButtonItemText = toolbarPreviousBarButtonItemText { + prevConfiguration = IQBarButtonItemConfiguration(title: doneBarButtonItemText, action: #selector(self.previousAction(_:))) + } else { + prevConfiguration = IQBarButtonItemConfiguration(image: (UIImage.keyboardPreviousImage() ?? UIImage()), action: #selector(self.previousAction(_:))) + } + + let nextConfiguration : IQBarButtonItemConfiguration + + if let doneBarButtonItemImage = toolbarNextBarButtonItemImage { + nextConfiguration = IQBarButtonItemConfiguration(image: doneBarButtonItemImage, action: #selector(self.nextAction(_:))) + } else if let doneBarButtonItemText = toolbarNextBarButtonItemText { + nextConfiguration = IQBarButtonItemConfiguration(title: doneBarButtonItemText, action: #selector(self.nextAction(_:))) + } else { + nextConfiguration = IQBarButtonItemConfiguration(image: (UIImage.keyboardNextImage() ?? UIImage()), action: #selector(self.nextAction(_:))) + } + + textField.addKeyboardToolbarWithTarget(target: self, titleText: (shouldShowToolbarPlaceholder ? textField.drawingToolbarPlaceholder : nil), rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration) + + textField.inputAccessoryView?.tag = IQKeyboardManager.kIQPreviousNextButtonToolbarTag // (Bug ID: #78) + } + + let toolbar = textField.keyboardToolbar + + // Setting toolbar to keyboard. + if let _textField = textField as? UITextField { + + //Bar style according to keyboard appearance + switch _textField.keyboardAppearance { + + case .dark: + toolbar.barStyle = UIBarStyle.black + toolbar.tintColor = UIColor.white + toolbar.barTintColor = nil + default: + toolbar.barStyle = UIBarStyle.default + toolbar.barTintColor = toolbarBarTintColor + + //Setting toolbar tintColor // (Enhancement ID: #30) + if shouldToolbarUsesTextFieldTintColor { + toolbar.tintColor = _textField.tintColor + } else if let tintColor = toolbarTintColor { + toolbar.tintColor = tintColor + } else { + toolbar.tintColor = UIColor.black + } + } + } else if let _textView = textField as? UITextView { + + //Bar style according to keyboard appearance + switch _textView.keyboardAppearance { + + case .dark: + toolbar.barStyle = UIBarStyle.black + toolbar.tintColor = UIColor.white + toolbar.barTintColor = nil + default: + toolbar.barStyle = UIBarStyle.default + toolbar.barTintColor = toolbarBarTintColor + + if shouldToolbarUsesTextFieldTintColor { + toolbar.tintColor = _textView.tintColor + } else if let tintColor = toolbarTintColor { + toolbar.tintColor = tintColor + } else { + toolbar.tintColor = UIColor.black + } + } + } + + //Setting toolbar title font. // (Enhancement ID: #30) + if shouldShowToolbarPlaceholder == true && + textField.shouldHideToolbarPlaceholder == false { + + //Updating placeholder font to toolbar. //(Bug ID: #148, #272) + if toolbar.titleBarButton.title == nil || + toolbar.titleBarButton.title != textField.drawingToolbarPlaceholder { + toolbar.titleBarButton.title = textField.drawingToolbarPlaceholder + } + + //Setting toolbar title font. // (Enhancement ID: #30) + if let font = placeholderFont { + toolbar.titleBarButton.titleFont = font + } + + //Setting toolbar title color. // (Enhancement ID: #880) + if let color = placeholderColor { + toolbar.titleBarButton.titleColor = color + } + + //Setting toolbar button title color. // (Enhancement ID: #880) + if let color = placeholderButtonColor { + toolbar.titleBarButton.selectableTitleColor = color + } + + } else { + + toolbar.titleBarButton.title = nil + } + + //In case of UITableView (Special), the next/previous buttons has to be refreshed everytime. (Bug ID: #56) + // If firstTextField, then previous should not be enabled. + if siblings.first == textField { + if (siblings.count == 1) { + textField.keyboardToolbar.previousBarButton.isEnabled = false + textField.keyboardToolbar.nextBarButton.isEnabled = false + } else { + textField.keyboardToolbar.previousBarButton.isEnabled = false + textField.keyboardToolbar.nextBarButton.isEnabled = true + } + } else if siblings.last == textField { // If lastTextField then next should not be enaled. + textField.keyboardToolbar.previousBarButton.isEnabled = true + textField.keyboardToolbar.nextBarButton.isEnabled = false + } else { + textField.keyboardToolbar.previousBarButton.isEnabled = true + textField.keyboardToolbar.nextBarButton.isEnabled = true + } + } + } + } + } + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + /** Remove any toolbar if it is IQToolbar. */ + private func removeToolbarIfRequired() { // (Bug ID: #18) + + let startTime = CACurrentMediaTime() + showLog("****** \(#function) started ******") + + // Getting all the sibling textFields. + if let siblings = responderViews() { + + showLog("Found \(siblings.count) responder sibling(s)") + + for view in siblings { + + if let toolbar = view.inputAccessoryView as? IQToolbar { + + //setInputAccessoryView: check (Bug ID: #307) + if view.responds(to: #selector(setter: UITextField.inputAccessoryView)) && + (toolbar.tag == IQKeyboardManager.kIQDoneButtonToolbarTag || toolbar.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag) { + + if let textField = view as? UITextField { + textField.inputAccessoryView = nil + textField.reloadInputViews() + } else if let textView = view as? UITextView { + textView.inputAccessoryView = nil + textView.reloadInputViews() + } + } + } + } + } + + let elapsedTime = CACurrentMediaTime() - startTime + showLog("****** \(#function) ended: \(elapsedTime) seconds ******\n") + } + + /** reloadInputViews to reload toolbar buttons enable/disable state on the fly Enhancement ID #434. */ + @objc public func reloadInputViews() { + + //If enabled then adding toolbar. + if privateIsEnableAutoToolbar() == true { + self.addToolbarIfRequired() + } else { + self.removeToolbarIfRequired() + } + } + + ///------------------------------------ + /// MARK: Debugging & Developer options + ///------------------------------------ + + @objc public var enableDebugging = false + + /** + @warning Use below methods to completely enable/disable notifications registered by library internally. Please keep in mind that library is totally dependent on NSNotification of UITextField, UITextField, Keyboard etc. If you do unregisterAllNotifications then library will not work at all. You should only use below methods if you want to completedly disable all library functions. You should use below methods at your own risk. + */ + @objc public func registerAllNotifications() { + + #if swift(>=4.2) + let UIKeyboardWillShow = UIResponder.keyboardWillShowNotification + let UIKeyboardDidShow = UIResponder.keyboardDidShowNotification + let UIKeyboardWillHide = UIResponder.keyboardWillHideNotification + let UIKeyboardDidHide = UIResponder.keyboardDidHideNotification + + let UITextFieldTextDidBeginEditing = UITextField.textDidBeginEditingNotification + let UITextFieldTextDidEndEditing = UITextField.textDidEndEditingNotification + + let UITextViewTextDidBeginEditing = UITextView.textDidBeginEditingNotification + let UITextViewTextDidEndEditing = UITextView.textDidEndEditingNotification + + let UIApplicationWillChangeStatusBarOrientation = UIApplication.willChangeStatusBarOrientationNotification + #else + let UIKeyboardWillShow = Notification.Name.UIKeyboardWillShow + let UIKeyboardDidShow = Notification.Name.UIKeyboardDidShow + let UIKeyboardWillHide = Notification.Name.UIKeyboardWillHide + let UIKeyboardDidHide = Notification.Name.UIKeyboardDidHide + + let UITextFieldTextDidBeginEditing = Notification.Name.UITextFieldTextDidBeginEditing + let UITextFieldTextDidEndEditing = Notification.Name.UITextFieldTextDidEndEditing + + let UITextViewTextDidBeginEditing = Notification.Name.UITextViewTextDidBeginEditing + let UITextViewTextDidEndEditing = Notification.Name.UITextViewTextDidEndEditing + + let UIApplicationWillChangeStatusBarOrientation = Notification.Name.UIApplicationWillChangeStatusBarOrientation + #endif + + // Registering for keyboard notification. + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: UIKeyboardWillShow, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(_:)), name: UIKeyboardDidShow, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIKeyboardWillHide, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(_:)), name: UIKeyboardDidHide, object: nil) + + // Registering for UITextField notification. + registerTextFieldViewClass(UITextField.self, didBeginEditingNotificationName: UITextFieldTextDidBeginEditing.rawValue, didEndEditingNotificationName: UITextFieldTextDidEndEditing.rawValue) + + // Registering for UITextView notification. + registerTextFieldViewClass(UITextView.self, didBeginEditingNotificationName: UITextViewTextDidBeginEditing.rawValue, didEndEditingNotificationName: UITextViewTextDidEndEditing.rawValue) + + // Registering for orientation changes notification + NotificationCenter.default.addObserver(self, selector: #selector(self.willChangeStatusBarOrientation(_:)), name: UIApplicationWillChangeStatusBarOrientation, object: UIApplication.shared) + } + + @objc public func unregisterAllNotifications() { + + #if swift(>=4.2) + let UIKeyboardWillShow = UIResponder.keyboardWillShowNotification + let UIKeyboardDidShow = UIResponder.keyboardDidShowNotification + let UIKeyboardWillHide = UIResponder.keyboardWillHideNotification + let UIKeyboardDidHide = UIResponder.keyboardDidHideNotification + + let UITextFieldTextDidBeginEditing = UITextField.textDidBeginEditingNotification + let UITextFieldTextDidEndEditing = UITextField.textDidEndEditingNotification + + let UITextViewTextDidBeginEditing = UITextView.textDidBeginEditingNotification + let UITextViewTextDidEndEditing = UITextView.textDidEndEditingNotification + + let UIApplicationWillChangeStatusBarOrientation = UIApplication.willChangeStatusBarOrientationNotification + #else + let UIKeyboardWillShow = Notification.Name.UIKeyboardWillShow + let UIKeyboardDidShow = Notification.Name.UIKeyboardDidShow + let UIKeyboardWillHide = Notification.Name.UIKeyboardWillHide + let UIKeyboardDidHide = Notification.Name.UIKeyboardDidHide + + let UITextFieldTextDidBeginEditing = Notification.Name.UITextFieldTextDidBeginEditing + let UITextFieldTextDidEndEditing = Notification.Name.UITextFieldTextDidEndEditing + + let UITextViewTextDidBeginEditing = Notification.Name.UITextViewTextDidBeginEditing + let UITextViewTextDidEndEditing = Notification.Name.UITextViewTextDidEndEditing + + let UIApplicationWillChangeStatusBarOrientation = Notification.Name.UIApplicationWillChangeStatusBarOrientation + #endif + + // Unregistering for keyboard notification. + NotificationCenter.default.removeObserver(self, name: UIKeyboardWillShow, object: nil) + NotificationCenter.default.removeObserver(self, name: UIKeyboardDidShow, object: nil) + NotificationCenter.default.removeObserver(self, name: UIKeyboardWillHide, object: nil) + NotificationCenter.default.removeObserver(self, name: UIKeyboardDidHide, object: nil) + + // Unregistering for UITextField notification. + unregisterTextFieldViewClass(UITextField.self, didBeginEditingNotificationName: UITextFieldTextDidBeginEditing.rawValue, didEndEditingNotificationName: UITextFieldTextDidEndEditing.rawValue) + + // Unregistering for UITextView notification. + unregisterTextFieldViewClass(UITextView.self, didBeginEditingNotificationName: UITextViewTextDidBeginEditing.rawValue, didEndEditingNotificationName: UITextViewTextDidEndEditing.rawValue) + + // Unregistering for orientation changes notification + NotificationCenter.default.removeObserver(self, name: UIApplicationWillChangeStatusBarOrientation, object: UIApplication.shared) + } + + private func showLog(_ logString: String) { + + if enableDebugging { + print("IQKeyboardManager: " + logString) + } + } +} + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift new file mode 100644 index 00000000..cd85eccb --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift @@ -0,0 +1,628 @@ +// +// IQKeyboardReturnKeyHandler.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import Foundation +import UIKit + +fileprivate class IQTextFieldViewInfoModal : NSObject { + + fileprivate weak var textFieldDelegate: UITextFieldDelegate? + fileprivate weak var textViewDelegate: UITextViewDelegate? + fileprivate weak var textFieldView: UIView? + fileprivate var originalReturnKeyType = UIReturnKeyType.default + + init(textFieldView : UIView?, textFieldDelegate : UITextFieldDelegate?, textViewDelegate : UITextViewDelegate?, originalReturnKeyType : UIReturnKeyType = .default) { + self.textFieldView = textFieldView + self.textFieldDelegate = textFieldDelegate + self.textViewDelegate = textViewDelegate + self.originalReturnKeyType = originalReturnKeyType + } +} + +/** +Manages the return key to work like next/done in a view hierarchy. +*/ +public class IQKeyboardReturnKeyHandler: NSObject , UITextFieldDelegate, UITextViewDelegate { + + + ///--------------- + /// MARK: Settings + ///--------------- + + /** + Delegate of textField/textView. + */ + @objc public weak var delegate: (UITextFieldDelegate & UITextViewDelegate)? + + /** + Set the last textfield return key type. Default is UIReturnKeyDefault. + */ + @objc public var lastTextFieldReturnKeyType : UIReturnKeyType = UIReturnKeyType.default { + + didSet { + + for modal in textFieldInfoCache { + + if let view = modal.textFieldView { + updateReturnKeyTypeOnTextField(view) + } + } + } + } + + ///-------------------------------------- + /// MARK: Initialization/Deinitialization + ///-------------------------------------- + + @objc public override init() { + super.init() + } + + /** + Add all the textFields available in UIViewController's view. + */ + @objc public init(controller : UIViewController) { + super.init() + + addResponderFromView(controller.view) + } + + deinit { + + for modal in textFieldInfoCache { + + if let textField = modal.textFieldView as? UITextField { + textField.returnKeyType = modal.originalReturnKeyType + + textField.delegate = modal.textFieldDelegate + + } else if let textView = modal.textFieldView as? UITextView { + + textView.returnKeyType = modal.originalReturnKeyType + + textView.delegate = modal.textViewDelegate + } + } + + textFieldInfoCache.removeAll() + } + + + ///------------------------ + /// MARK: Private variables + ///------------------------ + private var textFieldInfoCache = [IQTextFieldViewInfoModal]() + + ///------------------------ + /// MARK: Private Functions + ///------------------------ + private func textFieldViewCachedInfo(_ textField : UIView) -> IQTextFieldViewInfoModal? { + + for modal in textFieldInfoCache { + + if let view = modal.textFieldView { + + if view == textField { + return modal + } + } + } + + return nil + } + + private func updateReturnKeyTypeOnTextField(_ view : UIView) + { + var superConsideredView : UIView? + + //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347) + for disabledClass in IQKeyboardManager.shared.toolbarPreviousNextAllowedClasses { + + superConsideredView = view.superviewOfClassType(disabledClass) + + if superConsideredView != nil { + break + } + } + + var textFields = [UIView]() + + //If there is a tableView in view's hierarchy, then fetching all it's subview that responds. + if let unwrappedTableView = superConsideredView { // (Enhancement ID: #22) + textFields = unwrappedTableView.deepResponderViews() + } else { //Otherwise fetching all the siblings + + textFields = view.responderSiblings() + + //Sorting textFields according to behaviour + switch IQKeyboardManager.shared.toolbarManageBehaviour { + //If needs to sort it by tag + case .byTag: textFields = textFields.sortedArrayByTag() + //If needs to sort it by Position + case .byPosition: textFields = textFields.sortedArrayByPosition() + default: break + } + } + + if let lastView = textFields.last { + + if let textField = view as? UITextField { + + //If it's the last textField in responder view, else next + textField.returnKeyType = (view == lastView) ? lastTextFieldReturnKeyType : UIReturnKeyType.next + } else if let textView = view as? UITextView { + + //If it's the last textField in responder view, else next + textView.returnKeyType = (view == lastView) ? lastTextFieldReturnKeyType : UIReturnKeyType.next + } + } + } + + + ///---------------------------------------------- + /// MARK: Registering/Unregistering textFieldView + ///---------------------------------------------- + + /** + Should pass UITextField/UITextView intance. Assign textFieldView delegate to self, change it's returnKeyType. + + @param view UITextField/UITextView object to register. + */ + @objc public func addTextFieldView(_ view : UIView) { + + let modal = IQTextFieldViewInfoModal(textFieldView: view, textFieldDelegate: nil, textViewDelegate: nil) + + if let textField = view as? UITextField { + + modal.originalReturnKeyType = textField.returnKeyType + modal.textFieldDelegate = textField.delegate + textField.delegate = self + + } else if let textView = view as? UITextView { + + modal.originalReturnKeyType = textView.returnKeyType + modal.textViewDelegate = textView.delegate + textView.delegate = self + } + + textFieldInfoCache.append(modal) + } + + /** + Should pass UITextField/UITextView intance. Restore it's textFieldView delegate and it's returnKeyType. + + @param view UITextField/UITextView object to unregister. + */ + @objc public func removeTextFieldView(_ view : UIView) { + + if let modal = textFieldViewCachedInfo(view) { + + if let textField = view as? UITextField { + + textField.returnKeyType = modal.originalReturnKeyType + textField.delegate = modal.textFieldDelegate + } else if let textView = view as? UITextView { + + textView.returnKeyType = modal.originalReturnKeyType + textView.delegate = modal.textViewDelegate + } + + if let index = textFieldInfoCache.index(where: { $0.textFieldView == view}) { + + textFieldInfoCache.remove(at: index) + } + } + } + + /** + Add all the UITextField/UITextView responderView's. + + @param view UIView object to register all it's responder subviews. + */ + @objc public func addResponderFromView(_ view : UIView) { + + let textFields = view.deepResponderViews() + + for textField in textFields { + + addTextFieldView(textField) + } + } + + /** + Remove all the UITextField/UITextView responderView's. + + @param view UIView object to unregister all it's responder subviews. + */ + @objc public func removeResponderFromView(_ view : UIView) { + + let textFields = view.deepResponderViews() + + for textField in textFields { + + removeTextFieldView(textField) + } + } + + @discardableResult private func goToNextResponderOrResign(_ view : UIView) -> Bool { + + var superConsideredView : UIView? + + //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347) + for disabledClass in IQKeyboardManager.shared.toolbarPreviousNextAllowedClasses { + + superConsideredView = view.superviewOfClassType(disabledClass) + + if superConsideredView != nil { + break + } + } + + var textFields = [UIView]() + + //If there is a tableView in view's hierarchy, then fetching all it's subview that responds. + if let unwrappedTableView = superConsideredView { // (Enhancement ID: #22) + textFields = unwrappedTableView.deepResponderViews() + } else { //Otherwise fetching all the siblings + + textFields = view.responderSiblings() + + //Sorting textFields according to behaviour + switch IQKeyboardManager.shared.toolbarManageBehaviour { + //If needs to sort it by tag + case .byTag: textFields = textFields.sortedArrayByTag() + //If needs to sort it by Position + case .byPosition: textFields = textFields.sortedArrayByPosition() + default: + break + } + } + + //Getting index of current textField. + if let index = textFields.index(of: view) { + //If it is not last textField. then it's next object becomeFirstResponder. + if index < (textFields.count - 1) { + + let nextTextField = textFields[index+1] + nextTextField.becomeFirstResponder() + return false + } else { + + view.resignFirstResponder() + return true + } + } else { + return true + } + } + + + ///--------------------------------------- + /// MARK: UITextField/UITextView delegates + ///--------------------------------------- + + @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate { + if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldBeginEditing(_:))) { + return unwrapDelegate.textFieldShouldBeginEditing?(textField) == true + } + } + } + + return true + } + + @objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate { + if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:))) { + return unwrapDelegate.textFieldShouldEndEditing?(textField) == true + } + } + } + + return true + } + + @objc public func textFieldDidBeginEditing(_ textField: UITextField) { + updateReturnKeyTypeOnTextField(textField) + + var aDelegate : UITextFieldDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textField) { + aDelegate = modal.textFieldDelegate + } + } + + aDelegate?.textFieldDidBeginEditing?(textField) + } + + @objc public func textFieldDidEndEditing(_ textField: UITextField) { + + var aDelegate : UITextFieldDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textField) { + aDelegate = modal.textFieldDelegate + } + } + + aDelegate?.textFieldDidEndEditing?(textField) + } + + #if swift(>=4.2) + @available(iOS 10.0, *) + @objc public func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) { + + var aDelegate : UITextFieldDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textField) { + aDelegate = modal.textFieldDelegate + } + } + + aDelegate?.textFieldDidEndEditing?(textField, reason: reason) + } + #else + @available(iOS 10.0, *) + @objc public func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) { + + var aDelegate : UITextFieldDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textField) { + aDelegate = modal.textFieldDelegate + } + } + + aDelegate?.textFieldDidEndEditing?(textField, reason: reason) + } + #endif + + @objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate { + if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textField(_:shouldChangeCharactersIn:replacementString:))) { + return unwrapDelegate.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == true + } + } + } + return true + } + + @objc public func textFieldShouldClear(_ textField: UITextField) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate { + if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldClear(_:))) { + return unwrapDelegate.textFieldShouldClear?(textField) == true + } + } + } + + return true + } + + + @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + + var shouldReturn = true + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate { + if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldReturn(_:))) { + shouldReturn = unwrapDelegate.textFieldShouldReturn?(textField) == true + } + } + } + + if shouldReturn == true { + goToNextResponderOrResign(textField) + return true + } else { + return goToNextResponderOrResign(textField) + } + } + + + @objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textViewShouldBeginEditing(_:))) { + return unwrapDelegate.textViewShouldBeginEditing?(textView) == true + } + } + } + + return true + } + + @objc public func textViewShouldEndEditing(_ textView: UITextView) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textViewShouldEndEditing(_:))) { + return unwrapDelegate.textViewShouldEndEditing?(textView) == true + } + } + } + + return true + } + + @objc public func textViewDidBeginEditing(_ textView: UITextView) { + updateReturnKeyTypeOnTextField(textView) + + var aDelegate : UITextViewDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textView) { + aDelegate = modal.textViewDelegate + } + } + + aDelegate?.textViewDidBeginEditing?(textView) + } + + @objc public func textViewDidEndEditing(_ textView: UITextView) { + + var aDelegate : UITextViewDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textView) { + aDelegate = modal.textViewDelegate + } + } + + aDelegate?.textViewDidEndEditing?(textView) + } + + @objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + + var shouldReturn = true + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(textView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textView(_:shouldChangeTextIn:replacementText:))) { + shouldReturn = (unwrapDelegate.textView?(textView, shouldChangeTextIn: range, replacementText: text)) == true + } + } + } + + if shouldReturn == true && text == "\n" { + shouldReturn = goToNextResponderOrResign(textView) + } + + return shouldReturn + } + + @objc public func textViewDidChange(_ textView: UITextView) { + + var aDelegate : UITextViewDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textView) { + aDelegate = modal.textViewDelegate + } + } + + aDelegate?.textViewDidChange?(textView) + } + + @objc public func textViewDidChangeSelection(_ textView: UITextView) { + + var aDelegate : UITextViewDelegate? = delegate + + if aDelegate == nil { + + if let modal = textFieldViewCachedInfo(textView) { + aDelegate = modal.textViewDelegate + } + } + + aDelegate?.textViewDidChangeSelection?(textView) + } + + @available(iOS 10.0, *) + @objc public func textView(_ aTextView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(textView as (UITextView, URL, NSRange, UITextItemInteraction) -> Bool)) { + return unwrapDelegate.textView?(aTextView, shouldInteractWith: URL, in: characterRange, interaction: interaction) == true + } + } + } + + return true + } + + @available(iOS 10.0, *) + @objc public func textView(_ aTextView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSTextAttachment, NSRange, UITextItemInteraction) -> Bool)) { + return unwrapDelegate.textView?(aTextView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) == true + } + } + } + + return true + } + + @available(iOS, deprecated: 10.0) + @objc public func textView(_ aTextView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(textView as (UITextView, URL, NSRange) -> Bool)) { + return unwrapDelegate.textView?(aTextView, shouldInteractWith: URL, in: characterRange) == true + } + } + } + + return true + } + + @available(iOS, deprecated: 10.0) + @objc public func textView(_ aTextView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool { + + if delegate == nil { + + if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate { + if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSTextAttachment, NSRange) -> Bool)) { + return unwrapDelegate.textView?(aTextView, shouldInteractWith: textAttachment, in: characterRange) == true + } + } + } + + return true + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift new file mode 100644 index 00000000..3f7ec316 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift @@ -0,0 +1,209 @@ +// +// IQTextView.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import UIKit + +/** @abstract UITextView with placeholder support */ +open class IQTextView : UITextView { + + @objc required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + #if swift(>=4.2) + let UITextViewTextDidChange = UITextView.textDidChangeNotification + #else + let UITextViewTextDidChange = Notification.Name.UITextViewTextDidChange + #endif + + NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name:UITextViewTextDidChange, object: self) + } + + @objc override public init(frame: CGRect, textContainer: NSTextContainer?) { + super.init(frame: frame, textContainer: textContainer) + + #if swift(>=4.2) + let notificationName = UITextView.textDidChangeNotification + #else + let notificationName = Notification.Name.UITextViewTextDidChange + #endif + + NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: notificationName, object: self) + } + + @objc override open func awakeFromNib() { + super.awakeFromNib() + + #if swift(>=4.2) + let UITextViewTextDidChange = UITextView.textDidChangeNotification + #else + let UITextViewTextDidChange = Notification.Name.UITextViewTextDidChange + #endif + + NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextViewTextDidChange, object: self) + } + + deinit { + placeholderLabel.removeFromSuperview() + NotificationCenter.default.removeObserver(self) + } + + private var placeholderInsets : UIEdgeInsets { + return UIEdgeInsets(top: self.textContainerInset.top, left: self.textContainerInset.left + self.textContainer.lineFragmentPadding, bottom: self.textContainerInset.bottom, right: self.textContainerInset.right + self.textContainer.lineFragmentPadding) + } + + private var placeholderExpectedFrame : CGRect { + let placeholderInsets = self.placeholderInsets + let maxWidth = self.frame.width-placeholderInsets.left-placeholderInsets.right + let expectedSize = placeholderLabel.sizeThatFits(CGSize(width: maxWidth, height: self.frame.height-placeholderInsets.top-placeholderInsets.bottom)) + + return CGRect(x: placeholderInsets.left, y: placeholderInsets.top, width: maxWidth, height: expectedSize.height) + } + + lazy var placeholderLabel: UILabel = { + let label = UILabel() + + label.autoresizingMask = [.flexibleWidth, .flexibleHeight] + label.lineBreakMode = .byWordWrapping + label.numberOfLines = 0 + label.font = self.font + label.textAlignment = self.textAlignment + label.backgroundColor = UIColor.clear + label.textColor = UIColor(white: 0.7, alpha: 1.0) + label.alpha = 0 + self.addSubview(label) + + return label + }() + + /** @abstract To set textView's placeholder text color. */ + @IBInspectable open var placeholderTextColor : UIColor? { + + get { + return placeholderLabel.textColor + } + + set { + placeholderLabel.textColor = newValue + } + } + + /** @abstract To set textView's placeholder text. Default is nil. */ + @IBInspectable open var placeholder : String? { + + get { + return placeholderLabel.text + } + + set { + placeholderLabel.text = newValue + refreshPlaceholder() + } + } + + /** @abstract To set textView's placeholder attributed text. Default is nil. */ + open var attributedPlaceholder: NSAttributedString? { + get { + return placeholderLabel.attributedText + } + + set { + placeholderLabel.attributedText = newValue + refreshPlaceholder() + } + } + + @objc override open func layoutSubviews() { + super.layoutSubviews() + + placeholderLabel.frame = placeholderExpectedFrame + } + + @objc internal func refreshPlaceholder() { + + if !text.isEmpty || !attributedText.string.isEmpty { + placeholderLabel.alpha = 0 + } else { + placeholderLabel.alpha = 1 + } + } + + @objc override open var text: String! { + + didSet { + refreshPlaceholder() + } + } + + open override var attributedText: NSAttributedString! { + + didSet { + refreshPlaceholder() + } + } + + @objc override open var font : UIFont? { + + didSet { + + if let unwrappedFont = font { + placeholderLabel.font = unwrappedFont + } else { + placeholderLabel.font = UIFont.systemFont(ofSize: 12) + } + } + } + + @objc override open var textAlignment: NSTextAlignment + { + didSet { + placeholderLabel.textAlignment = textAlignment + } + } + + @objc override open var delegate : UITextViewDelegate? { + + get { + refreshPlaceholder() + return super.delegate + } + + set { + super.delegate = newValue + } + } + + @objc override open var intrinsicContentSize: CGSize { + guard !hasText else { + return super.intrinsicContentSize + } + + var newSize = super.intrinsicContentSize + let placeholderInsets = self.placeholderInsets + newSize.height = placeholderExpectedFrame.height + placeholderInsets.top + placeholderInsets.bottom + + return newSize + } +} + + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift new file mode 100644 index 00000000..77bd00a9 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift @@ -0,0 +1,136 @@ +// +// IQBarButtonItem.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import UIKit +import Foundation + +open class IQBarButtonItem: UIBarButtonItem { + + private static var _classInitialize: Void = classInitialize() + + @objc public override init() { + _ = IQBarButtonItem._classInitialize + super.init() + } + + @objc public required init?(coder aDecoder: NSCoder) { + _ = IQBarButtonItem._classInitialize + super.init(coder: aDecoder) + } + + + private class func classInitialize() { + + let appearanceProxy = self.appearance() + + #if swift(>=4.2) + let states : [UIControl.State] + #else + let states : [UIControlState] + #endif + + states = [.normal,.highlighted,.disabled,.selected,.application,.reserved] + + for state in states { + + appearanceProxy.setBackgroundImage(nil, for: state, barMetrics: .default) + appearanceProxy.setBackgroundImage(nil, for: state, style: .done, barMetrics: .default) + appearanceProxy.setBackgroundImage(nil, for: state, style: .plain, barMetrics: .default) + appearanceProxy.setBackButtonBackgroundImage(nil, for: state, barMetrics: .default) + } + + appearanceProxy.setTitlePositionAdjustment(UIOffset(), for: .default) + appearanceProxy.setBackgroundVerticalPositionAdjustment(0, for: .default) + appearanceProxy.setBackButtonBackgroundVerticalPositionAdjustment(0, for: .default) + } + + @objc override open var tintColor: UIColor? { + didSet { + + #if swift(>=4.2) + var textAttributes = [NSAttributedString.Key : Any]() + let foregroundColorKey = NSAttributedString.Key.foregroundColor + #elseif swift(>=4) + var textAttributes = [NSAttributedStringKey : Any]() + let foregroundColorKey = NSAttributedStringKey.foregroundColor + #else + var textAttributes = [String:Any]() + let foregroundColorKey = NSForegroundColorAttributeName + #endif + + textAttributes[foregroundColorKey] = tintColor + + #if swift(>=4) + + if let attributes = titleTextAttributes(for: .normal) { + + for (key, value) in attributes { + #if swift(>=4.2) + textAttributes[key] = value + #else + textAttributes[NSAttributedStringKey.init(key)] = value + #endif + } + } + + #else + + if let attributes = titleTextAttributes(for: .normal) { + textAttributes = attributes + } + #endif + + setTitleTextAttributes(textAttributes, for: .normal) + } + } + + /** + Boolean to know if it's a system item or custom item, we are having a limitation that we cannot override a designated initializer, so we are manually setting this property once in initialization + */ + @objc internal var isSystemItem = false + + /** + Additional target & action to do get callback action. Note that setting custom target & selector doesn't affect native functionality, this is just an additional target to get a callback. + + @param target Target object. + @param action Target Selector. + */ + @objc open func setTarget(_ target: AnyObject?, action: Selector?) { + if let target = target, let action = action { + invocation = IQInvocation(target, action) + } else { + invocation = nil + } + } + + /** + Customized Invocation to be called when button is pressed. invocation is internally created using setTarget:action: method. + */ + @objc open var invocation : IQInvocation? + + deinit { + target = nil + invocation = nil + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift new file mode 100644 index 00000000..cb28e131 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift @@ -0,0 +1,45 @@ +// +// IQInvocation.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import UIKit + +public class IQInvocation : NSObject { + public weak var target: AnyObject? + public var action: Selector + + @objc public init(_ target: AnyObject, _ action: Selector) { + self.target = target + self.action = action + } + + @objc public func invoke(from: Any) { + if let target = target { + UIApplication.shared.sendAction(action, to: target, from: from, for: UIEvent()) + } + } + + deinit { + target = nil + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift new file mode 100644 index 00000000..fb6a879e --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift @@ -0,0 +1,28 @@ +// +// IQPreviousNextView.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +public class IQPreviousNextView: UIView { + +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift new file mode 100644 index 00000000..984f80ff --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift @@ -0,0 +1,178 @@ +// +// IQTitleBarButtonItem.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import Foundation +import UIKit + +open class IQTitleBarButtonItem: IQBarButtonItem { + + @objc open var titleFont : UIFont? { + + didSet { + if let unwrappedFont = titleFont { + titleButton?.titleLabel?.font = unwrappedFont + } else { + titleButton?.titleLabel?.font = UIFont.systemFont(ofSize: 13) + } + } + } + + @objc override open var title: String? { + didSet { + titleButton?.setTitle(title, for: .normal) + } + } + + /** + titleColor to be used for displaying button text when displaying title (disabled state). + */ + @objc open var titleColor : UIColor? { + + didSet { + + if let color = titleColor { + titleButton?.setTitleColor(color, for:.disabled) + } else { + titleButton?.setTitleColor(UIColor.lightGray, for:.disabled) + } + } + } + + /** + selectableTitleColor to be used for displaying button text when button is enabled. + */ + @objc open var selectableTitleColor : UIColor? { + + didSet { + + if let color = selectableTitleColor { + titleButton?.setTitleColor(color, for:.normal) + } else { + titleButton?.setTitleColor(UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for:.normal) + } + } + } + + /** + Customized Invocation to be called on title button action. titleInvocation is internally created using setTitleTarget:action: method. + */ + @objc override open var invocation : IQInvocation? { + + didSet { + + if let target = invocation?.target, let action = invocation?.action { + self.isEnabled = true + titleButton?.isEnabled = true + titleButton?.addTarget(target, action: action, for: .touchUpInside) + } else { + self.isEnabled = false + titleButton?.isEnabled = false + titleButton?.removeTarget(nil, action: nil, for: .touchUpInside) + } + } + } + + internal var titleButton : UIButton? + private var _titleView : UIView? + + override init() { + super.init() + } + + @objc public convenience init(title : String?) { + + self.init(title: nil, style: .plain, target: nil, action: nil) + + _titleView = UIView() + _titleView?.backgroundColor = UIColor.clear + + titleButton = UIButton(type: .system) + titleButton?.isEnabled = false + titleButton?.titleLabel?.numberOfLines = 3 + titleButton?.setTitleColor(UIColor.lightGray, for:.disabled) + titleButton?.setTitleColor(UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for:.normal) + titleButton?.backgroundColor = UIColor.clear + titleButton?.titleLabel?.textAlignment = .center + titleButton?.setTitle(title, for: .normal) + titleFont = UIFont.systemFont(ofSize: 13.0) + titleButton?.titleLabel?.font = self.titleFont + _titleView?.addSubview(titleButton!) + +#if swift(>=3.2) + if #available(iOS 11, *) { + + var layoutDefaultLowPriority : UILayoutPriority + var layoutDefaultHighPriority : UILayoutPriority + + #if swift(>=4.0) + let layoutPriorityLowValue = UILayoutPriority.defaultLow.rawValue-1 + let layoutPriorityHighValue = UILayoutPriority.defaultHigh.rawValue-1 + layoutDefaultLowPriority = UILayoutPriority(rawValue: layoutPriorityLowValue) + layoutDefaultHighPriority = UILayoutPriority(rawValue: layoutPriorityHighValue) + #else + layoutDefaultLowPriority = UILayoutPriorityDefaultLow-1 + layoutDefaultHighPriority = UILayoutPriorityDefaultHigh-1 + #endif + + _titleView?.translatesAutoresizingMaskIntoConstraints = false + _titleView?.setContentHuggingPriority(layoutDefaultLowPriority, for: .vertical) + _titleView?.setContentHuggingPriority(layoutDefaultLowPriority, for: .horizontal) + _titleView?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .vertical) + _titleView?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .horizontal) + + titleButton?.translatesAutoresizingMaskIntoConstraints = false + titleButton?.setContentHuggingPriority(layoutDefaultLowPriority, for: .vertical) + titleButton?.setContentHuggingPriority(layoutDefaultLowPriority, for: .horizontal) + titleButton?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .vertical) + titleButton?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .horizontal) + + let top = NSLayoutConstraint.init(item: titleButton!, attribute: .top, relatedBy: .equal, toItem: _titleView, attribute: .top, multiplier: 1, constant: 0) + let bottom = NSLayoutConstraint.init(item: titleButton!, attribute: .bottom, relatedBy: .equal, toItem: _titleView, attribute: .bottom, multiplier: 1, constant: 0) + let leading = NSLayoutConstraint.init(item: titleButton!, attribute: .leading, relatedBy: .equal, toItem: _titleView, attribute: .leading, multiplier: 1, constant: 0) + let trailing = NSLayoutConstraint.init(item: titleButton!, attribute: .trailing, relatedBy: .equal, toItem: _titleView, attribute: .trailing, multiplier: 1, constant: 0) + + _titleView?.addConstraints([top,bottom,leading,trailing]) + } else { + _titleView?.autoresizingMask = [.flexibleWidth,.flexibleHeight] + titleButton?.autoresizingMask = [.flexibleWidth,.flexibleHeight] + } +#else + _titleView?.autoresizingMask = [.flexibleWidth,.flexibleHeight] + titleButton?.autoresizingMask = [.flexibleWidth,.flexibleHeight] +#endif + + customView = _titleView + } + + @objc required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + deinit { + customView = nil + titleButton?.removeTarget(nil, action: nil, for: .touchUpInside) + _titleView = nil + titleButton = nil + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift new file mode 100644 index 00000000..808bb8e9 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift @@ -0,0 +1,369 @@ +// +// IQToolbar.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import UIKit + +/** @abstract IQToolbar for IQKeyboardManager. */ +open class IQToolbar: UIToolbar , UIInputViewAudioFeedback { + + private static var _classInitialize: Void = classInitialize() + + private class func classInitialize() { + + let appearanceProxy = self.appearance() + + appearanceProxy.barTintColor = nil + + let positions : [UIBarPosition] = [.any,.bottom,.top,.topAttached] + + for position in positions { + + appearanceProxy.setBackgroundImage(nil, forToolbarPosition: position, barMetrics: .default) + appearanceProxy.setShadowImage(nil, forToolbarPosition: .any) + } + + //Background color + appearanceProxy.backgroundColor = nil + } + + /** + Previous bar button of toolbar. + */ + private var privatePreviousBarButton: IQBarButtonItem? + @objc open var previousBarButton : IQBarButtonItem { + get { + if privatePreviousBarButton == nil { + privatePreviousBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil) + privatePreviousBarButton?.accessibilityLabel = "Toolbar Previous Button" + } + return privatePreviousBarButton! + } + + set (newValue) { + privatePreviousBarButton = newValue + } + } + + /** + Next bar button of toolbar. + */ + private var privateNextBarButton: IQBarButtonItem? + @objc open var nextBarButton : IQBarButtonItem { + get { + if privateNextBarButton == nil { + privateNextBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil) + privateNextBarButton?.accessibilityLabel = "Toolbar Next Button" + } + return privateNextBarButton! + } + + set (newValue) { + privateNextBarButton = newValue + } + } + + /** + Title bar button of toolbar. + */ + private var privateTitleBarButton: IQTitleBarButtonItem? + @objc open var titleBarButton : IQTitleBarButtonItem { + get { + if privateTitleBarButton == nil { + privateTitleBarButton = IQTitleBarButtonItem(title: nil) + privateTitleBarButton?.accessibilityLabel = "Toolbar Title Button" + } + return privateTitleBarButton! + } + + set (newValue) { + privateTitleBarButton = newValue + } + } + + /** + Done bar button of toolbar. + */ + private var privateDoneBarButton: IQBarButtonItem? + @objc open var doneBarButton : IQBarButtonItem { + get { + if privateDoneBarButton == nil { + privateDoneBarButton = IQBarButtonItem(title: nil, style: .done, target: nil, action: nil) + privateDoneBarButton?.accessibilityLabel = "Toolbar Done Button" + } + return privateDoneBarButton! + } + + set (newValue) { + privateDoneBarButton = newValue + } + } + + /** + Fixed space bar button of toolbar. + */ + private var privateFixedSpaceBarButton: IQBarButtonItem? + @objc open var fixedSpaceBarButton : IQBarButtonItem { + get { + if privateFixedSpaceBarButton == nil { + privateFixedSpaceBarButton = IQBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + } + privateFixedSpaceBarButton!.isSystemItem = true + + if #available(iOS 10, *) { + privateFixedSpaceBarButton!.width = 6 + } else { + privateFixedSpaceBarButton!.width = 20 + } + + return privateFixedSpaceBarButton! + } + + set (newValue) { + privateFixedSpaceBarButton = newValue + } + } + + override init(frame: CGRect) { + _ = IQToolbar._classInitialize + super.init(frame: frame) + + sizeToFit() + + autoresizingMask = .flexibleWidth + self.isTranslucent = true + } + + @objc required public init?(coder aDecoder: NSCoder) { + _ = IQToolbar._classInitialize + super.init(coder: aDecoder) + + sizeToFit() + + autoresizingMask = .flexibleWidth + self.isTranslucent = true + } + + @objc override open func sizeThatFits(_ size: CGSize) -> CGSize { + var sizeThatFit = super.sizeThatFits(size) + sizeThatFit.height = 44 + return sizeThatFit + } + + @objc override open var tintColor: UIColor! { + + didSet { + if let unwrappedItems = items { + for item in unwrappedItems { + item.tintColor = tintColor + } + } + } + } + + @objc override open var barStyle: UIBarStyle { + didSet { + + if titleBarButton.selectableTitleColor == nil { + if barStyle == .default { + titleBarButton.titleButton?.setTitleColor(UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for: .normal) + } else { + titleBarButton.titleButton?.setTitleColor(UIColor.yellow, for: .normal) + } + } + } + } + + @objc override open func layoutSubviews() { + + super.layoutSubviews() + + //If running on Xcode9 (iOS11) only then we'll validate for iOS version, otherwise for older versions of Xcode (iOS10 and below) we'll just execute the tweak +#if swift(>=3.2) + + if #available(iOS 11, *) { + return + } else if let customTitleView = titleBarButton.customView { + var leftRect = CGRect.null + var rightRect = CGRect.null + var isTitleBarButtonFound = false + + let sortedSubviews = self.subviews.sorted(by: { (view1 : UIView, view2 : UIView) -> Bool in + + let x1 = view1.frame.minX + let y1 = view1.frame.minY + let x2 = view2.frame.minX + let y2 = view2.frame.minY + + if x1 != x2 { + return x1 < x2 + } else { + return y1 < y2 + } + }) + + for barButtonItemView in sortedSubviews { + + if isTitleBarButtonFound == true { + rightRect = barButtonItemView.frame + break + } else if barButtonItemView === customTitleView { + isTitleBarButtonFound = true + //If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem) + } else if barButtonItemView.isKind(of: UIControl.self) == true { + leftRect = barButtonItemView.frame + } + } + + let titleMargin : CGFloat = 16 + + let maxWidth : CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX) + let maxHeight = self.frame.height + + let sizeThatFits = customTitleView.sizeThatFits(CGSize(width: maxWidth, height: maxHeight)) + + var titleRect : CGRect + + if sizeThatFits.width > 0 && sizeThatFits.height > 0 { + let width = min(sizeThatFits.width, maxWidth) + let height = min(sizeThatFits.height, maxHeight) + + var x : CGFloat + + if (leftRect.isNull == false) { + x = titleMargin + leftRect.maxX + ((maxWidth - width)/2) + } else { + x = titleMargin + } + + let y = (maxHeight - height)/2 + + titleRect = CGRect(x: x, y: y, width: width, height: height) + } else { + + var x : CGFloat + + if (leftRect.isNull == false) { + x = titleMargin + leftRect.maxX + } else { + x = titleMargin + } + + let width : CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX) + + titleRect = CGRect(x: x, y: 0, width: width, height: maxHeight) + } + + customTitleView.frame = titleRect + } + +#else + if let customTitleView = titleBarButton.customView { + var leftRect = CGRect.null + var rightRect = CGRect.null + var isTitleBarButtonFound = false + + let sortedSubviews = self.subviews.sorted(by: { (view1 : UIView, view2 : UIView) -> Bool in + + let x1 = view1.frame.minX + let y1 = view1.frame.minY + let x2 = view2.frame.minX + let y2 = view2.frame.minY + + if x1 != x2 { + return x1 < x2 + } else { + return y1 < y2 + } + }) + + for barButtonItemView in sortedSubviews { + + if isTitleBarButtonFound == true { + rightRect = barButtonItemView.frame + break + } else if barButtonItemView === titleBarButton.customView { + isTitleBarButtonFound = true + //If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem) + } else if barButtonItemView.isKind(of: UIControl.self) == true { + leftRect = barButtonItemView.frame + } + } + + let titleMargin : CGFloat = 16 + let maxWidth : CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX) + let maxHeight = self.frame.height + + let sizeThatFits = customTitleView.sizeThatFits(CGSize(width: maxWidth, height: maxHeight)) + + var titleRect : CGRect + + if sizeThatFits.width > 0 && sizeThatFits.height > 0 { + let width = min(sizeThatFits.width, maxWidth) + let height = min(sizeThatFits.height, maxHeight) + + var x : CGFloat + + if (leftRect.isNull == false) { + x = titleMargin + leftRect.maxX + ((maxWidth - width)/2) + } else { + x = titleMargin + } + + let y = (maxHeight - height)/2 + + titleRect = CGRect(x: x, y: y, width: width, height: height) + } else { + + var x : CGFloat + + if (leftRect.isNull == false) { + x = titleMargin + leftRect.maxX + } else { + x = titleMargin + } + + let width : CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX) + + titleRect = CGRect(x: x, y: 0, width: width, height: maxHeight) + } + + customTitleView.frame = titleRect + } +#endif + } + + @objc open var enableInputClicksWhenVisible: Bool { + return true + } + + deinit { + + items = nil + privatePreviousBarButton = nil + privateNextBarButton = nil + privateTitleBarButton = nil + privateDoneBarButton = nil + privateFixedSpaceBarButton = nil + } +} diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift new file mode 100644 index 00000000..a59c7fb5 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift @@ -0,0 +1,612 @@ +// +// IQUIView+IQKeyboardToolbar.swift +// https://github.com/hackiftekhar/IQKeyboardManager +// Copyright (c) 2013-16 Iftekhar Qurashi. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import UIKit + +private var kIQShouldHideToolbarPlaceholder = "kIQShouldHideToolbarPlaceholder" +private var kIQToolbarPlaceholder = "kIQToolbarPlaceholder" + +private var kIQKeyboardToolbar = "kIQKeyboardToolbar" + +/** + IQBarButtonItemConfiguration for creating toolbar with bar button items + */ +public class IQBarButtonItemConfiguration : NSObject { + + #if swift(>=4.2) + public init(barButtonSystemItem : UIBarButtonItem.SystemItem, action: Selector) { + self.barButtonSystemItem = barButtonSystemItem + self.image = nil + self.title = nil + self.action = action + super.init() + } + #else + public init(barButtonSystemItem : UIBarButtonSystemItem, action: Selector) { + self.barButtonSystemItem = barButtonSystemItem + self.image = nil + self.title = nil + self.action = action + super.init() + } + #endif + + public init(image : UIImage, action: Selector) { + self.barButtonSystemItem = nil + self.image = image + self.title = nil + self.action = action + super.init() + } + + public init(title : String, action: Selector) { + self.barButtonSystemItem = nil + self.image = nil + self.title = title + self.action = action + super.init() + } + + #if swift(>=4.2) + public let barButtonSystemItem : UIBarButtonItem.SystemItem? //System Item to be used to instantiate bar button. + #else + public let barButtonSystemItem : UIBarButtonSystemItem? //System Item to be used to instantiate bar button. + #endif + + public let image : UIImage? //Image to show on bar button item if it's not a system item. + + public let title : String? //Title to show on bar button item if it's not a system item. + + public let action : Selector? //action for bar button item. Usually 'doneAction:(IQBarButtonItem*)item'. +} + +/** + UIImage category methods to get next/prev images + */ +extension UIImage { + + public static func keyboardPreviousiOS9Image() -> UIImage? { + + struct Static { + static var keyboardPreviousiOS9Image : UIImage? + } + + if Static.keyboardPreviousiOS9Image == nil { + // Get the top level "bundle" which may actually be the framework + var bundle = Bundle(for: IQKeyboardManager.self) + + if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { + if let resourcesBundle = Bundle(path: resourcePath) { + bundle = resourcesBundle + } + } + + Static.keyboardPreviousiOS9Image = UIImage(named: "IQButtonBarArrowLeft", in: bundle, compatibleWith: nil) + + //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) + if #available(iOS 9, *) { + Static.keyboardPreviousiOS9Image = Static.keyboardPreviousiOS9Image?.imageFlippedForRightToLeftLayoutDirection() + } + } + + return Static.keyboardPreviousiOS9Image + } + + public static func keyboardNextiOS9Image() -> UIImage? { + + struct Static { + static var keyboardNextiOS9Image : UIImage? + } + + if Static.keyboardNextiOS9Image == nil { + // Get the top level "bundle" which may actually be the framework + var bundle = Bundle(for: IQKeyboardManager.self) + + if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { + if let resourcesBundle = Bundle(path: resourcePath) { + bundle = resourcesBundle + } + } + + Static.keyboardNextiOS9Image = UIImage(named: "IQButtonBarArrowRight", in: bundle, compatibleWith: nil) + + //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) + if #available(iOS 9, *) { + Static.keyboardNextiOS9Image = Static.keyboardNextiOS9Image?.imageFlippedForRightToLeftLayoutDirection() + } + } + + return Static.keyboardNextiOS9Image + } + + public static func keyboardPreviousiOS10Image() -> UIImage? { + + struct Static { + static var keyboardPreviousiOS10Image : UIImage? + } + + if Static.keyboardPreviousiOS10Image == nil { + // Get the top level "bundle" which may actually be the framework + var bundle = Bundle(for: IQKeyboardManager.self) + + if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { + if let resourcesBundle = Bundle(path: resourcePath) { + bundle = resourcesBundle + } + } + + Static.keyboardPreviousiOS10Image = UIImage(named: "IQButtonBarArrowUp", in: bundle, compatibleWith: nil) + + //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) + if #available(iOS 9, *) { + Static.keyboardPreviousiOS10Image = Static.keyboardPreviousiOS10Image?.imageFlippedForRightToLeftLayoutDirection() + } + } + + return Static.keyboardPreviousiOS10Image + } + + public static func keyboardNextiOS10Image() -> UIImage? { + + struct Static { + static var keyboardNextiOS10Image : UIImage? + } + + if Static.keyboardNextiOS10Image == nil { + // Get the top level "bundle" which may actually be the framework + var bundle = Bundle(for: IQKeyboardManager.self) + + if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { + if let resourcesBundle = Bundle(path: resourcePath) { + bundle = resourcesBundle + } + } + + Static.keyboardNextiOS10Image = UIImage(named: "IQButtonBarArrowDown", in: bundle, compatibleWith: nil) + + //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) + if #available(iOS 9, *) { + Static.keyboardNextiOS10Image = Static.keyboardNextiOS10Image?.imageFlippedForRightToLeftLayoutDirection() + } + } + + return Static.keyboardNextiOS10Image + } + + public static func keyboardPreviousImage() -> UIImage? { + + if #available(iOS 10, *) { + return keyboardPreviousiOS10Image() + } else { + return keyboardPreviousiOS9Image() + } + } + + public static func keyboardNextImage() -> UIImage? { + + if #available(iOS 10, *) { + return keyboardNextiOS10Image() + } else { + return keyboardNextiOS9Image() + } + } +} + +/** +UIView category methods to add IQToolbar on UIKeyboard. +*/ +public extension UIView { + + ///-------------- + /// MARK: Toolbar + ///-------------- + + /** + IQToolbar references for better customization control. + */ + public var keyboardToolbar: IQToolbar { + var toolbar = inputAccessoryView as? IQToolbar + + if (toolbar == nil) + { + toolbar = objc_getAssociatedObject(self, &kIQKeyboardToolbar) as? IQToolbar + } + + if let unwrappedToolbar = toolbar { + + return unwrappedToolbar + + } else { + + let newToolbar = IQToolbar() + + objc_setAssociatedObject(self, &kIQKeyboardToolbar, newToolbar, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + return newToolbar + } + } + + ///-------------------- + /// MARK: Toolbar title + ///-------------------- + + /** + If `shouldHideToolbarPlaceholder` is YES, then title will not be added to the toolbar. Default to NO. + */ + @objc public var shouldHideToolbarPlaceholder: Bool { + get { + let aValue = objc_getAssociatedObject(self, &kIQShouldHideToolbarPlaceholder) as Any? + + if let unwrapedValue = aValue as? Bool { + return unwrapedValue + } else { + return false + } + } + set(newValue) { + objc_setAssociatedObject(self, &kIQShouldHideToolbarPlaceholder, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder + } + } + + /** + `toolbarPlaceholder` to override default `placeholder` text when drawing text on toolbar. + */ + @objc public var toolbarPlaceholder: String? { + get { + let aValue = objc_getAssociatedObject(self, &kIQToolbarPlaceholder) as? String + + return aValue + } + set(newValue) { + objc_setAssociatedObject(self, &kIQToolbarPlaceholder, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder + } + } + + /** + `drawingToolbarPlaceholder` will be actual text used to draw on toolbar. This would either `placeholder` or `toolbarPlaceholder`. + */ + @objc public var drawingToolbarPlaceholder: String? { + + if (self.shouldHideToolbarPlaceholder) + { + return nil + } + else if (self.toolbarPlaceholder?.isEmpty == false) { + return self.toolbarPlaceholder + } + else if self.responds(to: #selector(getter: UITextField.placeholder)) { + + if let textField = self as? UITextField { + return textField.placeholder + } else if let textView = self as? IQTextView { + return textView.placeholder + } else { + return nil + } + } + else { + return nil + } + } + + ///--------------------- + /// MARK: Private helper + ///--------------------- + + private static func flexibleBarButtonItem () -> IQBarButtonItem { + + struct Static { + + static let nilButton = IQBarButtonItem(barButtonSystemItem:.flexibleSpace, target: nil, action: nil) + } + + Static.nilButton.isSystemItem = true + return Static.nilButton + } + + ///------------- + /// MARK: Common + ///------------- + + @objc public func addKeyboardToolbarWithTarget(target : AnyObject?, titleText : String?, rightBarButtonConfiguration:IQBarButtonItemConfiguration?, previousBarButtonConfiguration:IQBarButtonItemConfiguration? = nil, nextBarButtonConfiguration:IQBarButtonItemConfiguration? = nil) { + + //If can't set InputAccessoryView. Then return + if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { + + // Creating a toolBar for phoneNumber keyboard + let toolbar = self.keyboardToolbar + + var items : [IQBarButtonItem] = [] + + if let prevConfig = previousBarButtonConfiguration { + + var prev = toolbar.previousBarButton + + if prevConfig.barButtonSystemItem == nil && prev.isSystemItem == false { + prev.title = prevConfig.title + prev.image = prevConfig.image + prev.target = target + prev.action = prevConfig.action + } else { + if let systemItem = prevConfig.barButtonSystemItem { + prev = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: prevConfig.action) + prev.isSystemItem = true + } else if let image = prevConfig.image { + prev = IQBarButtonItem(image: image, style: .plain, target: target, action: prevConfig.action) + } else { + prev = IQBarButtonItem(title: prevConfig.title, style: .plain, target: target, action: prevConfig.action) + } + + prev.invocation = toolbar.previousBarButton.invocation + prev.accessibilityLabel = toolbar.previousBarButton.accessibilityLabel + toolbar.previousBarButton = prev + } + + items.append(prev) + } + + if previousBarButtonConfiguration != nil && nextBarButtonConfiguration != nil { + + items.append(toolbar.fixedSpaceBarButton) + } + + if let nextConfig = nextBarButtonConfiguration { + + var next = toolbar.nextBarButton + + if nextConfig.barButtonSystemItem == nil && next.isSystemItem == false { + next.title = nextConfig.title + next.image = nextConfig.image + next.target = target + next.action = nextConfig.action + } else { + if let systemItem = nextConfig.barButtonSystemItem { + next = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: nextConfig.action) + next.isSystemItem = true + } else if let image = nextConfig.image { + next = IQBarButtonItem(image: image, style: .plain, target: target, action: nextConfig.action) + } else { + next = IQBarButtonItem(title: nextConfig.title, style: .plain, target: target, action: nextConfig.action) + } + + next.invocation = toolbar.nextBarButton.invocation + next.accessibilityLabel = toolbar.nextBarButton.accessibilityLabel + toolbar.nextBarButton = next + } + + items.append(next) + } + + //Title bar button item + do { + //Flexible space + items.append(UIView.flexibleBarButtonItem()) + + //Title button + toolbar.titleBarButton.title = titleText + + #if swift(>=3.2) + if #available(iOS 11, *) {} + else { + toolbar.titleBarButton.customView?.frame = CGRect.zero + } + #else + toolbar.titleBarButton.customView?.frame = CGRect.zero + #endif + + items.append(toolbar.titleBarButton) + + //Flexible space + items.append(UIView.flexibleBarButtonItem()) + } + + if let rightConfig = rightBarButtonConfiguration { + + var done = toolbar.doneBarButton + + if rightConfig.barButtonSystemItem == nil && done.isSystemItem == false { + done.title = rightConfig.title + done.image = rightConfig.image + done.target = target + done.action = rightConfig.action + } else { + if let systemItem = rightConfig.barButtonSystemItem { + done = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: rightConfig.action) + done.isSystemItem = true + } else if let image = rightConfig.image { + done = IQBarButtonItem(image: image, style: .plain, target: target, action: rightConfig.action) + } else { + done = IQBarButtonItem(title: rightConfig.title, style: .plain, target: target, action: rightConfig.action) + } + + done.invocation = toolbar.doneBarButton.invocation + done.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel + toolbar.doneBarButton = done + } + + items.append(done) + } + + // Adding button to toolBar. + toolbar.items = items + + // Setting toolbar to keyboard. + if let textField = self as? UITextField { + textField.inputAccessoryView = toolbar + + switch textField.keyboardAppearance { + case .dark: + toolbar.barStyle = UIBarStyle.black + default: + toolbar.barStyle = UIBarStyle.default + } + } else if let textView = self as? UITextView { + textView.inputAccessoryView = toolbar + + switch textView.keyboardAppearance { + case .dark: + toolbar.barStyle = UIBarStyle.black + default: + toolbar.barStyle = UIBarStyle.default + } + } + } + } + + ///------------ + /// MARK: Right + ///------------ + + @objc public func addDoneOnKeyboardWithTarget(_ target : AnyObject?, action : Selector, shouldShowPlaceholder: Bool = false) { + + addDoneOnKeyboardWithTarget(target, action: action, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addDoneOnKeyboardWithTarget(_ target : AnyObject?, action : Selector, titleText: String?) { + + let rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: action) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration) + } + + + @objc public func addRightButtonOnKeyboardWithImage(_ image : UIImage, target : AnyObject?, action : Selector, shouldShowPlaceholder: Bool = false) { + + addRightButtonOnKeyboardWithImage(image, target: target, action: action, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addRightButtonOnKeyboardWithImage(_ image : UIImage, target : AnyObject?, action : Selector, titleText: String?) { + + let rightConfiguration = IQBarButtonItemConfiguration(image: image, action: action) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration) + } + + + @objc public func addRightButtonOnKeyboardWithText(_ text : String, target : AnyObject?, action : Selector, shouldShowPlaceholder: Bool = false) { + + addRightButtonOnKeyboardWithText(text, target: target, action: action, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addRightButtonOnKeyboardWithText(_ text : String, target : AnyObject?, action : Selector, titleText: String?) { + + let rightConfiguration = IQBarButtonItemConfiguration(title: text, action: action) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration) + } + + ///----------------- + /// MARK: Right/Left + ///----------------- + + @objc public func addCancelDoneOnKeyboardWithTarget(_ target : AnyObject?, cancelAction : Selector, doneAction : Selector, shouldShowPlaceholder: Bool = false) { + + addCancelDoneOnKeyboardWithTarget(target, cancelAction: cancelAction, doneAction: doneAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addRightLeftOnKeyboardWithTarget(_ target : AnyObject?, leftButtonTitle : String, rightButtonTitle : String, leftButtonAction : Selector, rightButtonAction : Selector, shouldShowPlaceholder: Bool = false) { + + addRightLeftOnKeyboardWithTarget(target, leftButtonTitle: leftButtonTitle, rightButtonTitle: rightButtonTitle, leftButtonAction: leftButtonAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addRightLeftOnKeyboardWithTarget(_ target : AnyObject?, leftButtonImage : UIImage, rightButtonImage : UIImage, leftButtonAction : Selector, rightButtonAction : Selector, shouldShowPlaceholder: Bool = false) { + + addRightLeftOnKeyboardWithTarget(target, leftButtonImage: leftButtonImage, rightButtonImage: rightButtonImage, leftButtonAction: leftButtonAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addCancelDoneOnKeyboardWithTarget(_ target : AnyObject?, cancelAction : Selector, doneAction : Selector, titleText: String?) { + + let leftConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .cancel, action: cancelAction) + let rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done , action: doneAction) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: leftConfiguration) + } + + @objc public func addRightLeftOnKeyboardWithTarget(_ target : AnyObject?, leftButtonTitle : String, rightButtonTitle : String, leftButtonAction : Selector, rightButtonAction : Selector, titleText: String?) { + + let leftConfiguration = IQBarButtonItemConfiguration(title: leftButtonTitle, action: leftButtonAction) + let rightConfiguration = IQBarButtonItemConfiguration(title: rightButtonTitle, action: rightButtonAction) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: leftConfiguration) + } + + @objc public func addRightLeftOnKeyboardWithTarget(_ target : AnyObject?, leftButtonImage : UIImage, rightButtonImage : UIImage, leftButtonAction : Selector, rightButtonAction : Selector, titleText: String?) { + + let leftConfiguration = IQBarButtonItemConfiguration(image: leftButtonImage, action: leftButtonAction) + let rightConfiguration = IQBarButtonItemConfiguration(image: rightButtonImage, action: rightButtonAction) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: leftConfiguration) + } + + ///-------------------------- + /// MARK: Previous/Next/Right + ///-------------------------- + + @objc public func addPreviousNextDoneOnKeyboardWithTarget (_ target : AnyObject?, previousAction : Selector, nextAction : Selector, doneAction : Selector, shouldShowPlaceholder: Bool = false) { + + addPreviousNextDoneOnKeyboardWithTarget(target, previousAction: previousAction, nextAction: nextAction, doneAction: doneAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addPreviousNextRightOnKeyboardWithTarget(_ target : AnyObject?, rightButtonImage : UIImage, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, shouldShowPlaceholder : Bool = false) { + + addPreviousNextRightOnKeyboardWithTarget(target, rightButtonImage: rightButtonImage, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addPreviousNextRightOnKeyboardWithTarget(_ target : AnyObject?, rightButtonTitle : String, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, shouldShowPlaceholder : Bool = false) { + + addPreviousNextRightOnKeyboardWithTarget(target, rightButtonTitle: rightButtonTitle, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder : nil)) + } + + @objc public func addPreviousNextDoneOnKeyboardWithTarget (_ target : AnyObject?, previousAction : Selector, nextAction : Selector, doneAction : Selector, titleText: String?) { + + let rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: doneAction) + let nextConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardNextImage() ?? UIImage(), action: nextAction) + let prevConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardPreviousImage() ?? UIImage(), action: previousAction) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration) + } + + @objc public func addPreviousNextRightOnKeyboardWithTarget(_ target : AnyObject?, rightButtonImage : UIImage, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, titleText : String?) { + + let rightConfiguration = IQBarButtonItemConfiguration(image: rightButtonImage, action: rightButtonAction) + let nextConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardNextImage() ?? UIImage(), action: nextAction) + let prevConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardPreviousImage() ?? UIImage(), action: previousAction) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration) + } + + @objc public func addPreviousNextRightOnKeyboardWithTarget(_ target : AnyObject?, rightButtonTitle : String, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, titleText : String?) { + + let rightConfiguration = IQBarButtonItemConfiguration(title: rightButtonTitle, action: rightButtonAction) + let nextConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardNextImage() ?? UIImage(), action: nextAction) + let prevConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardPreviousImage() ?? UIImage(), action: previousAction) + + addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration) + } +} + diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowDown@2x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowDown@2x.png new file mode 100644 index 00000000..81db2ed2 Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowDown@2x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowDown@3x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowDown@3x.png new file mode 100644 index 00000000..dd341229 Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowDown@3x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowLeft@2x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowLeft@2x.png new file mode 100644 index 00000000..cfc40d6e Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowLeft@2x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowLeft@3x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowLeft@3x.png new file mode 100644 index 00000000..849b9913 Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowLeft@3x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowRight@2x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowRight@2x.png new file mode 100644 index 00000000..c8b9a874 Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowRight@2x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowRight@3x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowRight@3x.png new file mode 100644 index 00000000..95c43973 Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowRight@3x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowUp@2x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowUp@2x.png new file mode 100644 index 00000000..8ec96a9d Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowUp@2x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowUp@3x.png b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowUp@3x.png new file mode 100644 index 00000000..9304f50f Binary files /dev/null and b/Santander-Test/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle/IQButtonBarArrowUp@3x.png differ diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/LICENSE.md b/Santander-Test/Pods/IQKeyboardManagerSwift/LICENSE.md new file mode 100644 index 00000000..c17c1077 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2017 Iftekhar Qurashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Santander-Test/Pods/IQKeyboardManagerSwift/README.md b/Santander-Test/Pods/IQKeyboardManagerSwift/README.md new file mode 100644 index 00000000..b3900c02 --- /dev/null +++ b/Santander-Test/Pods/IQKeyboardManagerSwift/README.md @@ -0,0 +1,210 @@ +

+ Icon +

+

IQKeyboardManager

+

+ GitHub license + + +[![Build Status](https://travis-ci.org/hackiftekhar/IQKeyboardManager.svg)](https://travis-ci.org/hackiftekhar/IQKeyboardManager) + + +Often while developing an app, We ran into an issues where the iPhone keyboard slide up and cover the `UITextField/UITextView`. `IQKeyboardManager` allows you to prevent issues of the keyboard sliding up and cover `UITextField/UITextView` without needing you to enter any code and no additional setup required. To use `IQKeyboardManager` you simply need to add source files to your project. + + +#### Key Features + +1) `**CODELESS**, Zero Lines Of Code` + +2) `Works Automatically` + +3) `No More UIScrollView` + +4) `No More Subclasses` + +5) `No More Manual Work` + +6) `No More #imports` + +`IQKeyboardManager` works on all orientations, and with the toolbar. There are also nice optional features allowing you to customize the distance from the text field, add the next/previous done button as a keyboard UIToolbar, play sounds when the user navigations through the form and more. + + +## Screenshot +[![IQKeyboardManager](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerScreenshot.png)](http://youtu.be/6nhLw6hju2A) +[![Settings](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerSettings.png)](http://youtu.be/6nhLw6hju2A) + +## GIF animation +[![IQKeyboardManager](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManager.gif)](http://youtu.be/6nhLw6hju2A) + +## Video + + + +## Tutorial video by @rebeloper ([#1135](https://github.com/hackiftekhar/IQKeyboardManager/issues/1135)) + +@rebeloper demonstrated two videos on how to implement **IQKeyboardManager** at it's core: + + + +https://www.youtube.com/playlist?list=PL_csAAO9PQ8aTL87XnueOXi3RpWE2m_8v + +## Warning + +- **If you're planning to build SDK/library/framework and wants to handle UITextField/UITextView with IQKeyboardManager then you're totally going on wrong way.** I would never suggest to add **IQKeyboardManager** as **dependency/adding/shipping** with any third-party library, instead of adding **IQKeyboardManager** you should implement your own solution to achieve same kind of results. **IQKeyboardManager** is totally designed for projects to help developers for their convenience, it's not designed for **adding/dependency/shipping** with any **third-party library**, because **doing this could block adoption by other developers for their projects as well(who are not using IQKeyboardManager and implemented their custom solution to handle UITextField/UITextView thought the project).** +- If **IQKeyboardManager** conflicts with other **third-party library**, then it's **developer responsibility** to **enable/disable IQKeyboardManager** when **presenting/dismissing** third-party library UI. Third-party libraries are not responsible to handle IQKeyboardManager. + +## Requirements +[![Platform iOS](https://img.shields.io/badge/Platform-iOS-blue.svg?style=fla)]() + +| | Language | Minimum iOS Target | Minimum Xcode Version | +|------------------------|----------|--------------------|-----------------------| +| IQKeyboardManager | Obj-C | iOS 8.0 | Xcode 8.2.1 | +| IQKeyboardManagerSwift | Swift | iOS 8.0 | Xcode 8.2.1 | +| Demo Project | | | Xcode 9.3 | + +**Note** +- 3.3.7 is the last iOS 7 supported version. + +#### Swift versions support + +| Swift | Xcode | IQKeyboardManagerSwift | +|-------------------|-------|------------------------| +| 4.2, 4.0, 3.2, 3.0| 10.0 | >= 6.0.4 | +| 4.0, 3.2, 3.0 | 9.0 | 5.0.0 | +| 3.1 | 8.3 | 4.0.10 | +| 3.0 (3.0.2) | 8.2 | 4.0.8 | +| 2.2 or 2.3 | 7.3 | 4.0.5 | +| 2.1.1 | 7.2 | 4.0.0 | +| 2.1 | 7.2 | 3.3.7 | +| 2.0 | 7.0 | 3.3.3.1 | +| 1.2 | 6.3 | 3.3.1 | +| 1.0 | 6.0 | 3.3.2 | + +Installation +========================== + +#### Installation with CocoaPods + +[![CocoaPods](https://img.shields.io/cocoapods/v/IQKeyboardManager.svg)](http://cocoadocs.org/docsets/IQKeyboardManager) + +***IQKeyboardManager (Objective-C):*** IQKeyboardManager is available through [CocoaPods](http://cocoapods.org), to install +it simply add the following line to your Podfile: ([#9](https://github.com/hackiftekhar/IQKeyboardManager/issues/9)) + +```ruby +pod 'IQKeyboardManager' #iOS8 and later + +pod 'IQKeyboardManager', '3.3.7' #iOS7 +``` + +***IQKeyboardManager (Swift):*** IQKeyboardManagerSwift is available through [CocoaPods](http://cocoapods.org), to install +it simply add the following line to your Podfile: ([#236](https://github.com/hackiftekhar/IQKeyboardManager/issues/236)) + +*Swift 4.2, 4.0, 3.2, 3.0 (Xcode 9)* + +```ruby +pod 'IQKeyboardManagerSwift' +``` + +*Or you can choose version you need based on Swift support table from [Requirements](README.md#requirements)* + +```ruby +pod 'IQKeyboardManagerSwift', '5.0.0' +``` + +In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager. + +```swift +import IQKeyboardManagerSwift + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + + IQKeyboardManager.shared.enable = true + + return true + } +} +``` + +#### Installation with Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. + +You can install Carthage with [Homebrew](http://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate `IQKeyboardManger` or `IQKeyboardManagerSwift` into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "hackiftekhar/IQKeyboardManager" +``` + +Run `carthage` to build the frameworks and drag the appropriate framework (`IQKeyboardManager.framework` or `IQKeyboardManagerSwift.framework`) into your Xcode project according to your need. Make sure to add only one framework and not both. + + +#### Installation with Source Code + +[![Github tag](https://img.shields.io/github/tag/hackiftekhar/iqkeyboardmanager.svg)]() + + + +***IQKeyboardManager (Objective-C):*** Just ***drag and drop*** `IQKeyboardManager` directory from demo project to your project. That's it. + +***IQKeyboardManager (Swift):*** ***Drag and drop*** `IQKeyboardManagerSwift` directory from demo project to your project + +In AppDelegate.swift, just enable IQKeyboardManager. + +```swift +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + + IQKeyboardManager.shared.enable = true + + return true + } +} +``` + +Migration Guide +========================== +- [IQKeyboardManager 6.0.0 Migration Guide](https://github.com/hackiftekhar/IQKeyboardManager/wiki/IQKeyboardManager-6.0.0-Migration-Guide) + +Other Links +========================== + +- [Known Issues](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Known-Issues) +- [Manual Management Tweaks](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Manual-Management) +- [Properties and functions usage](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Properties-&-Functions) + +## Flow Diagram +[![IQKeyboardManager CFD](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg)](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg) + +If you would like to see detailed Flow diagram then see [Detailed Flow Diagram](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerCFD.jpg). + + +LICENSE +--- +Distributed under the MIT License. + +Contributions +--- +Any contribution is more than welcome! You can contribute through pull requests and issues on GitHub. + +Author +--- +If you wish to contact me, email at: hack.iftekhar@gmail.com diff --git a/Santander-Test/Pods/Manifest.lock b/Santander-Test/Pods/Manifest.lock new file mode 100644 index 00000000..15324d25 --- /dev/null +++ b/Santander-Test/Pods/Manifest.lock @@ -0,0 +1,38 @@ +PODS: + - Alamofire (4.8.1) + - AMPopTip (3.6.0) + - IQKeyboardManagerSwift (6.2.0) + - Moya (12.0.1): + - Moya/Core (= 12.0.1) + - Moya/Core (12.0.1): + - Alamofire (~> 4.1) + - Result (~> 4.0) + - Result (4.1.0) + - StatusAlert (1.1.0) + +DEPENDENCIES: + - AMPopTip + - IQKeyboardManagerSwift + - Moya + - StatusAlert + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - Alamofire + - AMPopTip + - IQKeyboardManagerSwift + - Moya + - Result + - StatusAlert + +SPEC CHECKSUMS: + Alamofire: 16ce2c353fb72865124ddae8a57c5942388f4f11 + AMPopTip: ebfa860ade372f383e8b3500d3101e8c4a4f73ce + IQKeyboardManagerSwift: b07ccf9d8cafe993dcd6cb794eb4ba34611a0c4e + Moya: cf730b3cd9e005401ef37a85143aa141a12fd38f + Result: bd966fac789cc6c1563440b348ab2598cc24d5c7 + StatusAlert: 4d6dadebf5f7abd5648fa774ef3062f2f95cc4aa + +PODFILE CHECKSUM: 4e150ff8c831fa6b97c7c1e6355aa319f0463e89 + +COCOAPODS: 1.6.0 diff --git a/Santander-Test/Pods/Moya/License.md b/Santander-Test/Pods/Moya/License.md new file mode 100644 index 00000000..58ef14fc --- /dev/null +++ b/Santander-Test/Pods/Moya/License.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-present Artsy, Ash Furrow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Santander-Test/Pods/Moya/Readme.md b/Santander-Test/Pods/Moya/Readme.md new file mode 100644 index 00000000..92a01824 --- /dev/null +++ b/Santander-Test/Pods/Moya/Readme.md @@ -0,0 +1,320 @@ +

+ +

+ +# Moya + +[![CircleCI](https://img.shields.io/circleci/project/github/Moya/Moya/master.svg)](https://circleci.com/gh/Moya/Moya/tree/master) +[![codecov.io](https://codecov.io/github/Moya/Moya/coverage.svg?branch=master)](https://codecov.io/github/Moya/Moya?branch=master) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![CocoaPods compatible](https://img.shields.io/cocoapods/v/Moya.svg)](https://cocoapods.org/pods/Moya) +[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) + +*A Chinese version of this document can be found [here](https://github.com/Moya/Moya/blob/master/Readme_CN.md).* + +You're a smart developer. You probably use [Alamofire](https://github.com/Alamofire/Alamofire) to abstract away access to +`URLSession` and all those nasty details you don't really care about. But then, +like lots of smart developers, you write ad hoc network abstraction layers. They +are probably called "APIManager" or "NetworkModel", and they always end in tears. + +![Moya Overview](web/diagram.png) + +Ad hoc network layers are common in iOS apps. They're bad for a few reasons: + +- Makes it hard to write new apps ("where do I begin?") +- Makes it hard to maintain existing apps ("oh my god, this mess...") +- Makes it hard to write unit tests ("how do I do this again?") + +So the basic idea of Moya is that we want some network abstraction layer that +sufficiently encapsulates actually calling Alamofire directly. It should be simple +enough that common things are easy, but comprehensive enough that complicated things +are also easy. + +> If you use Alamofire to abstract away `URLSession`, why not use something +to abstract away the nitty gritty of URLs, parameters, etc? + +Some awesome features of Moya: + +- Compile-time checking for correct API endpoint accesses. +- Lets you define a clear usage of different endpoints with associated enum values. +- Treats test stubs as first-class citizens so unit testing is super-easy. + +You can check out more about the project direction in the [vision document](https://github.com/Moya/Moya/blob/master/Vision.md). + +## Sample Projects + +We have provided two sample projects in the repository. To use it download the repo, run `carthage update` to download the required libraries and open [Moya.xcodeproj](https://github.com/Moya/Moya/tree/master/Moya.xcodeproj). You'll see two schemes: `Basic` and `Multi-Target` - select one and then build & run! Source files for these are in the `Examples` directory in project navigator. Have fun! + +## Project Status + +This project is actively under development, and is being used in [Artsy's +new auction app](https://github.com/Artsy/eidolon). We consider it +ready for production use. + +## Installation + +### Moya version vs Swift version. + +Below is a table that shows which version of Moya you should use for +your Swift version. + +| Swift | Moya | RxMoya | ReactiveMoya | +| ----- | ------------- |---------------|---------------| +| 4.X | >= 9.0 | >= 10.0 | >= 9.0 | +| 3.X | 8.0.0 - 8.0.5 | 8.0.0 - 8.0.5 | 8.0.0 - 8.0.5 | +| 2.3 | 7.0.2 - 7.0.4 | 7.0.2 - 7.0.4 | 7.0.2 - 7.0.4 | +| 2.2 | <= 7.0.1 | <= 7.0.1 | <= 7.0.1 | + +**Upgrading to a new major version of Moya? Check out our [migration guides](https://github.com/Moya/Moya/blob/master/docs/MigrationGuides).** + +### Swift Package Manager + +To integrate using Apple's Swift package manager, add the following as a dependency to your `Package.swift`: + +```swift +.package(url: "https://github.com/Moya/Moya.git", .upToNextMajor(from: "11.0.0")) +``` + +and then specify `"Moya"` as a dependency of the Target in which you wish to use Moya. +If you want to use reactive extensions, add also `"ReactiveMoya"` or `"RxMoya"` as your Target dependency respectively. +Here's an example `PackageDescription`: + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "MyPackage", + products: [ + .library( + name: "MyPackage", + targets: ["MyPackage"]), + ], + dependencies: [ + .package(url: "https://github.com/Moya/Moya.git", .upToNextMajor(from: "12.0.0")) + ], + targets: [ + .target( + name: "MyPackage", + dependencies: ["ReactiveMoya"]) + ] +) +``` + +Note that as of Moya 10, SPM only works with Swift 4 toolchain and greater. + +### CocoaPods + +For Moya, use the following entry in your Podfile: + +```rb +pod 'Moya', '~> 12.0' + +# or + +pod 'Moya/RxSwift', '~> 12.0' + +# or + +pod 'Moya/ReactiveSwift', '~> 12.0' +``` + +Then run `pod install`. + +In any file you'd like to use Moya in, don't forget to +import the framework with `import Moya`. + +### Carthage + +Carthage users can point to this repository and use whichever +generated framework they'd like, `Moya`, `RxMoya`, or `ReactiveMoya`. + +Make the following entry in your Cartfile: + +``` +github "Moya/Moya" +``` + +Then run `carthage update`. + +If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). + +> NOTE: At this time, Carthage does not provide a way to build only specific repository submodules. All submodules and their dependencies will be built with the above command. However, you don't need to copy frameworks you aren't using into your project. For instance, if you aren't using `ReactiveSwift`, feel free to delete that framework along with `ReactiveMoya` from the Carthage Build directory after `carthage update` completes. Or if you are using `ReactiveSwift` but not `RxSwift`, then `RxMoya`, `RxTest`, `RxCocoa`, etc. can safely be deleted. + +### Manually + +- Open up Terminal, `cd` into your top-level project directory, and run the following command *if* your project is not initialized as a git repository: + +```bash +$ git init +``` + +- Add Alamofire, Result & Moya as a git [submodule](http://git-scm.com/docs/git-submodule) by running the following commands: + +```bash +$ git submodule add https://github.com/Alamofire/Alamofire.git +$ git submodule add https://github.com/antitypical/Result.git +$ git submodule add https://github.com/Moya/Moya.git +``` + +- Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. Do the same with the `Result.xcodeproj` in the `Result` folder and `Moya.xcodeproj` in the `Moya` folder. + +> They should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter. + +- Verify that the deployment targets of the `xcodeproj`s match that of your application target in the Project Navigator. +- Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar. +- In the tab bar at the top of that window, open the "General" panel. +- Click on the `+` button under the "Embedded Binaries" section. +- You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder. + +> It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`. + +- Select the top `Alamofire.framework` for iOS and the bottom one for 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 either `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS` or `Alamofire watchOS`. + +- Click on the `+` button under "Embedded Binaries" again and add the build target you need for `Result`. +- Click on the `+` button again and add the correct build target for `Moya`. + +- And that's it! + +> The three frameworks are automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. + +## Usage + +After [some setup](https://github.com/Moya/Moya/blob/master/docs/Examples/Basic.md), using Moya is really simple. You can access an API like this: + +```swift +provider = MoyaProvider() +provider.request(.zen) { result in + switch result { + case let .success(moyaResponse): + let data = moyaResponse.data + let statusCode = moyaResponse.statusCode + // do something with the response data or statusCode + case let .failure(error): + // this means there was a network failure - either the request + // wasn't sent (connectivity), or no response was received (server + // timed out). If the server responds with a 4xx or 5xx error, that + // will be sent as a ".success"-ful response. + } +} +``` + +That's a basic example. Many API requests need parameters. Moya encodes these +into the enum you use to access the endpoint, like this: + +```swift +provider = MoyaProvider() +provider.request(.userProfile("ashfurrow")) { result in + // do something with the result +} +``` + +No more typos in URLs. No more missing parameter values. No more messing with +parameter encoding. + +For more examples, see the [documentation](https://github.com/Moya/Moya/blob/master/docs/Examples). + +## Reactive Extensions + +Even cooler are the reactive extensions. Moya provides reactive extensions for +[ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift) and +[RxSwift](https://github.com/ReactiveX/RxSwift). + +### ReactiveSwift + +[`ReactiveSwift` extension](https://github.com/Moya/Moya/blob/master/docs/ReactiveSwift.md) provides both `reactive.request(:callbackQueue:)` and +`reactive.requestWithProgress(:callbackQueue:)` methods that immediately return +`SignalProducer`s that you can start, bind, map, or whatever you want to do. +To handle errors, for instance, we could do the following: + +```swift +provider = MoyaProvider() +provider.reactive.request(.userProfile("ashfurrow")).start { event in + switch event { + case let .value(response): + image = UIImage(data: response.data) + case let .failed(error): + print(error) + default: + break + } +} +``` + +### RxSwift + +[`RxSwift` extension](https://github.com/Moya/Moya/blob/master/docs/RxSwift.md) also provide both `rx.request(:callbackQueue:)` and +`rx.requestWithProgress(:callbackQueue:)` methods, but return type is +different for both. In case of a normal `rx.request(:callbackQueue)`, the +return type is `Single` which emits either single element or an +error. In case of a `rx.requestWithProgress(:callbackQueue:)`, the return +type is `Observable`, since we may get multiple events +from progress and one last event which is a response. + +To handle errors, for instance, we could do the following: + +```swift +provider = MoyaProvider() +provider.rx.request(.userProfile("ashfurrow")).subscribe { event in + switch event { + case let .success(response): + image = UIImage(data: response.data) + case let .error(error): + print(error) + } +} +``` + +In addition to the option of using signals instead of callback blocks, there are +also a series of signal operators for RxSwift and ReactiveSwift that will attempt +to map the data received from the network response into either an image, some JSON, +or a string, with `mapImage()`, `mapJSON()`, and `mapString()`, respectively. If the mapping is unsuccessful, you'll get an error on the signal. You also get handy methods +for filtering out certain status codes. This means that you can place your code for +handling API errors like 400's in the same places as code for handling invalid +responses. + +## Community Projects + +[Moya has a great community around it and some people have created some very helpful extensions](https://github.com/Moya/Moya/blob/master/docs/CommunityProjects.md). + +## Contributing + +Hey! Do you like Moya? Awesome! We could actually really use your help! + +Open source isn't just writing code. Moya could use your help with any of the +following: + +- Finding (and reporting!) bugs. +- New feature suggestions. +- Answering questions on issues. +- Documentation improvements. +- Reviewing pull requests. +- Helping to manage issue priorities. +- Fixing bugs/new features. + +If any of that sounds cool to you, send a pull request! After your first +contribution, we will add you as a member to the repo so you can merge pull +requests and help steer the ship :ship: You can read more details about that [in our contributor guidelines](https://github.com/Moya/Moya/blob/master/Contributing.md). + +Moya's community has a tremendous positive energy, and the maintainers are committed to keeping things awesome. Like [in the CocoaPods community](https://github.com/CocoaPods/CocoaPods/wiki/Communication-&-Design-Rules), always assume positive intent; even if a comment sounds mean-spirited, give the person the benefit of the doubt. + +Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by [its terms](https://github.com/Moya/Moya/blob/master/Code%20of%20Conduct.md). + +### Adding new source files + +If you add or remove a source file from Moya, a corresponding change needs to be made to the `Moya.xcodeproj` project at the root of this repository. This project is used for Carthage. Don't worry, you'll get an automated warning when submitting a pull request if you forget. + +### Help us improve Moya documentation +Whether you’re a core member or a user trying it out for the first time, you can make a valuable contribution to Moya by improving the documentation. Help us by: + +- sending us feedback about something you thought was confusing or simply missing +- suggesting better wording or ways of explaining certain topics +- sending us a pull request via GitHub +- improving the [Chinese documentation](https://github.com/Moya/Moya/blob/master/Readme_CN.md) + + +## License + +Moya is released under an MIT license. See [License.md](https://github.com/Moya/Moya/blob/master/License.md) for more information. diff --git a/Santander-Test/Pods/Moya/Sources/Moya/AnyEncodable.swift b/Santander-Test/Pods/Moya/Sources/Moya/AnyEncodable.swift new file mode 100644 index 00000000..15c7128e --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/AnyEncodable.swift @@ -0,0 +1,14 @@ +import Foundation + +struct AnyEncodable: Encodable { + + private let encodable: Encodable + + public init(_ encodable: Encodable) { + self.encodable = encodable + } + + func encode(to encoder: Encoder) throws { + try encodable.encode(to: encoder) + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Cancellable.swift b/Santander-Test/Pods/Moya/Sources/Moya/Cancellable.swift new file mode 100644 index 00000000..9a9c783f --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Cancellable.swift @@ -0,0 +1,26 @@ +/// Protocol to define the opaque type returned from a request. +public protocol Cancellable { + + /// A Boolean value stating whether a request is cancelled. + var isCancelled: Bool { get } + + /// Cancels the represented request. + func cancel() +} + +internal class CancellableWrapper: Cancellable { + internal var innerCancellable: Cancellable = SimpleCancellable() + + var isCancelled: Bool { return innerCancellable.isCancelled } + + internal func cancel() { + innerCancellable.cancel() + } +} + +internal class SimpleCancellable: Cancellable { + var isCancelled = false + func cancel() { + isCancelled = true + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Endpoint.swift b/Santander-Test/Pods/Moya/Sources/Moya/Endpoint.swift new file mode 100755 index 00000000..37f79283 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Endpoint.swift @@ -0,0 +1,137 @@ +import Foundation + +/// Used for stubbing responses. +public enum EndpointSampleResponse { + + /// The network returned a response, including status code and data. + case networkResponse(Int, Data) + + /// The network returned response which can be fully customized. + case response(HTTPURLResponse, Data) + + /// The network failed to send the request, or failed to retrieve a response (eg a timeout). + case networkError(NSError) +} + +/// Class for reifying a target of the `Target` enum unto a concrete `Endpoint`. +/// - Note: As of Moya 11.0.0 Endpoint is no longer generic. +/// Existing code should work as is after removing the generic. +/// See #1529 and #1524 for the discussion. +open class Endpoint { + public typealias SampleResponseClosure = () -> EndpointSampleResponse + + /// A string representation of the URL for the request. + public let url: String + + /// A closure responsible for returning an `EndpointSampleResponse`. + public let sampleResponseClosure: SampleResponseClosure + + /// The HTTP method for the request. + public let method: Moya.Method + + /// The `Task` for the request. + public let task: Task + + /// The HTTP header fields for the request. + public let httpHeaderFields: [String: String]? + + public init(url: String, + sampleResponseClosure: @escaping SampleResponseClosure, + method: Moya.Method, + task: Task, + httpHeaderFields: [String: String]?) { + + self.url = url + self.sampleResponseClosure = sampleResponseClosure + self.method = method + self.task = task + self.httpHeaderFields = httpHeaderFields + } + + /// Convenience method for creating a new `Endpoint` with the same properties as the receiver, but with added HTTP header fields. + open func adding(newHTTPHeaderFields: [String: String]) -> Endpoint { + return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: add(httpHeaderFields: newHTTPHeaderFields)) + } + + /// Convenience method for creating a new `Endpoint` with the same properties as the receiver, but with replaced `task` parameter. + open func replacing(task: Task) -> Endpoint { + return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: httpHeaderFields) + } + + fileprivate func add(httpHeaderFields headers: [String: String]?) -> [String: String]? { + guard let unwrappedHeaders = headers, unwrappedHeaders.isEmpty == false else { + return self.httpHeaderFields + } + + var newHTTPHeaderFields = self.httpHeaderFields ?? [:] + unwrappedHeaders.forEach { key, value in + newHTTPHeaderFields[key] = value + } + return newHTTPHeaderFields + } +} + +/// Extension for converting an `Endpoint` into a `URLRequest`. +extension Endpoint { + // swiftlint:disable cyclomatic_complexity + /// Returns the `Endpoint` converted to a `URLRequest` if valid. Throws an error otherwise. + public func urlRequest() throws -> URLRequest { + guard let requestURL = Foundation.URL(string: url) else { + throw MoyaError.requestMapping(url) + } + + var request = URLRequest(url: requestURL) + request.httpMethod = method.rawValue + request.allHTTPHeaderFields = httpHeaderFields + + switch task { + case .requestPlain, .uploadFile, .uploadMultipart, .downloadDestination: + return request + case .requestData(let data): + request.httpBody = data + return request + case let .requestJSONEncodable(encodable): + return try request.encoded(encodable: encodable) + case let .requestCustomJSONEncodable(encodable, encoder: encoder): + return try request.encoded(encodable: encodable, encoder: encoder) + case let .requestParameters(parameters, parameterEncoding): + return try request.encoded(parameters: parameters, parameterEncoding: parameterEncoding) + case let .uploadCompositeMultipart(_, urlParameters): + let parameterEncoding = URLEncoding(destination: .queryString) + return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding) + case let .downloadParameters(parameters, parameterEncoding, _): + return try request.encoded(parameters: parameters, parameterEncoding: parameterEncoding) + case let .requestCompositeData(bodyData: bodyData, urlParameters: urlParameters): + request.httpBody = bodyData + let parameterEncoding = URLEncoding(destination: .queryString) + return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding) + case let .requestCompositeParameters(bodyParameters: bodyParameters, bodyEncoding: bodyParameterEncoding, urlParameters: urlParameters): + if let bodyParameterEncoding = bodyParameterEncoding as? URLEncoding, bodyParameterEncoding.destination != .httpBody { + fatalError("Only URLEncoding that `bodyEncoding` accepts is URLEncoding.httpBody. Others like `default`, `queryString` or `methodDependent` are prohibited - if you want to use them, add your parameters to `urlParameters` instead.") + } + let bodyfulRequest = try request.encoded(parameters: bodyParameters, parameterEncoding: bodyParameterEncoding) + let urlEncoding = URLEncoding(destination: .queryString) + return try bodyfulRequest.encoded(parameters: urlParameters, parameterEncoding: urlEncoding) + } + } + // swiftlint:enable cyclomatic_complexity +} + +/// Required for using `Endpoint` as a key type in a `Dictionary`. +extension Endpoint: Equatable, Hashable { + public var hashValue: Int { + let request = try? urlRequest() + return request?.hashValue ?? url.hashValue + } + + /// Note: If both Endpoints fail to produce a URLRequest the comparison will + /// fall back to comparing each Endpoint's hashValue. + public static func == (lhs: Endpoint, rhs: Endpoint) -> Bool { + let lhsRequest = try? lhs.urlRequest() + let rhsRequest = try? rhs.urlRequest() + if lhsRequest != nil, rhsRequest == nil { return false } + if lhsRequest == nil, rhsRequest != nil { return false } + if lhsRequest == nil, rhsRequest == nil { return lhs.hashValue == rhs.hashValue } + return (lhsRequest == rhsRequest) + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Image.swift b/Santander-Test/Pods/Moya/Sources/Moya/Image.swift new file mode 100644 index 00000000..233be5f2 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Image.swift @@ -0,0 +1,10 @@ +#if os(iOS) || os(watchOS) || os(tvOS) + import UIKit.UIImage + public typealias ImageType = UIImage +#elseif os(macOS) + import AppKit.NSImage + public typealias ImageType = NSImage +#endif + +/// An alias for the SDK's image type. +public typealias Image = ImageType diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Moya+Alamofire.swift b/Santander-Test/Pods/Moya/Sources/Moya/Moya+Alamofire.swift new file mode 100644 index 00000000..8d4cd4a7 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Moya+Alamofire.swift @@ -0,0 +1,90 @@ +import Foundation +import Alamofire + +public typealias Manager = Alamofire.SessionManager +internal typealias Request = Alamofire.Request +internal typealias DownloadRequest = Alamofire.DownloadRequest +internal typealias UploadRequest = Alamofire.UploadRequest +internal typealias DataRequest = Alamofire.DataRequest + +internal typealias URLRequestConvertible = Alamofire.URLRequestConvertible + +/// Represents an HTTP method. +public typealias Method = Alamofire.HTTPMethod + +/// Choice of parameter encoding. +public typealias ParameterEncoding = Alamofire.ParameterEncoding +public typealias JSONEncoding = Alamofire.JSONEncoding +public typealias URLEncoding = Alamofire.URLEncoding +public typealias PropertyListEncoding = Alamofire.PropertyListEncoding + +/// Multipart form. +public typealias RequestMultipartFormData = Alamofire.MultipartFormData + +/// Multipart form data encoding result. +public typealias MultipartFormDataEncodingResult = Manager.MultipartFormDataEncodingResult +public typealias DownloadDestination = Alamofire.DownloadRequest.DownloadFileDestination + +/// Make the Alamofire Request type conform to our type, to prevent leaking Alamofire to plugins. +extension Request: RequestType { } + +/// Internal token that can be used to cancel requests +public final class CancellableToken: Cancellable, CustomDebugStringConvertible { + let cancelAction: () -> Void + let request: Request? + + public fileprivate(set) var isCancelled = false + + fileprivate var lock: DispatchSemaphore = DispatchSemaphore(value: 1) + + public func cancel() { + _ = lock.wait(timeout: DispatchTime.distantFuture) + defer { lock.signal() } + guard !isCancelled else { return } + isCancelled = true + cancelAction() + } + + public init(action: @escaping () -> Void) { + self.cancelAction = action + self.request = nil + } + + init(request: Request) { + self.request = request + self.cancelAction = { + request.cancel() + } + } + + /// A textual representation of this instance, suitable for debugging. + public var debugDescription: String { + guard let request = self.request else { + return "Empty Request" + } + return request.debugDescription + } + +} + +internal typealias RequestableCompletion = (HTTPURLResponse?, URLRequest?, Data?, Swift.Error?) -> Void + +internal protocol Requestable { + func response(callbackQueue: DispatchQueue?, completionHandler: @escaping RequestableCompletion) -> Self +} + +extension DataRequest: Requestable { + internal func response(callbackQueue: DispatchQueue?, completionHandler: @escaping RequestableCompletion) -> Self { + return response(queue: callbackQueue) { handler in + completionHandler(handler.response, handler.request, handler.data, handler.error) + } + } +} + +extension DownloadRequest: Requestable { + internal func response(callbackQueue: DispatchQueue?, completionHandler: @escaping RequestableCompletion) -> Self { + return response(queue: callbackQueue) { handler in + completionHandler(handler.response, handler.request, nil, handler.error) + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/MoyaError.swift b/Santander-Test/Pods/Moya/Sources/Moya/MoyaError.swift new file mode 100644 index 00000000..472026cd --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/MoyaError.swift @@ -0,0 +1,76 @@ +import Foundation + +/// A type representing possible errors Moya can throw. +public enum MoyaError: Swift.Error { + + /// Indicates a response failed to map to an image. + case imageMapping(Response) + + /// Indicates a response failed to map to a JSON structure. + case jsonMapping(Response) + + /// Indicates a response failed to map to a String. + case stringMapping(Response) + + /// Indicates a response failed to map to a Decodable object. + case objectMapping(Swift.Error, Response) + + /// Indicates that Encodable couldn't be encoded into Data + case encodableMapping(Swift.Error) + + /// Indicates a response failed with an invalid HTTP status code. + case statusCode(Response) + + /// Indicates a response failed due to an underlying `Error`. + case underlying(Swift.Error, Response?) + + /// Indicates that an `Endpoint` failed to map to a `URLRequest`. + case requestMapping(String) + + /// Indicates that an `Endpoint` failed to encode the parameters for the `URLRequest`. + case parameterEncoding(Swift.Error) +} + +public extension MoyaError { + /// Depending on error type, returns a `Response` object. + var response: Moya.Response? { + switch self { + case .imageMapping(let response): return response + case .jsonMapping(let response): return response + case .stringMapping(let response): return response + case .objectMapping(_, let response): return response + case .statusCode(let response): return response + case .underlying(_, let response): return response + case .encodableMapping: return nil + case .requestMapping: return nil + case .parameterEncoding: return nil + } + } +} + +// MARK: - Error Descriptions + +extension MoyaError: LocalizedError { + public var errorDescription: String? { + switch self { + case .imageMapping: + return "Failed to map data to an Image." + case .jsonMapping: + return "Failed to map data to JSON." + case .stringMapping: + return "Failed to map data to a String." + case .objectMapping: + return "Failed to map data to a Decodable object." + case .encodableMapping: + return "Failed to encode Encodable object into data." + case .statusCode: + return "Status code didn't fall within the given range." + case .requestMapping: + return "Failed to map Endpoint to a URLRequest." + case .parameterEncoding(let error): + return "Failed to encode parameters for URLRequest. \(error.localizedDescription)" + case .underlying(let error, _): + return error.localizedDescription + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider+Defaults.swift b/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider+Defaults.swift new file mode 100644 index 00000000..7f39824c --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider+Defaults.swift @@ -0,0 +1,36 @@ +import Foundation + +/// These functions are default mappings to `MoyaProvider`'s properties: endpoints, requests, manager, etc. +public extension MoyaProvider { + public final class func defaultEndpointMapping(for target: Target) -> Endpoint { + return Endpoint( + url: URL(target: target).absoluteString, + sampleResponseClosure: { .networkResponse(200, target.sampleData) }, + method: target.method, + task: target.task, + httpHeaderFields: target.headers + ) + } + + public final class func defaultRequestMapping(for endpoint: Endpoint, closure: RequestResultClosure) { + do { + let urlRequest = try endpoint.urlRequest() + closure(.success(urlRequest)) + } catch MoyaError.requestMapping(let url) { + closure(.failure(MoyaError.requestMapping(url))) + } catch MoyaError.parameterEncoding(let error) { + closure(.failure(MoyaError.parameterEncoding(error))) + } catch { + closure(.failure(MoyaError.underlying(error, nil))) + } + } + + public final class func defaultAlamofireManager() -> Manager { + let configuration = URLSessionConfiguration.default + configuration.httpAdditionalHeaders = Manager.defaultHTTPHeaders + + let manager = Manager(configuration: configuration) + manager.startRequestsImmediately = false + return manager + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider+Internal.swift b/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider+Internal.swift new file mode 100644 index 00000000..e8bb4d21 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider+Internal.swift @@ -0,0 +1,274 @@ +import Foundation +import Result + +// MARK: - Method + +extension Method { + /// A Boolean value determining whether the request supports multipart. + public var supportsMultipart: Bool { + switch self { + case .post, .put, .patch, .connect: + return true + case .get, .delete, .head, .options, .trace: + return false + } + } +} + +// MARK: - MoyaProvider + +/// Internal extension to keep the inner-workings outside the main Moya.swift file. +public extension MoyaProvider { + /// Performs normal requests. + func requestNormal(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> Cancellable { + let endpoint = self.endpoint(target) + let stubBehavior = self.stubClosure(target) + let cancellableToken = CancellableWrapper() + + // Allow plugins to modify response + let pluginsWithCompletion: Moya.Completion = { result in + let processedResult = self.plugins.reduce(result) { $1.process($0, target: target) } + completion(processedResult) + } + + if trackInflights { + objc_sync_enter(self) + var inflightCompletionBlocks = self.inflightRequests[endpoint] + inflightCompletionBlocks?.append(pluginsWithCompletion) + self.inflightRequests[endpoint] = inflightCompletionBlocks + objc_sync_exit(self) + + if inflightCompletionBlocks != nil { + return cancellableToken + } else { + objc_sync_enter(self) + self.inflightRequests[endpoint] = [pluginsWithCompletion] + objc_sync_exit(self) + } + } + + let performNetworking = { (requestResult: Result) in + if cancellableToken.isCancelled { + self.cancelCompletion(pluginsWithCompletion, target: target) + return + } + + var request: URLRequest! + + switch requestResult { + case .success(let urlRequest): + request = urlRequest + case .failure(let error): + pluginsWithCompletion(.failure(error)) + return + } + + // Allow plugins to modify request + let preparedRequest = self.plugins.reduce(request) { $1.prepare($0, target: target) } + + let networkCompletion: Moya.Completion = { result in + if self.trackInflights { + self.inflightRequests[endpoint]?.forEach { $0(result) } + + objc_sync_enter(self) + self.inflightRequests.removeValue(forKey: endpoint) + objc_sync_exit(self) + } else { + pluginsWithCompletion(result) + } + } + + cancellableToken.innerCancellable = self.performRequest(target, request: preparedRequest, callbackQueue: callbackQueue, progress: progress, completion: networkCompletion, endpoint: endpoint, stubBehavior: stubBehavior) + } + + requestClosure(endpoint, performNetworking) + + return cancellableToken + } + + // swiftlint:disable:next function_parameter_count + private func performRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion, endpoint: Endpoint, stubBehavior: Moya.StubBehavior) -> Cancellable { + switch stubBehavior { + case .never: + switch endpoint.task { + case .requestPlain, .requestData, .requestJSONEncodable, .requestCustomJSONEncodable, .requestParameters, .requestCompositeData, .requestCompositeParameters: + return self.sendRequest(target, request: request, callbackQueue: callbackQueue, progress: progress, completion: completion) + case .uploadFile(let file): + return self.sendUploadFile(target, request: request, callbackQueue: callbackQueue, file: file, progress: progress, completion: completion) + case .uploadMultipart(let multipartBody), .uploadCompositeMultipart(let multipartBody, _): + guard !multipartBody.isEmpty && endpoint.method.supportsMultipart else { + fatalError("\(target) is not a multipart upload target.") + } + return self.sendUploadMultipart(target, request: request, callbackQueue: callbackQueue, multipartBody: multipartBody, progress: progress, completion: completion) + case .downloadDestination(let destination), .downloadParameters(_, _, let destination): + return self.sendDownloadRequest(target, request: request, callbackQueue: callbackQueue, destination: destination, progress: progress, completion: completion) + } + default: + return self.stubRequest(target, request: request, callbackQueue: callbackQueue, completion: completion, endpoint: endpoint, stubBehavior: stubBehavior) + } + } + + func cancelCompletion(_ completion: Moya.Completion, target: Target) { + let error = MoyaError.underlying(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: nil), nil) + plugins.forEach { $0.didReceive(.failure(error), target: target) } + completion(.failure(error)) + } + + /// Creates a function which, when called, executes the appropriate stubbing behavior for the given parameters. + public final func createStubFunction(_ token: CancellableToken, forTarget target: Target, withCompletion completion: @escaping Moya.Completion, endpoint: Endpoint, plugins: [PluginType], request: URLRequest) -> (() -> Void) { // swiftlint:disable:this function_parameter_count + return { + if token.isCancelled { + self.cancelCompletion(completion, target: target) + return + } + + let validate = { (response: Moya.Response) -> Result in + let validCodes = target.validationType.statusCodes + guard !validCodes.isEmpty else { return .success(response) } + if validCodes.contains(response.statusCode) { + return .success(response) + } else { + let statusError = MoyaError.statusCode(response) + let error = MoyaError.underlying(statusError, response) + return .failure(error) + } + } + + switch endpoint.sampleResponseClosure() { + case .networkResponse(let statusCode, let data): + let response = Moya.Response(statusCode: statusCode, data: data, request: request, response: nil) + let result = validate(response) + plugins.forEach { $0.didReceive(result, target: target) } + completion(result) + case .response(let customResponse, let data): + let response = Moya.Response(statusCode: customResponse.statusCode, data: data, request: request, response: customResponse) + let result = validate(response) + plugins.forEach { $0.didReceive(result, target: target) } + completion(result) + case .networkError(let error): + let error = MoyaError.underlying(error, nil) + plugins.forEach { $0.didReceive(.failure(error), target: target) } + completion(.failure(error)) + } + } + } + + /// Notify all plugins that a stub is about to be performed. You must call this if overriding `stubRequest`. + final func notifyPluginsOfImpendingStub(for request: URLRequest, target: Target) { + let alamoRequest = manager.request(request as URLRequestConvertible) + plugins.forEach { $0.willSend(alamoRequest, target: target) } + alamoRequest.cancel() + } +} + +private extension MoyaProvider { + func sendUploadMultipart(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, multipartBody: [MultipartFormData], progress: Moya.ProgressBlock? = nil, completion: @escaping Moya.Completion) -> CancellableWrapper { + let cancellable = CancellableWrapper() + + let multipartFormData: (RequestMultipartFormData) -> Void = { form in + form.applyMoyaMultipartFormData(multipartBody) + } + + manager.upload(multipartFormData: multipartFormData, with: request) { result in + switch result { + case .success(let alamoRequest, _, _): + if cancellable.isCancelled { + self.cancelCompletion(completion, target: target) + return + } + let validationCodes = target.validationType.statusCodes + let validatedRequest = validationCodes.isEmpty ? alamoRequest : alamoRequest.validate(statusCode: validationCodes) + cancellable.innerCancellable = self.sendAlamofireRequest(validatedRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) + case .failure(let error): + completion(.failure(MoyaError.underlying(error as NSError, nil))) + } + } + + return cancellable + } + + func sendUploadFile(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, file: URL, progress: ProgressBlock? = nil, completion: @escaping Completion) -> CancellableToken { + let uploadRequest = manager.upload(file, with: request) + let validationCodes = target.validationType.statusCodes + let alamoRequest = validationCodes.isEmpty ? uploadRequest : uploadRequest.validate(statusCode: validationCodes) + return self.sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) + } + + func sendDownloadRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, destination: @escaping DownloadDestination, progress: ProgressBlock? = nil, completion: @escaping Completion) -> CancellableToken { + let downloadRequest = manager.download(request, to: destination) + let validationCodes = target.validationType.statusCodes + let alamoRequest = validationCodes.isEmpty ? downloadRequest : downloadRequest.validate(statusCode: validationCodes) + return self.sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) + } + + func sendRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> CancellableToken { + let initialRequest = manager.request(request as URLRequestConvertible) + let validationCodes = target.validationType.statusCodes + let alamoRequest = validationCodes.isEmpty ? initialRequest : initialRequest.validate(statusCode: validationCodes) + return sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) + } + + // swiftlint:disable:next cyclomatic_complexity + func sendAlamofireRequest(_ alamoRequest: T, target: Target, callbackQueue: DispatchQueue?, progress progressCompletion: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> CancellableToken where T: Requestable, T: Request { + // Give plugins the chance to alter the outgoing request + let plugins = self.plugins + plugins.forEach { $0.willSend(alamoRequest, target: target) } + + var progressAlamoRequest = alamoRequest + let progressClosure: (Progress) -> Void = { progress in + let sendProgress: () -> Void = { + progressCompletion?(ProgressResponse(progress: progress)) + } + + if let callbackQueue = callbackQueue { + callbackQueue.async(execute: sendProgress) + } else { + sendProgress() + } + } + + // Perform the actual request + if progressCompletion != nil { + switch progressAlamoRequest { + case let downloadRequest as DownloadRequest: + if let downloadRequest = downloadRequest.downloadProgress(closure: progressClosure) as? T { + progressAlamoRequest = downloadRequest + } + case let uploadRequest as UploadRequest: + if let uploadRequest = uploadRequest.uploadProgress(closure: progressClosure) as? T { + progressAlamoRequest = uploadRequest + } + case let dataRequest as DataRequest: + if let dataRequest = dataRequest.downloadProgress(closure: progressClosure) as? T { + progressAlamoRequest = dataRequest + } + default: break + } + } + + let completionHandler: RequestableCompletion = { response, request, data, error in + let result = convertResponseToResult(response, request: request, data: data, error: error) + // Inform all plugins about the response + plugins.forEach { $0.didReceive(result, target: target) } + if let progressCompletion = progressCompletion { + switch progressAlamoRequest { + case let downloadRequest as DownloadRequest: + progressCompletion(ProgressResponse(progress: downloadRequest.progress, response: result.value)) + case let uploadRequest as UploadRequest: + progressCompletion(ProgressResponse(progress: uploadRequest.uploadProgress, response: result.value)) + case let dataRequest as DataRequest: + progressCompletion(ProgressResponse(progress: dataRequest.progress, response: result.value)) + default: + progressCompletion(ProgressResponse(response: result.value)) + } + } + completion(result) + } + + progressAlamoRequest = progressAlamoRequest.response(callbackQueue: callbackQueue, completionHandler: completionHandler) + + progressAlamoRequest.resume() + + return CancellableToken(request: progressAlamoRequest) + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider.swift b/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider.swift new file mode 100755 index 00000000..4bced8b9 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/MoyaProvider.swift @@ -0,0 +1,206 @@ +import Foundation +import Result + +/// Closure to be executed when a request has completed. +public typealias Completion = (_ result: Result) -> Void + +/// Closure to be executed when progress changes. +public typealias ProgressBlock = (_ progress: ProgressResponse) -> Void + +/// A type representing the progress of a request. +public struct ProgressResponse { + + /// The optional response of the request. + public let response: Response? + + /// An object that conveys ongoing progress for a given request. + public let progressObject: Progress? + + /// Initializes a `ProgressResponse`. + public init(progress: Progress? = nil, response: Response? = nil) { + self.progressObject = progress + self.response = response + } + + /// The fraction of the overall work completed by the progress object. + public var progress: Double { + return progressObject?.fractionCompleted ?? 1.0 + } + + /// A Boolean value stating whether the request is completed. + public var completed: Bool { + return progress == 1.0 && response != nil + } +} + +/// A protocol representing a minimal interface for a MoyaProvider. +/// Used by the reactive provider extensions. +public protocol MoyaProviderType: AnyObject { + + associatedtype Target: TargetType + + /// Designated request-making method. Returns a `Cancellable` token to cancel the request later. + func request(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> Cancellable +} + +/// Request provider class. Requests should be made through this class only. +open class MoyaProvider: MoyaProviderType { + + /// Closure that defines the endpoints for the provider. + public typealias EndpointClosure = (Target) -> Endpoint + + /// Closure that decides if and what request should be performed. + public typealias RequestResultClosure = (Result) -> Void + + /// Closure that resolves an `Endpoint` into a `RequestResult`. + public typealias RequestClosure = (Endpoint, @escaping RequestResultClosure) -> Void + + /// Closure that decides if/how a request should be stubbed. + public typealias StubClosure = (Target) -> Moya.StubBehavior + + /// A closure responsible for mapping a `TargetType` to an `EndPoint`. + public let endpointClosure: EndpointClosure + + /// A closure deciding if and what request should be performed. + public let requestClosure: RequestClosure + + /// A closure responsible for determining the stubbing behavior + /// of a request for a given `TargetType`. + public let stubClosure: StubClosure + + /// The manager for the session. + public let manager: Manager + + /// A list of plugins. + /// e.g. for logging, network activity indicator or credentials. + public let plugins: [PluginType] + + public let trackInflights: Bool + + open internal(set) var inflightRequests: [Endpoint: [Moya.Completion]] = [:] + + /// Propagated to Alamofire as callback queue. If nil - the Alamofire default (as of their API in 2017 - the main queue) will be used. + let callbackQueue: DispatchQueue? + + /// Initializes a provider. + public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping, + requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping, + stubClosure: @escaping StubClosure = MoyaProvider.neverStub, + callbackQueue: DispatchQueue? = nil, + manager: Manager = MoyaProvider.defaultAlamofireManager(), + plugins: [PluginType] = [], + trackInflights: Bool = false) { + + self.endpointClosure = endpointClosure + self.requestClosure = requestClosure + self.stubClosure = stubClosure + self.manager = manager + self.plugins = plugins + self.trackInflights = trackInflights + self.callbackQueue = callbackQueue + } + + /// Returns an `Endpoint` based on the token, method, and parameters by invoking the `endpointClosure`. + open func endpoint(_ token: Target) -> Endpoint { + return endpointClosure(token) + } + + /// Designated request-making method. Returns a `Cancellable` token to cancel the request later. + @discardableResult + open func request(_ target: Target, + callbackQueue: DispatchQueue? = .none, + progress: ProgressBlock? = .none, + completion: @escaping Completion) -> Cancellable { + + let callbackQueue = callbackQueue ?? self.callbackQueue + return requestNormal(target, callbackQueue: callbackQueue, progress: progress, completion: completion) + } + + // swiftlint:disable function_parameter_count + /// When overriding this method, take care to `notifyPluginsOfImpendingStub` and to perform the stub using the `createStubFunction` method. + /// Note: this was previously in an extension, however it must be in the original class declaration to allow subclasses to override. + @discardableResult + open func stubRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, completion: @escaping Moya.Completion, endpoint: Endpoint, stubBehavior: Moya.StubBehavior) -> CancellableToken { + let callbackQueue = callbackQueue ?? self.callbackQueue + let cancellableToken = CancellableToken { } + notifyPluginsOfImpendingStub(for: request, target: target) + let plugins = self.plugins + let stub: () -> Void = createStubFunction(cancellableToken, forTarget: target, withCompletion: completion, endpoint: endpoint, plugins: plugins, request: request) + switch stubBehavior { + case .immediate: + switch callbackQueue { + case .none: + stub() + case .some(let callbackQueue): + callbackQueue.async(execute: stub) + } + case .delayed(let delay): + let killTimeOffset = Int64(CDouble(delay) * CDouble(NSEC_PER_SEC)) + let killTime = DispatchTime.now() + Double(killTimeOffset) / Double(NSEC_PER_SEC) + (callbackQueue ?? DispatchQueue.main).asyncAfter(deadline: killTime) { + stub() + } + case .never: + fatalError("Method called to stub request when stubbing is disabled.") + } + + return cancellableToken + } + // swiftlint:enable function_parameter_count +} + +/// Mark: Stubbing + +/// Controls how stub responses are returned. +public enum StubBehavior { + + /// Do not stub. + case never + + /// Return a response immediately. + case immediate + + /// Return a response after a delay. + case delayed(seconds: TimeInterval) +} + +public extension MoyaProvider { + + // Swift won't let us put the StubBehavior enum inside the provider class, so we'll + // at least add some class functions to allow easy access to common stubbing closures. + + /// Do not stub. + public final class func neverStub(_: Target) -> Moya.StubBehavior { + return .never + } + + /// Return a response immediately. + public final class func immediatelyStub(_: Target) -> Moya.StubBehavior { + return .immediate + } + + /// Return a response after a delay. + public final class func delayedStub(_ seconds: TimeInterval) -> (Target) -> Moya.StubBehavior { + return { _ in return .delayed(seconds: seconds) } + } +} + +/// A public function responsible for converting the result of a `URLRequest` to a Result. +public func convertResponseToResult(_ response: HTTPURLResponse?, request: URLRequest?, data: Data?, error: Swift.Error?) -> + Result { + switch (response, data, error) { + case let (.some(response), data, .none): + let response = Moya.Response(statusCode: response.statusCode, data: data ?? Data(), request: request, response: response) + return .success(response) + case let (.some(response), _, .some(error)): + let response = Moya.Response(statusCode: response.statusCode, data: data ?? Data(), request: request, response: response) + let error = MoyaError.underlying(error, response) + return .failure(error) + case let (_, _, .some(error)): + let error = MoyaError.underlying(error, nil) + return .failure(error) + default: + let error = MoyaError.underlying(NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil), nil) + return .failure(error) + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/MultiTarget.swift b/Santander-Test/Pods/Moya/Sources/Moya/MultiTarget.swift new file mode 100644 index 00000000..7b813e4e --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/MultiTarget.swift @@ -0,0 +1,54 @@ +import Foundation + +/// A `TargetType` used to enable `MoyaProvider` to process multiple `TargetType`s. +public enum MultiTarget: TargetType { + /// The embedded `TargetType`. + case target(TargetType) + + /// Initializes a `MultiTarget`. + public init(_ target: TargetType) { + self = MultiTarget.target(target) + } + + /// The embedded target's base `URL`. + public var path: String { + return target.path + } + + /// The baseURL of the embedded target. + public var baseURL: URL { + return target.baseURL + } + + /// The HTTP method of the embedded target. + public var method: Moya.Method { + return target.method + } + + /// The sampleData of the embedded target. + public var sampleData: Data { + return target.sampleData + } + + /// The `Task` of the embedded target. + public var task: Task { + return target.task + } + + /// The `ValidationType` of the embedded target. + public var validationType: ValidationType { + return target.validationType + } + + /// The headers of the embedded target. + public var headers: [String: String]? { + return target.headers + } + + /// The embedded `TargetType`. + public var target: TargetType { + switch self { + case .target(let target): return target + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/MultipartFormData.swift b/Santander-Test/Pods/Moya/Sources/Moya/MultipartFormData.swift new file mode 100644 index 00000000..418cae21 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/MultipartFormData.swift @@ -0,0 +1,73 @@ +import Foundation +import Alamofire + +/// Represents "multipart/form-data" for an upload. +public struct MultipartFormData { + + /// Method to provide the form data. + public enum FormDataProvider { + case data(Foundation.Data) + case file(URL) + case stream(InputStream, UInt64) + } + + public init(provider: FormDataProvider, name: String, fileName: String? = nil, mimeType: String? = nil) { + self.provider = provider + self.name = name + self.fileName = fileName + self.mimeType = mimeType + } + + /// The method being used for providing form data. + public let provider: FormDataProvider + + /// The name. + public let name: String + + /// The file name. + public let fileName: String? + + /// The MIME type + public let mimeType: String? + +} + +// MARK: RequestMultipartFormData appending +internal extension RequestMultipartFormData { + func append(data: Data, bodyPart: MultipartFormData) { + if let mimeType = bodyPart.mimeType { + if let fileName = bodyPart.fileName { + append(data, withName: bodyPart.name, fileName: fileName, mimeType: mimeType) + } else { + append(data, withName: bodyPart.name, mimeType: mimeType) + } + } else { + append(data, withName: bodyPart.name) + } + } + + func append(fileURL url: URL, bodyPart: MultipartFormData) { + if let fileName = bodyPart.fileName, let mimeType = bodyPart.mimeType { + append(url, withName: bodyPart.name, fileName: fileName, mimeType: mimeType) + } else { + append(url, withName: bodyPart.name) + } + } + + func append(stream: InputStream, length: UInt64, bodyPart: MultipartFormData) { + append(stream, withLength: length, name: bodyPart.name, fileName: bodyPart.fileName ?? "", mimeType: bodyPart.mimeType ?? "") + } + + func applyMoyaMultipartFormData(_ multipartBody: [Moya.MultipartFormData]) { + for bodyPart in multipartBody { + switch bodyPart.provider { + case .data(let data): + append(data: data, bodyPart: bodyPart) + case .file(let url): + append(fileURL: url, bodyPart: bodyPart) + case .stream(let stream, let length): + append(stream: stream, length: length, bodyPart: bodyPart) + } + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Plugin.swift b/Santander-Test/Pods/Moya/Sources/Moya/Plugin.swift new file mode 100644 index 00000000..3f5bd6c1 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Plugin.swift @@ -0,0 +1,47 @@ +import Foundation +import Result + +/// A Moya Plugin receives callbacks to perform side effects wherever a request is sent or received. +/// +/// for example, a plugin may be used to +/// - log network requests +/// - hide and show a network activity indicator +/// - inject additional information into a request +public protocol PluginType { + /// Called to modify a request before sending. + func prepare(_ request: URLRequest, target: TargetType) -> URLRequest + + /// Called immediately before a request is sent over the network (or stubbed). + func willSend(_ request: RequestType, target: TargetType) + + /// Called after a response has been received, but before the MoyaProvider has invoked its completion handler. + func didReceive(_ result: Result, target: TargetType) + + /// Called to modify a result before completion. + func process(_ result: Result, target: TargetType) -> Result +} + +public extension PluginType { + func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { return request } + func willSend(_ request: RequestType, target: TargetType) { } + func didReceive(_ result: Result, target: TargetType) { } + func process(_ result: Result, target: TargetType) -> Result { return result } +} + +/// Request type used by `willSend` plugin function. +public protocol RequestType { + + // Note: + // + // We use this protocol instead of the Alamofire request to avoid leaking that abstraction. + // A plugin should not know about Alamofire at all. + + /// Retrieve an `NSURLRequest` representation. + var request: URLRequest? { get } + + /// Authenticates the request with a username and password. + func authenticate(user: String, password: String, persistence: URLCredential.Persistence) -> Self + + /// Authenticates the request with an `NSURLCredential` instance. + func authenticate(usingCredential credential: URLCredential) -> Self +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Plugins/AccessTokenPlugin.swift b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/AccessTokenPlugin.swift new file mode 100644 index 00000000..e1234e22 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/AccessTokenPlugin.swift @@ -0,0 +1,93 @@ +import Foundation +import Result + +// MARK: - AccessTokenAuthorizable + +/// A protocol for controlling the behavior of `AccessTokenPlugin`. +public protocol AccessTokenAuthorizable { + + /// Represents the authorization header to use for requests. + var authorizationType: AuthorizationType { get } +} + +// MARK: - AuthorizationType + +/// An enum representing the header to use with an `AccessTokenPlugin` +public enum AuthorizationType { + /// No header. + case none + + /// The `"Basic"` header. + case basic + + /// The `"Bearer"` header. + case bearer + + /// Custom header implementation. + case custom(String) + + public var value: String? { + switch self { + case .none: return nil + case .basic: return "Basic" + case .bearer: return "Bearer" + case .custom(let customValue): return customValue + } + } +} + +// MARK: - AccessTokenPlugin + +/** + A plugin for adding basic or bearer-type authorization headers to requests. Example: + + ``` + Authorization: Basic + Authorization: Bearer + Authorization: <Сustom> + ``` + +*/ +public struct AccessTokenPlugin: PluginType { + + /// A closure returning the access token to be applied in the header. + public let tokenClosure: () -> String + + /** + Initialize a new `AccessTokenPlugin`. + + - parameters: + - tokenClosure: A closure returning the token to be applied in the pattern `Authorization: ` + */ + public init(tokenClosure: @escaping () -> String) { + self.tokenClosure = tokenClosure + } + + /** + Prepare a request by adding an authorization header if necessary. + + - parameters: + - request: The request to modify. + - target: The target of the request. + - returns: The modified `URLRequest`. + */ + public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { + + guard let authorizable = target as? AccessTokenAuthorizable else { return request } + + let authorizationType = authorizable.authorizationType + var request = request + + switch authorizationType { + case .basic, .bearer, .custom: + if let value = authorizationType.value { + let authValue = value + " " + tokenClosure() + request.addValue(authValue, forHTTPHeaderField: "Authorization") + } + case .none: + break + } + + return request + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Plugins/CredentialsPlugin.swift b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/CredentialsPlugin.swift new file mode 100644 index 00000000..5f22aab8 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/CredentialsPlugin.swift @@ -0,0 +1,22 @@ +import Foundation +import Result + +/// Provides each request with optional URLCredentials. +public final class CredentialsPlugin: PluginType { + + public typealias CredentialClosure = (TargetType) -> URLCredential? + let credentialsClosure: CredentialClosure + + /// Initializes a CredentialsPlugin. + public init(credentialsClosure: @escaping CredentialClosure) { + self.credentialsClosure = credentialsClosure + } + + // MARK: Plugin + + public func willSend(_ request: RequestType, target: TargetType) { + if let credentials = credentialsClosure(target) { + _ = request.authenticate(usingCredential: credentials) + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Plugins/NetworkActivityPlugin.swift b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/NetworkActivityPlugin.swift new file mode 100644 index 00000000..c9001051 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/NetworkActivityPlugin.swift @@ -0,0 +1,31 @@ +import Foundation +import Result + +/// Network activity change notification type. +public enum NetworkActivityChangeType { + case began, ended +} + +/// Notify a request's network activity changes (request begins or ends). +public final class NetworkActivityPlugin: PluginType { + + public typealias NetworkActivityClosure = (_ change: NetworkActivityChangeType, _ target: TargetType) -> Void + let networkActivityClosure: NetworkActivityClosure + + /// Initializes a NetworkActivityPlugin. + public init(networkActivityClosure: @escaping NetworkActivityClosure) { + self.networkActivityClosure = networkActivityClosure + } + + // MARK: Plugin + + /// Called by the provider as soon as the request is about to start + public func willSend(_ request: RequestType, target: TargetType) { + networkActivityClosure(.began, target) + } + + /// Called by the provider as soon as a response arrives, even if the request is canceled. + public func didReceive(_ result: Result, target: TargetType) { + networkActivityClosure(.ended, target) + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Plugins/NetworkLoggerPlugin.swift b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/NetworkLoggerPlugin.swift new file mode 100644 index 00000000..cc91afe8 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Plugins/NetworkLoggerPlugin.swift @@ -0,0 +1,114 @@ +import Foundation +import Result + +/// Logs network activity (outgoing requests and incoming responses). +public final class NetworkLoggerPlugin: PluginType { + fileprivate let loggerId = "Moya_Logger" + fileprivate let dateFormatString = "dd/MM/yyyy HH:mm:ss" + fileprivate let dateFormatter = DateFormatter() + fileprivate let separator = ", " + fileprivate let terminator = "\n" + fileprivate let cURLTerminator = "\\\n" + fileprivate let output: (_ separator: String, _ terminator: String, _ items: Any...) -> Void + fileprivate let requestDataFormatter: ((Data) -> (String))? + fileprivate let responseDataFormatter: ((Data) -> (Data))? + + /// A Boolean value determing whether response body data should be logged. + public let isVerbose: Bool + public let cURL: Bool + + /// Initializes a NetworkLoggerPlugin. + public init(verbose: Bool = false, cURL: Bool = false, output: ((_ separator: String, _ terminator: String, _ items: Any...) -> Void)? = nil, requestDataFormatter: ((Data) -> (String))? = nil, responseDataFormatter: ((Data) -> (Data))? = nil) { + self.cURL = cURL + self.isVerbose = verbose + self.output = output ?? NetworkLoggerPlugin.reversedPrint + self.requestDataFormatter = requestDataFormatter + self.responseDataFormatter = responseDataFormatter + } + + public func willSend(_ request: RequestType, target: TargetType) { + if let request = request as? CustomDebugStringConvertible, cURL { + output(separator, terminator, request.debugDescription) + return + } + outputItems(logNetworkRequest(request.request as URLRequest?)) + } + + public func didReceive(_ result: Result, target: TargetType) { + if case .success(let response) = result { + outputItems(logNetworkResponse(response.response, data: response.data, target: target)) + } else { + outputItems(logNetworkResponse(nil, data: nil, target: target)) + } + } + + fileprivate func outputItems(_ items: [String]) { + if isVerbose { + items.forEach { output(separator, terminator, $0) } + } else { + output(separator, terminator, items) + } + } +} + +private extension NetworkLoggerPlugin { + + var date: String { + dateFormatter.dateFormat = dateFormatString + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + return dateFormatter.string(from: Date()) + } + + func format(_ loggerId: String, date: String, identifier: String, message: String) -> String { + return "\(loggerId): [\(date)] \(identifier): \(message)" + } + + func logNetworkRequest(_ request: URLRequest?) -> [String] { + + var output = [String]() + + output += [format(loggerId, date: date, identifier: "Request", message: request?.description ?? "(invalid request)")] + + if let headers = request?.allHTTPHeaderFields { + output += [format(loggerId, date: date, identifier: "Request Headers", message: headers.description)] + } + + if let bodyStream = request?.httpBodyStream { + output += [format(loggerId, date: date, identifier: "Request Body Stream", message: bodyStream.description)] + } + + if let httpMethod = request?.httpMethod { + output += [format(loggerId, date: date, identifier: "HTTP Request Method", message: httpMethod)] + } + + if let body = request?.httpBody, let stringOutput = requestDataFormatter?(body) ?? String(data: body, encoding: .utf8), isVerbose { + output += [format(loggerId, date: date, identifier: "Request Body", message: stringOutput)] + } + + return output + } + + func logNetworkResponse(_ response: HTTPURLResponse?, data: Data?, target: TargetType) -> [String] { + guard let response = response else { + return [format(loggerId, date: date, identifier: "Response", message: "Received empty network response for \(target).")] + } + + var output = [String]() + + output += [format(loggerId, date: date, identifier: "Response", message: response.description)] + + if let data = data, let stringData = String(data: responseDataFormatter?(data) ?? data, encoding: String.Encoding.utf8), isVerbose { + output += [stringData] + } + + return output + } +} + +fileprivate extension NetworkLoggerPlugin { + static func reversedPrint(_ separator: String, terminator: String, items: Any...) { + for item in items { + print(item, separator: separator, terminator: terminator) + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Response.swift b/Santander-Test/Pods/Moya/Sources/Moya/Response.swift new file mode 100644 index 00000000..42567ccc --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Response.swift @@ -0,0 +1,192 @@ +import Foundation + +/// Represents a response to a `MoyaProvider.request`. +public final class Response: CustomDebugStringConvertible, Equatable { + + /// The status code of the response. + public let statusCode: Int + + /// The response data. + public let data: Data + + /// The original URLRequest for the response. + public let request: URLRequest? + + /// The HTTPURLResponse object. + public let response: HTTPURLResponse? + + public init(statusCode: Int, data: Data, request: URLRequest? = nil, response: HTTPURLResponse? = nil) { + self.statusCode = statusCode + self.data = data + self.request = request + self.response = response + } + + /// A text description of the `Response`. + public var description: String { + return "Status Code: \(statusCode), Data Length: \(data.count)" + } + + /// A text description of the `Response`. Suitable for debugging. + public var debugDescription: String { + return description + } + + public static func == (lhs: Response, rhs: Response) -> Bool { + return lhs.statusCode == rhs.statusCode + && lhs.data == rhs.data + && lhs.response == rhs.response + } +} + +public extension Response { + + /** + Returns the `Response` if the `statusCode` falls within the specified range. + + - parameters: + - statusCodes: The range of acceptable status codes. + - throws: `MoyaError.statusCode` when others are encountered. + */ + public func filter(statusCodes: R) throws -> Response where R.Bound == Int { + guard statusCodes.contains(statusCode) else { + throw MoyaError.statusCode(self) + } + return self + } + + /** + Returns the `Response` if it has the specified `statusCode`. + + - parameters: + - statusCode: The acceptable status code. + - throws: `MoyaError.statusCode` when others are encountered. + */ + public func filter(statusCode: Int) throws -> Response { + return try filter(statusCodes: statusCode...statusCode) + } + + /** + Returns the `Response` if the `statusCode` falls within the range 200 - 299. + + - throws: `MoyaError.statusCode` when others are encountered. + */ + public func filterSuccessfulStatusCodes() throws -> Response { + return try filter(statusCodes: 200...299) + } + + /** + Returns the `Response` if the `statusCode` falls within the range 200 - 399. + + - throws: `MoyaError.statusCode` when others are encountered. + */ + public func filterSuccessfulStatusAndRedirectCodes() throws -> Response { + return try filter(statusCodes: 200...399) + } + + /// Maps data received from the signal into an Image. + func mapImage() throws -> Image { + guard let image = Image(data: data) else { + throw MoyaError.imageMapping(self) + } + return image + } + + /// Maps data received from the signal into a JSON object. + /// + /// - parameter failsOnEmptyData: A Boolean value determining + /// whether the mapping should fail if the data is empty. + func mapJSON(failsOnEmptyData: Bool = true) throws -> Any { + do { + return try JSONSerialization.jsonObject(with: data, options: .allowFragments) + } catch { + if data.count < 1 && !failsOnEmptyData { + return NSNull() + } + throw MoyaError.jsonMapping(self) + } + } + + /// Maps data received from the signal into a String. + /// + /// - parameter atKeyPath: Optional key path at which to parse string. + public func mapString(atKeyPath keyPath: String? = nil) throws -> String { + if let keyPath = keyPath { + // Key path was provided, try to parse string at key path + guard let jsonDictionary = try mapJSON() as? NSDictionary, + let string = jsonDictionary.value(forKeyPath: keyPath) as? String else { + throw MoyaError.stringMapping(self) + } + return string + } else { + // Key path was not provided, parse entire response as string + guard let string = String(data: data, encoding: .utf8) else { + throw MoyaError.stringMapping(self) + } + return string + } + } + + /// Maps data received from the signal into a Decodable object. + /// + /// - parameter atKeyPath: Optional key path at which to parse object. + /// - parameter using: A `JSONDecoder` instance which is used to decode data to an object. + func map(_ type: D.Type, atKeyPath keyPath: String? = nil, using decoder: JSONDecoder = JSONDecoder(), failsOnEmptyData: Bool = true) throws -> D { + let serializeToData: (Any) throws -> Data? = { (jsonObject) in + guard JSONSerialization.isValidJSONObject(jsonObject) else { + return nil + } + do { + return try JSONSerialization.data(withJSONObject: jsonObject) + } catch { + throw MoyaError.jsonMapping(self) + } + } + let jsonData: Data + keyPathCheck: if let keyPath = keyPath { + guard let jsonObject = (try mapJSON(failsOnEmptyData: failsOnEmptyData) as? NSDictionary)?.value(forKeyPath: keyPath) else { + if failsOnEmptyData { + throw MoyaError.jsonMapping(self) + } else { + jsonData = data + break keyPathCheck + } + } + + if let data = try serializeToData(jsonObject) { + jsonData = data + } else { + let wrappedJsonObject = ["value": jsonObject] + let wrappedJsonData: Data + if let data = try serializeToData(wrappedJsonObject) { + wrappedJsonData = data + } else { + throw MoyaError.jsonMapping(self) + } + do { + return try decoder.decode(DecodableWrapper.self, from: wrappedJsonData).value + } catch let error { + throw MoyaError.objectMapping(error, self) + } + } + } else { + jsonData = data + } + do { + if jsonData.count < 1 && !failsOnEmptyData { + if let emptyJSONObjectData = "{}".data(using: .utf8), let emptyDecodableValue = try? decoder.decode(D.self, from: emptyJSONObjectData) { + return emptyDecodableValue + } else if let emptyJSONArrayData = "[{}]".data(using: .utf8), let emptyDecodableValue = try? decoder.decode(D.self, from: emptyJSONArrayData) { + return emptyDecodableValue + } + } + return try decoder.decode(D.self, from: jsonData) + } catch let error { + throw MoyaError.objectMapping(error, self) + } + } +} + +private struct DecodableWrapper: Decodable { + let value: T +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/TargetType.swift b/Santander-Test/Pods/Moya/Sources/Moya/TargetType.swift new file mode 100644 index 00000000..48c77a00 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/TargetType.swift @@ -0,0 +1,44 @@ +import Foundation + +/// The protocol used to define the specifications necessary for a `MoyaProvider`. +public protocol TargetType { + + /// The target's base `URL`. + var baseURL: URL { get } + + /// The path to be appended to `baseURL` to form the full `URL`. + var path: String { get } + + /// The HTTP method used in the request. + var method: Moya.Method { get } + + /// Provides stub data for use in testing. + var sampleData: Data { get } + + /// The type of HTTP task to be performed. + var task: Task { get } + + /// The type of validation to perform on the request. Default is `.none`. + var validationType: ValidationType { get } + + /// The headers to be used in the request. + var headers: [String: String]? { get } +} + +public extension TargetType { + + /// The type of validation to perform on the request. Default is `.none`. + var validationType: ValidationType { + return .none + } +} + +// MARK: - Deprecated + +extension TargetType { + @available(*, deprecated: 11.0, message: + "TargetType's validate property has been deprecated in 11.0. Please use validationType: ValidationType.") + var validate: Bool { + return false + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/Task.swift b/Santander-Test/Pods/Moya/Sources/Moya/Task.swift new file mode 100644 index 00000000..191e2593 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/Task.swift @@ -0,0 +1,41 @@ +import Foundation + +/// Represents an HTTP task. +public enum Task { + + /// A request with no additional data. + case requestPlain + + /// A requests body set with data. + case requestData(Data) + + /// A request body set with `Encodable` type + case requestJSONEncodable(Encodable) + + /// A request body set with `Encodable` type and custom encoder + case requestCustomJSONEncodable(Encodable, encoder: JSONEncoder) + + /// A requests body set with encoded parameters. + case requestParameters(parameters: [String: Any], encoding: ParameterEncoding) + + /// A requests body set with data, combined with url parameters. + case requestCompositeData(bodyData: Data, urlParameters: [String: Any]) + + /// A requests body set with encoded parameters combined with url parameters. + case requestCompositeParameters(bodyParameters: [String: Any], bodyEncoding: ParameterEncoding, urlParameters: [String: Any]) + + /// A file upload task. + case uploadFile(URL) + + /// A "multipart/form-data" upload task. + case uploadMultipart([MultipartFormData]) + + /// A "multipart/form-data" upload task combined with url parameters. + case uploadCompositeMultipart([MultipartFormData], urlParameters: [String: Any]) + + /// A file download task to a destination. + case downloadDestination(DownloadDestination) + + /// A file download task to a destination with extra parameters using the given encoding. + case downloadParameters(parameters: [String: Any], encoding: ParameterEncoding, destination: DownloadDestination) +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/URL+Moya.swift b/Santander-Test/Pods/Moya/Sources/Moya/URL+Moya.swift new file mode 100644 index 00000000..9351fa43 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/URL+Moya.swift @@ -0,0 +1,16 @@ +import Foundation + +public extension URL { + + /// Initialize URL from Moya's `TargetType`. + init(target: T) { + // When a TargetType's path is empty, URL.appendingPathComponent may introduce trailing /, which may not be wanted in some cases + // See: https://github.com/Moya/Moya/pull/1053 + // And: https://github.com/Moya/Moya/issues/1049 + if target.path.isEmpty { + self = target.baseURL + } else { + self = target.baseURL.appendingPathComponent(target.path) + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/URLRequest+Encoding.swift b/Santander-Test/Pods/Moya/Sources/Moya/URLRequest+Encoding.swift new file mode 100644 index 00000000..232d86df --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/URLRequest+Encoding.swift @@ -0,0 +1,28 @@ +import Foundation + +internal extension URLRequest { + + mutating func encoded(encodable: Encodable, encoder: JSONEncoder = JSONEncoder()) throws -> URLRequest { + do { + let encodable = AnyEncodable(encodable) + httpBody = try encoder.encode(encodable) + + let contentTypeHeaderName = "Content-Type" + if value(forHTTPHeaderField: contentTypeHeaderName) == nil { + setValue("application/json", forHTTPHeaderField: contentTypeHeaderName) + } + + return self + } catch { + throw MoyaError.encodableMapping(error) + } + } + + func encoded(parameters: [String: Any], parameterEncoding: ParameterEncoding) throws -> URLRequest { + do { + return try parameterEncoding.encode(self, with: parameters) + } catch { + throw MoyaError.parameterEncoding(error) + } + } +} diff --git a/Santander-Test/Pods/Moya/Sources/Moya/ValidationType.swift b/Santander-Test/Pods/Moya/Sources/Moya/ValidationType.swift new file mode 100644 index 00000000..ffc8d8f2 --- /dev/null +++ b/Santander-Test/Pods/Moya/Sources/Moya/ValidationType.swift @@ -0,0 +1,47 @@ +import Foundation + +/// Represents the status codes to validate through Alamofire. +public enum ValidationType { + + /// No validation. + case none + + /// Validate success codes (only 2xx). + case successCodes + + /// Validate success codes and redirection codes (only 2xx and 3xx). + case successAndRedirectCodes + + /// Validate only the given status codes. + case customCodes([Int]) + + /// The list of HTTP status codes to validate. + var statusCodes: [Int] { + switch self { + case .successCodes: + return Array(200..<300) + case .successAndRedirectCodes: + return Array(200..<400) + case .customCodes(let codes): + return codes + case .none: + return [] + } + } +} + +extension ValidationType: Equatable { + + public static func == (lhs: ValidationType, rhs: ValidationType) -> Bool { + switch (lhs, rhs) { + case (.none, .none), + (.successCodes, .successCodes), + (.successAndRedirectCodes, .successAndRedirectCodes): + return true + case (.customCodes(let code1), .customCodes(let code2)): + return code1 == code2 + default: + return false + } + } +} diff --git a/Santander-Test/Pods/Pods.xcodeproj/project.pbxproj b/Santander-Test/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 00000000..6575d7f9 --- /dev/null +++ b/Santander-Test/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,2188 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 02FB225950EEA73C12B14A3D74A2096E /* IQUIView+IQKeyboardToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C69D1A74C5F456C4A803DFAC82CE09 /* IQUIView+IQKeyboardToolbar.swift */; }; + 059D92B7BBFBEC53E9A3B6E11C5C3B3A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05EA68EF87A95B537922D70C6DC0427 /* Response.swift */; }; + 077FC20E6644D0CC24318906467DB4EA /* StatusAlertUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D88DA44C23CFA4142EBCFF9B27EFF5 /* StatusAlertUtils.swift */; }; + 09067C2692931E434234858ADBC4C6D6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 09F37003965A0689DCDB98EEBF35988A /* PopTip+Transitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4C8B5361C64793391D06271956C190 /* PopTip+Transitions.swift */; }; + 0A39AF55285A3A4F7CBABB6D822FA4A3 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 364E4A632F5EC247E27102522F340CDD /* Alamofire.swift */; }; + 117A9A7A12D6466C58337E512984EA09 /* AMPopTip-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9B159763A6EABA1D15D3D6E8BB8EE3 /* AMPopTip-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 11F309B46EE6B36CB281409A579996C0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 1B131595178FA51FC0B7C2FB12ED5ADB /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D55C096C7852AD0FD5900FFDEC056A7 /* Endpoint.swift */; }; + 1C496E2C46E94DECD60F0B1C7CA511E4 /* NetworkLoggerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BBA6A8B1542C5036781E5CB1AD15100 /* NetworkLoggerPlugin.swift */; }; + 1D29D2ACADF961F69D32B06FA6A09E28 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71C48AA44E7FE6CA667F2F4956309ED0 /* Notifications.swift */; }; + 225E880F1459B17432E7DF840D03A3E7 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1271F76A652701E7081E1256BCEA38D5 /* Task.swift */; }; + 2F0E58380E710D15560B27A683FB6FE3 /* PopTip+Draw.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9DFC3190B2000CE552C11328102AD09 /* PopTip+Draw.swift */; }; + 3005629E42A84DED8C68F0C783DEE3A6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 30C07F53D870FC78B292E0E4F91FEE9E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 86F84942F79C4255F7497FC979196A65 /* QuartzCore.framework */; }; + 313895F247F6C04661152D6D4948C100 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7905351DDA208FF2BCDDDB073F863EA1 /* Image.swift */; }; + 314B8AD1268E89278CCE47BFEA79C43F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 31895E3CF848D69123E3A6DDEDBBF86F /* URL+Moya.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C902A8CC69883010175615A839CBCD /* URL+Moya.swift */; }; + 32083CD40AE0F52B9C5EE6520F51D866 /* AMPopTip-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1299D2802DF81035888B1EF8BD097426 /* AMPopTip-dummy.m */; }; + 329BBBB341642193502FA0DF77C011FA /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EFB6498CD7E9F2FB9A16399EB90D28B7 /* UIKit.framework */; }; + 355C18EEC82624A06A6CC93965258E33 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58D219C426FD2752002E027A43233F2 /* Request.swift */; }; + 36DD0B1007BF3C7BFE352704D5C415F7 /* IQTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E6C381B71ED17C8633C06674859A450 /* IQTextView.swift */; }; + 3BD1DAFE1F3E6C9E4EB0F2099B6D6FA2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 3E0749AF6C51BCF0E4A41CF1D6A76FED /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DACC21DA90E8AA013FAE6339E6E285 /* DispatchQueue+Alamofire.swift */; }; + 4467D1C8AABFA6FC321AC7CD6F327B48 /* IQUITextFieldView+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB4BE60C2E697DB2B80B3F13833F5E8 /* IQUITextFieldView+Additions.swift */; }; + 4A3294F2FCFF96D15DA6FD2D5D839788 /* IQUIView+Hierarchy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FD9D3900B13A0C0C03E7343CB19EF /* IQUIView+Hierarchy.swift */; }; + 4A42935426AB30760B47ADDE1BB1C39D /* NoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC703355C70EAC3B48FF2F65BEF1DAB /* NoError.swift */; }; + 4D3F118DF9909DFF62FD2453B5EED984 /* StatusAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793D9BD7B815AAF1CE1A5BC097261E71 /* StatusAlert.swift */; }; + 4E3E6EC07A4F261D041BA0C4CA258260 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1829CB35B6E3E9121C9CF36E8FCDBAE6 /* CoreGraphics.framework */; }; + 5677C54BF14ABA45675F2DEB98AFD0D1 /* IQKeyboardManagerSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 15C6D9AAE7AFB1D510907D85B8CAC6B6 /* IQKeyboardManagerSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58DAB19CBEC891D2FC9C085735782F33 /* StatusAlert-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BC9DAF4C343EFAC7BEC0603F45F19B3 /* StatusAlert-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5A18281634C290F12C5ABF249DEE9564 /* UIDevice+Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83455889E13854AD8ABDD99CD2CC39DA /* UIDevice+Platform.swift */; }; + 5D53EA65636A0B296726611E3C491CF7 /* Result-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FA89BCF8A139DFA2E60DDAB8DC3BDFE1 /* Result-dummy.m */; }; + 5D97ACC58BF197F2426B2C6853142DF5 /* NetworkActivityPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26A8905411437A4096352B51EC87710 /* NetworkActivityPlugin.swift */; }; + 60B6C2A003864AAD3A426448152F67BE /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED490BFE696CCBB0DFAA6B89FB2ACBF /* Timeline.swift */; }; + 633A43974DB81C2CCB1A2C3A7B1BFB37 /* IQKeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DECA3076074BA79EE14747097FA4232 /* IQKeyboardManager.swift */; }; + 6499810827FC895E23B2E30263617389 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C7B2747A259463EF9F583ED0735FFF /* Plugin.swift */; }; + 64C35953B1CEDA2F929331C7626611CC /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C381BBCB0AA8AFC6E94A60990BF256E /* MultipartFormData.swift */; }; + 68BB9452F3012913BABE12BE69C74785 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CDDA230D44A9D90E4E680D0F704ECA /* AnyEncodable.swift */; }; + 6AAA002A59C5011CB71E9B83E6803C6D /* ResultProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E07512BEE542791831615B14890629F /* ResultProtocol.swift */; }; + 6CA1BEBC175680F7D37A9C47917203B1 /* Pods-Santander-TestUITests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1410754A2D8AEF29CBAD3CF4DC4714BB /* Pods-Santander-TestUITests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D5139BE6172B47602185037BB82DF47 /* PopTip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9505602C5BF594EAAC4A87E55F2C370A /* PopTip.swift */; }; + 6E04C92190FD45451E6690122ABF62E9 /* Moya-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A64D86776CE0A15D579A12A1E3435FC /* Moya-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E64C6F2AF1EBF3B28AD5CC90D8D3E2F /* AnyError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8821CC7B10075A400D45D7905D391703 /* AnyError.swift */; }; + 6EFD003458AE7F689DEA720A2030C261 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF22CDAC0AC4795D80AD5F6585FDCB2 /* ResponseSerialization.swift */; }; + 700D3D95AF9520CB227846DFD943A2DA /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6CDA7D7EAF4FC3809223A51A3B6520 /* ParameterEncoding.swift */; }; + 73AB05789A4982944AF68DBD013E3EB7 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40F4581CF8577E41F9E26140B86166D4 /* NetworkReachabilityManager.swift */; }; + 772911DA6E33D1CBBC30131B7C8BDBB3 /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DD6D83DD5AE4B9C0CA1C80B73978D5F /* Alamofire-dummy.m */; }; + 778F9B551E5A346DA84BC46CBFFA1B22 /* CredentialsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB06C4313AEB9EB7D11D7BC4AC79893 /* CredentialsPlugin.swift */; }; + 77E8F0EB9FFBE2E3EB0C77095C644606 /* TaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756857999614F2D10AB3F2D2085DB1A7 /* TaskDelegate.swift */; }; + 7A6FFEFDCF07683D5E2976077212FD27 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 7BEFFCD672FD28B3D77927E392412FB9 /* Result-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 11E428322B8F20B4BD04558E00C12301 /* Result-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7E3538A6992A38276764936A9733493D /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5BE578C477043FD77994721DF2CF79 /* AFError.swift */; }; + 80111F83B0BCA39D24985D0F122F0001 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7C27ADB3CDDE59F459AB5DF4B23583F /* Result.swift */; }; + 88AE9E6392539A558E0F4417775EEA91 /* URLRequest+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BF013BC64BC6FEC0A17E5BB75E959F7 /* URLRequest+Encoding.swift */; }; + 88D92EDE37F1B362ECB8A6F8911DAC6E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + 8C7CD095FC0252C2920A4204819AF718 /* Pods-Santander-Test-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9FAA9914E2184A41D32A3A311B1860 /* Pods-Santander-Test-dummy.m */; }; + 8D2A6A90A6DDAF75EA52D471258545CC /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F87DA44039A5D2EC5DB495708C0AB39 /* ServerTrustPolicy.swift */; }; + 92E9BD908B71CFA1C6EDA2FA86BF1635 /* IQKeyboardManagerSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A5271583821AE646F1015B53E4B0D22C /* IQKeyboardManagerSwift-dummy.m */; }; + 9774AB3F3E8B65D7F1A644CCA6254D03 /* Pods-Santander-TestTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 638AF6E4AD3D6CF6AED6C2F601C4A809 /* Pods-Santander-TestTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 986EF56798DE2C7C65737AD9334E517D /* IQNSArray+Sort.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CA6ADC7C7F6B367BAD8CBEBF1343A6 /* IQNSArray+Sort.swift */; }; + 9EBD0113F23A6FBB0CDC07F66E77ED81 /* AccessTokenPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82244C246EAA54475DC81632EA200415 /* AccessTokenPlugin.swift */; }; + A1EC41966B261DCE460BCDE5124A1DBE /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94FBF4B876049E18090503AA42985DEC /* SessionDelegate.swift */; }; + A266363126A240900FCC5CCFE1EDB8BE /* Moya+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA4B95F0E633A9A9A8873BF0C4903533 /* Moya+Alamofire.swift */; }; + A380ABE3F1AF1CA7A14F9449BDC0B73F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + AA56769D8733D3F3E7976742D5ABA998 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821A0250D510FC0475D60FCF6CA59F8 /* Validation.swift */; }; + AB037232146E78285C5B8F6394D1A658 /* MultiTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F01307A1F45845DBA269F14D919F08 /* MultiTarget.swift */; }; + AEA829AB1A8AF2AD077A808AED6B178A /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19C5155BECC982BA93863CCE6A88B01 /* MultipartFormData.swift */; }; + B178B38FCD7F99B07422484FC7FD6861 /* IQKeyboardManager.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 4E6702644CDF6D2BB61A66FB6113E484 /* IQKeyboardManager.bundle */; }; + B1EB1CD6804A333102774EA1DE9F1B94 /* TargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7896B8BF68E862E13985590C6A5FA5E3 /* TargetType.swift */; }; + B277AE2A50A7C5F6B1BB9EDA5E1A97B8 /* Pods-Santander-TestTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E349DAE1260294B909EE4EF74E1CEE /* Pods-Santander-TestTests-dummy.m */; }; + B2A4A7D1BD7C4D59EA0FF0A49163A387 /* IQBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F79064F1A379876B04975C47909474E /* IQBarButtonItem.swift */; }; + B642F75A16A71ED991DB8A9DA65AD439 /* ValidationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3AC696856747E97BB0C7036CC738F0 /* ValidationType.swift */; }; + B6F6E4CC26451E91B59FAE0F6841DC1F /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 9007EDEB45A2B5EBA58AEBA614715C01 /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B71861435CE67838D5C615C7BF529F8D /* StatusAlert-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F52E478305B403B1169C3B03E7EBC07F /* StatusAlert-dummy.m */; }; + B888C49BC46C9A62CBF03A8FAD7126F1 /* IQInvocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C23191AA0D756503E4C42654D277AE58 /* IQInvocation.swift */; }; + B8FBCA47A659B360E2684C44D970F107 /* Pods-Santander-Test-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 064B8C62456DB7AEFF1AEFF7FC9E39F7 /* Pods-Santander-Test-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA61F495C0E45AA2A88D21D51575FF02 /* IQKeyboardReturnKeyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F1E014D4F06349619A808BCC3D021A /* IQKeyboardReturnKeyHandler.swift */; }; + C089BCD74D2A26B10186867D672DA506 /* UIDevice+Blur.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE397110EB17007AD8326CD6E6A3C67E /* UIDevice+Blur.swift */; }; + C5A49CAAB41E281C266C192EADBE4EB7 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45C4ABEAFCB4FA284808F5DBE887B20D /* Alamofire.framework */; }; + CD321D15156EF51F506D6183152B0E1C /* MoyaProvider+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 291574678DD74D639BB420782B57ECFC /* MoyaProvider+Internal.swift */; }; + D0D3B6F6481841FB0B0DC61653217179 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731A83702890AE7019DC4C29DB8E4EC9 /* Response.swift */; }; + D348D88FE7939869DE08BC623976B321 /* Moya-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7B15147E249F80E5874E77D31A450C /* Moya-dummy.m */; }; + D3A177F9BCAD7042B46E0A36CEDDBF8E /* IQTitleBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157AB990513DA582CA4369A962E2F52F /* IQTitleBarButtonItem.swift */; }; + D3FA0AA634AAEA99AB3FABC36BB4958C /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF6CAF521EED88DF2040CD240D5F4C3A /* SessionManager.swift */; }; + D7D49BAE6EAAD5EC2DAF370BCDE783A2 /* IQToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 214339B9ACA04BC4CA6C980F9545E964 /* IQToolbar.swift */; }; + D8F4B5841E455F5170B1C2FC30863B05 /* IQKeyboardManagerConstantsInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3007389BFB5C400ADA99640D19484908 /* IQKeyboardManagerConstantsInternal.swift */; }; + DD420F096096C518F8E6B44583BF6521 /* IQKeyboardManagerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE087567C6073974F01DC1BAA34DE25C /* IQKeyboardManagerConstants.swift */; }; + DD90258D5667ACC775417DE0E6EE6E77 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C283542EF8EEF865485C078FFAAD4FCC /* Result.framework */; }; + DDAC963022C8D96EBAAD2284780E0276 /* IQUIScrollView+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E4593C0360CBA8031C9DC2E64BEBC1 /* IQUIScrollView+Additions.swift */; }; + DE6AC2459484032563903F1BFCCD3756 /* MoyaProvider+Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = A621FA60DF20FE1423923BA34B6029F1 /* MoyaProvider+Defaults.swift */; }; + E15EA05765812FBDA4F4428F7B6BBF6D /* Pods-Santander-TestUITests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CD5133133E24C110EF3769A9D93DE159 /* Pods-Santander-TestUITests-dummy.m */; }; + E3334CC660C4BD5E41C1061B9DC56FF6 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EFB6498CD7E9F2FB9A16399EB90D28B7 /* UIKit.framework */; }; + E7E895502A83EDA1534DC002A0267D8D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */; }; + EB0DD8CEA1A69867A30267439C970440 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C43DAE9908ABB7744297D7506D770A /* Result.swift */; }; + EB65CF8FE56C314DBAF088FE713C9F88 /* MoyaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05C078CAC67FA75DFA89DF555E04156A /* MoyaProvider.swift */; }; + F0A9F7366B446EDAE4716768BCCADE6C /* IQPreviousNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B079EB42F083C7BEE0CFAE9F9CBBE17 /* IQPreviousNextView.swift */; }; + F44AA7A6E15EFE1927FC41B447D6EFD1 /* Cancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC9BF101351D627A2889DC5AC3F84506 /* Cancellable.swift */; }; + F852C0100AB6D9D2CE3E28FCA487C67E /* IQUIViewController+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45FB62426083CE495268BAF39405DFD /* IQUIViewController+Additions.swift */; }; + FF38A3D897A7C4228CAA5CCA71401376 /* MoyaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF38D7A18F03C664EC6EE0C9B22A7CE4 /* MoyaError.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 394DD8541DDFF77A28FBB238EEF723A3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 692F870FA017FAB61B5C3290C3BC5F54; + remoteInfo = AMPopTip; + }; + 49600E263670D867E35C1B1E918A2EEF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = EFF87187B46551C36797F2B899C54212; + remoteInfo = IQKeyboardManagerSwift; + }; + 6A4EDBBF08D44260DF0148CD85CDDA29 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4B0183450F95394DC0C49E326A20B42D; + remoteInfo = Result; + }; + 7D231DD4F66D54D4FD7A2F8FEBEF1FCC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4B0183450F95394DC0C49E326A20B42D; + remoteInfo = Result; + }; + 89B7C1F59BE279B9AC2C2AEA832F837B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3383968E74B5371B20BB519B170DC7FD; + remoteInfo = Alamofire; + }; + 969DCCA076A9B9D70065EA7CB5DB6A86 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F6743F51E8DAAB9AB7DB29A84A37F017; + remoteInfo = "Pods-Santander-Test"; + }; + A7BE02881176372328240EA60252BB3A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BAAB01D1BA82ABAE2B535579B77ED0CB; + remoteInfo = Moya; + }; + BE2FE96C842D970D743B817C3C29C800 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F6743F51E8DAAB9AB7DB29A84A37F017; + remoteInfo = "Pods-Santander-Test"; + }; + C2484FBCD16255B9BC841E5263AE7033 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3383968E74B5371B20BB519B170DC7FD; + remoteInfo = Alamofire; + }; + CB56E2428B8E2A6752A736019B630B48 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F6CF1E66B5C83639563339131FB4A512; + remoteInfo = StatusAlert; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00DACC21DA90E8AA013FAE6339E6E285 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; + 01B4720D4F3A72FEF3A1525F6D268E7A /* Pods-Santander-Test-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Santander-Test-frameworks.sh"; sourceTree = ""; }; + 05C078CAC67FA75DFA89DF555E04156A /* MoyaProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MoyaProvider.swift; path = Sources/Moya/MoyaProvider.swift; sourceTree = ""; }; + 05E349DAE1260294B909EE4EF74E1CEE /* Pods-Santander-TestTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Santander-TestTests-dummy.m"; sourceTree = ""; }; + 064B8C62456DB7AEFF1AEFF7FC9E39F7 /* Pods-Santander-Test-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Santander-Test-umbrella.h"; sourceTree = ""; }; + 08A81152EC75BCA30190A6265734A2E9 /* Pods-Santander-Test-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Santander-Test-acknowledgements.markdown"; sourceTree = ""; }; + 0A64D86776CE0A15D579A12A1E3435FC /* Moya-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Moya-umbrella.h"; sourceTree = ""; }; + 0A9370C1B5A38F7E656EE5FAB9855D66 /* AMPopTip.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = AMPopTip.xcconfig; sourceTree = ""; }; + 0ED490BFE696CCBB0DFAA6B89FB2ACBF /* Timeline.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeline.swift; path = Source/Timeline.swift; sourceTree = ""; }; + 1101C4E2CAB959A7BFD21FE75E332005 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire.framework; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1112BA5594638444A8463D9E6BD25F8C /* Pods-Santander-Test.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Santander-Test.debug.xcconfig"; sourceTree = ""; }; + 11E428322B8F20B4BD04558E00C12301 /* Result-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Result-umbrella.h"; sourceTree = ""; }; + 1271F76A652701E7081E1256BCEA38D5 /* Task.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Task.swift; path = Sources/Moya/Task.swift; sourceTree = ""; }; + 1299D2802DF81035888B1EF8BD097426 /* AMPopTip-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "AMPopTip-dummy.m"; sourceTree = ""; }; + 13E4593C0360CBA8031C9DC2E64BEBC1 /* IQUIScrollView+Additions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIScrollView+Additions.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift"; sourceTree = ""; }; + 1410754A2D8AEF29CBAD3CF4DC4714BB /* Pods-Santander-TestUITests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Santander-TestUITests-umbrella.h"; sourceTree = ""; }; + 145218CC34A5DEC0A2852C9D9724C4EB /* AMPopTip-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AMPopTip-prefix.pch"; sourceTree = ""; }; + 157AB990513DA582CA4369A962E2F52F /* IQTitleBarButtonItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQTitleBarButtonItem.swift; path = IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift; sourceTree = ""; }; + 15C6D9AAE7AFB1D510907D85B8CAC6B6 /* IQKeyboardManagerSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "IQKeyboardManagerSwift-umbrella.h"; sourceTree = ""; }; + 17F4B87CA0F449904B527F31FA799551 /* IQKeyboardManagerSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = IQKeyboardManagerSwift.modulemap; sourceTree = ""; }; + 1829CB35B6E3E9121C9CF36E8FCDBAE6 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + 18C43DAE9908ABB7744297D7506D770A /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Source/Result.swift; sourceTree = ""; }; + 1DD6D83DD5AE4B9C0CA1C80B73978D5F /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; + 1EB06C4313AEB9EB7D11D7BC4AC79893 /* CredentialsPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CredentialsPlugin.swift; path = Sources/Moya/Plugins/CredentialsPlugin.swift; sourceTree = ""; }; + 1FA9ACAB0572FE18DB0EFC4D0D489C7B /* Pods-Santander-TestTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Santander-TestTests-acknowledgements.markdown"; sourceTree = ""; }; + 214339B9ACA04BC4CA6C980F9545E964 /* IQToolbar.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQToolbar.swift; path = IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift; sourceTree = ""; }; + 27E2BB89559B46B7D51C7E161F1DE20E /* Pods-Santander-TestUITests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Santander-TestUITests-Info.plist"; sourceTree = ""; }; + 27F1E014D4F06349619A808BCC3D021A /* IQKeyboardReturnKeyHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardReturnKeyHandler.swift; path = IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift; sourceTree = ""; }; + 291574678DD74D639BB420782B57ECFC /* MoyaProvider+Internal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "MoyaProvider+Internal.swift"; path = "Sources/Moya/MoyaProvider+Internal.swift"; sourceTree = ""; }; + 2C381BBCB0AA8AFC6E94A60990BF256E /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Sources/Moya/MultipartFormData.swift; sourceTree = ""; }; + 2F309854407F224D9C16B2A9333BDADE /* IQKeyboardManagerSwift-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "IQKeyboardManagerSwift-Info.plist"; sourceTree = ""; }; + 3007389BFB5C400ADA99640D19484908 /* IQKeyboardManagerConstantsInternal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardManagerConstantsInternal.swift; path = IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift; sourceTree = ""; }; + 3177B689266F9D9900310F56859F5DB2 /* Pods-Santander-TestTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Santander-TestTests.modulemap"; sourceTree = ""; }; + 322053D9E9993C774F41A2AA6CB500A7 /* Pods-Santander-Test-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Santander-Test-acknowledgements.plist"; sourceTree = ""; }; + 34718F3B9548C70AF0984731DDCA7580 /* Pods-Santander-TestUITests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Santander-TestUITests-acknowledgements.markdown"; sourceTree = ""; }; + 364E4A632F5EC247E27102522F340CDD /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; + 3A7155821E550BB48423DD1D5A915B48 /* StatusAlert.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = StatusAlert.xcconfig; sourceTree = ""; }; + 3CF22CDAC0AC4795D80AD5F6585FDCB2 /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = ""; }; + 3D55C096C7852AD0FD5900FFDEC056A7 /* Endpoint.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Endpoint.swift; path = Sources/Moya/Endpoint.swift; sourceTree = ""; }; + 40F4581CF8577E41F9E26140B86166D4 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = ""; }; + 45C4ABEAFCB4FA284808F5DBE887B20D /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 47CDDA230D44A9D90E4E680D0F704ECA /* AnyEncodable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyEncodable.swift; path = Sources/Moya/AnyEncodable.swift; sourceTree = ""; }; + 4A815150FE659B719E0E5ECD4EC715BA /* Moya.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Moya.xcconfig; sourceTree = ""; }; + 4E07512BEE542791831615B14890629F /* ResultProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResultProtocol.swift; path = Result/ResultProtocol.swift; sourceTree = ""; }; + 4E6702644CDF6D2BB61A66FB6113E484 /* IQKeyboardManager.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = IQKeyboardManager.bundle; path = IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle; sourceTree = ""; }; + 4E8913DBA190C8B315A24290438509E2 /* IQKeyboardManagerSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "IQKeyboardManagerSwift-prefix.pch"; sourceTree = ""; }; + 500A039A12C1D4FC4DD1BA43A9523E91 /* Pods-Santander-Test.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Santander-Test.release.xcconfig"; sourceTree = ""; }; + 52A7962BA43F6C95A0167DE119923864 /* Alamofire-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Alamofire-Info.plist"; sourceTree = ""; }; + 54A5B4CE524043844F286D1E6867FD30 /* Moya.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Moya.modulemap; sourceTree = ""; }; + 555CDDB61EE2030B6AA753AE6C16F884 /* IQKeyboardManagerSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = IQKeyboardManagerSwift.framework; path = IQKeyboardManagerSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5D9FAA9914E2184A41D32A3A311B1860 /* Pods-Santander-Test-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Santander-Test-dummy.m"; sourceTree = ""; }; + 5E1CF47F8BD0FE48ABCF46917BFDB639 /* Pods-Santander-Test-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Santander-Test-Info.plist"; sourceTree = ""; }; + 5EDE2F0D0D20F8B9666B15E4E4980ABA /* AMPopTip-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "AMPopTip-Info.plist"; sourceTree = ""; }; + 5F87DA44039A5D2EC5DB495708C0AB39 /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustPolicy.swift; path = Source/ServerTrustPolicy.swift; sourceTree = ""; }; + 6009D670E92930F3D826B7B232BAE2DE /* Pods-Santander-TestTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Santander-TestTests.debug.xcconfig"; sourceTree = ""; }; + 61C7B2747A259463EF9F583ED0735FFF /* Plugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Plugin.swift; path = Sources/Moya/Plugin.swift; sourceTree = ""; }; + 62D8E1A78982244825E42655DEF31E2B /* AMPopTip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = AMPopTip.framework; path = AMPopTip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 638AF6E4AD3D6CF6AED6C2F601C4A809 /* Pods-Santander-TestTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Santander-TestTests-umbrella.h"; sourceTree = ""; }; + 6778F6C5A30EF7507D609F04AA77C837 /* StatusAlert-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "StatusAlert-Info.plist"; sourceTree = ""; }; + 6896D29C611F9F43AB11277DBED46F8C /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = ""; }; + 6B0D3C25CAABB853B0E1562B0AB04773 /* Alamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.xcconfig; sourceTree = ""; }; + 6BBA6A8B1542C5036781E5CB1AD15100 /* NetworkLoggerPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkLoggerPlugin.swift; path = Sources/Moya/Plugins/NetworkLoggerPlugin.swift; sourceTree = ""; }; + 6BF013BC64BC6FEC0A17E5BB75E959F7 /* URLRequest+Encoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLRequest+Encoding.swift"; path = "Sources/Moya/URLRequest+Encoding.swift"; sourceTree = ""; }; + 6DECA3076074BA79EE14747097FA4232 /* IQKeyboardManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardManager.swift; path = IQKeyboardManagerSwift/IQKeyboardManager.swift; sourceTree = ""; }; + 6E6C381B71ED17C8633C06674859A450 /* IQTextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQTextView.swift; path = IQKeyboardManagerSwift/IQTextView/IQTextView.swift; sourceTree = ""; }; + 6F7B15147E249F80E5874E77D31A450C /* Moya-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Moya-dummy.m"; sourceTree = ""; }; + 71C48AA44E7FE6CA667F2F4956309ED0 /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = ""; }; + 72E40C3B6E66E684514A3B0403D07BFE /* Pods-Santander-TestUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Santander-TestUITests.debug.xcconfig"; sourceTree = ""; }; + 731A83702890AE7019DC4C29DB8E4EC9 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Sources/Moya/Response.swift; sourceTree = ""; }; + 756857999614F2D10AB3F2D2085DB1A7 /* TaskDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskDelegate.swift; path = Source/TaskDelegate.swift; sourceTree = ""; }; + 7896B8BF68E862E13985590C6A5FA5E3 /* TargetType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TargetType.swift; path = Sources/Moya/TargetType.swift; sourceTree = ""; }; + 7905351DDA208FF2BCDDDB073F863EA1 /* Image.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Moya/Image.swift; sourceTree = ""; }; + 793D9BD7B815AAF1CE1A5BC097261E71 /* StatusAlert.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StatusAlert.swift; path = Sources/StatusAlert/StatusAlert.swift; sourceTree = ""; }; + 7A8D526CFB9CE5AE19261B6D3A1AA350 /* Pods-Santander-TestUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Santander-TestUITests.release.xcconfig"; sourceTree = ""; }; + 7B079EB42F083C7BEE0CFAE9F9CBBE17 /* IQPreviousNextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQPreviousNextView.swift; path = IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift; sourceTree = ""; }; + 7BB4646A4AE5EDF356A6C99B24EE2022 /* StatusAlert-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "StatusAlert-prefix.pch"; sourceTree = ""; }; + 82244C246EAA54475DC81632EA200415 /* AccessTokenPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AccessTokenPlugin.swift; path = Sources/Moya/Plugins/AccessTokenPlugin.swift; sourceTree = ""; }; + 82C69D1A74C5F456C4A803DFAC82CE09 /* IQUIView+IQKeyboardToolbar.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIView+IQKeyboardToolbar.swift"; path = "IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift"; sourceTree = ""; }; + 83455889E13854AD8ABDD99CD2CC39DA /* UIDevice+Platform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIDevice+Platform.swift"; path = "Sources/StatusAlert/Extensions/UIDevice+Platform.swift"; sourceTree = ""; }; + 86F84942F79C4255F7497FC979196A65 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + 8754AA9A73992CC94C22AC805D068C94 /* IQKeyboardManagerSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = IQKeyboardManagerSwift.xcconfig; sourceTree = ""; }; + 8821CC7B10075A400D45D7905D391703 /* AnyError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyError.swift; path = Result/AnyError.swift; sourceTree = ""; }; + 8BE8F1E8D7017DC3790579A436C90309 /* Moya-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Moya-Info.plist"; sourceTree = ""; }; + 8F79064F1A379876B04975C47909474E /* IQBarButtonItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQBarButtonItem.swift; path = IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift; sourceTree = ""; }; + 9007EDEB45A2B5EBA58AEBA614715C01 /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; + 94EA58095938F9B3C04D77F29F66A5D0 /* Pods_Santander_TestUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Santander_TestUITests.framework; path = "Pods-Santander-TestUITests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 94FBF4B876049E18090503AA42985DEC /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; + 9505602C5BF594EAAC4A87E55F2C370A /* PopTip.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PopTip.swift; path = Source/PopTip.swift; sourceTree = ""; }; + 9549855D3684432FAA2479D3F48F8D39 /* Result.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Result.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 955E3FDCA8BA841ACF0A01EFD5D71CE5 /* Result-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Result-Info.plist"; sourceTree = ""; }; + 95F01307A1F45845DBA269F14D919F08 /* MultiTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultiTarget.swift; path = Sources/Moya/MultiTarget.swift; sourceTree = ""; }; + 97D88DA44C23CFA4142EBCFF9B27EFF5 /* StatusAlertUtils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StatusAlertUtils.swift; path = Sources/StatusAlert/StatusAlertUtils.swift; sourceTree = ""; }; + 98EE8FF0327A2617229C1DCBE5E3BB81 /* Result.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Result.modulemap; sourceTree = ""; }; + 9BC9DAF4C343EFAC7BEC0603F45F19B3 /* StatusAlert-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "StatusAlert-umbrella.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; }; + 9E3AC696856747E97BB0C7036CC738F0 /* ValidationType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ValidationType.swift; path = Sources/Moya/ValidationType.swift; sourceTree = ""; }; + A45FB62426083CE495268BAF39405DFD /* IQUIViewController+Additions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIViewController+Additions.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift"; sourceTree = ""; }; + A5271583821AE646F1015B53E4B0D22C /* IQKeyboardManagerSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "IQKeyboardManagerSwift-dummy.m"; sourceTree = ""; }; + A621FA60DF20FE1423923BA34B6029F1 /* MoyaProvider+Defaults.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "MoyaProvider+Defaults.swift"; path = "Sources/Moya/MoyaProvider+Defaults.swift"; sourceTree = ""; }; + AA2C7453145C44C27B00E2138508290D /* Pods-Santander-Test.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Santander-Test.modulemap"; sourceTree = ""; }; + B19C5155BECC982BA93863CCE6A88B01 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = ""; }; + B2D49289BFD83B3A1582148FE3B1FBCD /* Pods-Santander-TestUITests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Santander-TestUITests-acknowledgements.plist"; sourceTree = ""; }; + B59FD9D3900B13A0C0C03E7343CB19EF /* IQUIView+Hierarchy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIView+Hierarchy.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift"; sourceTree = ""; }; + B821A0250D510FC0475D60FCF6CA59F8 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; + B9DFC3190B2000CE552C11328102AD09 /* PopTip+Draw.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "PopTip+Draw.swift"; path = "Source/PopTip+Draw.swift"; sourceTree = ""; }; + BF38D7A18F03C664EC6EE0C9B22A7CE4 /* MoyaError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MoyaError.swift; path = Sources/Moya/MoyaError.swift; sourceTree = ""; }; + BF7E09A17DBD8AB26665BBCDBC2CDFEA /* Moya-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Moya-prefix.pch"; sourceTree = ""; }; + BFB4BE60C2E697DB2B80B3F13833F5E8 /* IQUITextFieldView+Additions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUITextFieldView+Additions.swift"; path = "IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift"; sourceTree = ""; }; + C23191AA0D756503E4C42654D277AE58 /* IQInvocation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQInvocation.swift; path = IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift; sourceTree = ""; }; + C283542EF8EEF865485C078FFAAD4FCC /* Result.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C4F3617A6AD2DA24CB5D716B779284F8 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; + C7C27ADB3CDDE59F459AB5DF4B23583F /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Result/Result.swift; sourceTree = ""; }; + CD5133133E24C110EF3769A9D93DE159 /* Pods-Santander-TestUITests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Santander-TestUITests-dummy.m"; sourceTree = ""; }; + CD7B595FE6596FB636EB30207B2F428A /* Pods-Santander-TestUITests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Santander-TestUITests.modulemap"; sourceTree = ""; }; + D02D3D21156144D3F4B936265D29A56A /* Pods-Santander-TestTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Santander-TestTests-acknowledgements.plist"; sourceTree = ""; }; + D05EA68EF87A95B537922D70C6DC0427 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = ""; }; + D1CB1588C635AF0E142CA79C1D791345 /* Result.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Result.xcconfig; sourceTree = ""; }; + D26A8905411437A4096352B51EC87710 /* NetworkActivityPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkActivityPlugin.swift; path = Sources/Moya/Plugins/NetworkActivityPlugin.swift; sourceTree = ""; }; + D90FE54F5D11CC69794D8BED8AA7D675 /* Pods-Santander-TestTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Santander-TestTests-Info.plist"; sourceTree = ""; }; + DA5BE578C477043FD77994721DF2CF79 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = ""; }; + DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + DF6CAF521EED88DF2040CD240D5F4C3A /* SessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Source/SessionManager.swift; sourceTree = ""; }; + E088536B128E3AB516E66A17708F6322 /* StatusAlert.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = StatusAlert.modulemap; sourceTree = ""; }; + E1C2681F4C36A1893AAE9A6548862463 /* Pods_Santander_Test.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Santander_Test.framework; path = "Pods-Santander-Test.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + E58D219C426FD2752002E027A43233F2 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = ""; }; + E819577D2B1029AE4C9270C33FFFE71E /* Result-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Result-prefix.pch"; sourceTree = ""; }; + E91E237C060ECC2B4366CA83D0CC2C5E /* Pods_Santander_TestTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Santander_TestTests.framework; path = "Pods-Santander-TestTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + E98DD21778D48883FC8AFC80B446C777 /* AMPopTip.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = AMPopTip.modulemap; sourceTree = ""; }; + ED4C8B5361C64793391D06271956C190 /* PopTip+Transitions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "PopTip+Transitions.swift"; path = "Source/PopTip+Transitions.swift"; sourceTree = ""; }; + EE397110EB17007AD8326CD6E6A3C67E /* UIDevice+Blur.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIDevice+Blur.swift"; path = "Sources/StatusAlert/Extensions/UIDevice+Blur.swift"; sourceTree = ""; }; + EFB6498CD7E9F2FB9A16399EB90D28B7 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + F001F52BA7BA1ED9D50942621A44C511 /* StatusAlert.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = StatusAlert.framework; path = StatusAlert.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F3708A4B829835EBC49B784D84968130 /* Pods-Santander-TestTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Santander-TestTests.release.xcconfig"; sourceTree = ""; }; + F52E478305B403B1169C3B03E7EBC07F /* StatusAlert-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "StatusAlert-dummy.m"; sourceTree = ""; }; + F7EF57EFB1DD70A9C3098E6C3F95C151 /* Moya.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Moya.framework; path = Moya.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F8C902A8CC69883010175615A839CBCD /* URL+Moya.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URL+Moya.swift"; path = "Sources/Moya/URL+Moya.swift"; sourceTree = ""; }; + F8CA6ADC7C7F6B367BAD8CBEBF1343A6 /* IQNSArray+Sort.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQNSArray+Sort.swift"; path = "IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift"; sourceTree = ""; }; + FA4B95F0E633A9A9A8873BF0C4903533 /* Moya+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Moya+Alamofire.swift"; path = "Sources/Moya/Moya+Alamofire.swift"; sourceTree = ""; }; + FA89BCF8A139DFA2E60DDAB8DC3BDFE1 /* Result-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Result-dummy.m"; sourceTree = ""; }; + FA9B159763A6EABA1D15D3D6E8BB8EE3 /* AMPopTip-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AMPopTip-umbrella.h"; sourceTree = ""; }; + FAC703355C70EAC3B48FF2F65BEF1DAB /* NoError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NoError.swift; path = Result/NoError.swift; sourceTree = ""; }; + FC9BF101351D627A2889DC5AC3F84506 /* Cancellable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cancellable.swift; path = Sources/Moya/Cancellable.swift; sourceTree = ""; }; + FD6CDA7D7EAF4FC3809223A51A3B6520 /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = ""; }; + FE087567C6073974F01DC1BAA34DE25C /* IQKeyboardManagerConstants.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardManagerConstants.swift; path = IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2FAF1D107A0B9FB6A19D58960CEC7344 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3005629E42A84DED8C68F0C783DEE3A6 /* Foundation.framework in Frameworks */, + 329BBBB341642193502FA0DF77C011FA /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 48ACF90A780D9991A643D8E81E6BEAEA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C5A49CAAB41E281C266C192EADBE4EB7 /* Alamofire.framework in Frameworks */, + 11F309B46EE6B36CB281409A579996C0 /* Foundation.framework in Frameworks */, + DD90258D5667ACC775417DE0E6EE6E77 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61ADA0C1B2897AA3D357547B127F1565 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 314B8AD1268E89278CCE47BFEA79C43F /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6ABC78A038A90F8F48964B9677372CB6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 09067C2692931E434234858ADBC4C6D6 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 89BB5572C584E93D4B27FFE1EE1E6FFF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 88D92EDE37F1B362ECB8A6F8911DAC6E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B886D41EF763DE44F455CB7E74E9A560 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4E3E6EC07A4F261D041BA0C4CA258260 /* CoreGraphics.framework in Frameworks */, + A380ABE3F1AF1CA7A14F9449BDC0B73F /* Foundation.framework in Frameworks */, + 30C07F53D870FC78B292E0E4F91FEE9E /* QuartzCore.framework in Frameworks */, + E3334CC660C4BD5E41C1061B9DC56FF6 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B88C9F2B38E16034967AC3610B938A97 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E7E895502A83EDA1534DC002A0267D8D /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D22CBC0AEFD5C611C593833B6DC007E7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A6FFEFDCF07683D5E2976077212FD27 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F1626ADC3A7EC435D2722B3173355D36 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BD1DAFE1F3E6C9E4EB0F2099B6D6FA2 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 04CC267E9A7BDA107E37B245ACDB31A7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 45C4ABEAFCB4FA284808F5DBE887B20D /* Alamofire.framework */, + C283542EF8EEF865485C078FFAAD4FCC /* Result.framework */, + A98624AAFB2B73C576EB4B41F1AA8197 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 1A1D14300A58FDC9F70D8885755512A1 /* Support Files */ = { + isa = PBXGroup; + children = ( + 6896D29C611F9F43AB11277DBED46F8C /* Alamofire.modulemap */, + 6B0D3C25CAABB853B0E1562B0AB04773 /* Alamofire.xcconfig */, + 1DD6D83DD5AE4B9C0CA1C80B73978D5F /* Alamofire-dummy.m */, + 52A7962BA43F6C95A0167DE119923864 /* Alamofire-Info.plist */, + C4F3617A6AD2DA24CB5D716B779284F8 /* Alamofire-prefix.pch */, + 9007EDEB45A2B5EBA58AEBA614715C01 /* Alamofire-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Alamofire"; + sourceTree = ""; + }; + 3EB0C83CAF9F96A61BFFC8BC01F5CAD8 /* Support Files */ = { + isa = PBXGroup; + children = ( + 17F4B87CA0F449904B527F31FA799551 /* IQKeyboardManagerSwift.modulemap */, + 8754AA9A73992CC94C22AC805D068C94 /* IQKeyboardManagerSwift.xcconfig */, + A5271583821AE646F1015B53E4B0D22C /* IQKeyboardManagerSwift-dummy.m */, + 2F309854407F224D9C16B2A9333BDADE /* IQKeyboardManagerSwift-Info.plist */, + 4E8913DBA190C8B315A24290438509E2 /* IQKeyboardManagerSwift-prefix.pch */, + 15C6D9AAE7AFB1D510907D85B8CAC6B6 /* IQKeyboardManagerSwift-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/IQKeyboardManagerSwift"; + sourceTree = ""; + }; + 4E62EEA8F485FE09ADA73F5E5D814811 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + BA0EF3892F9082EED2C4A464C1FF099B /* Pods-Santander-Test */, + 863C6216E2FC106570AB6ECF1BDDC850 /* Pods-Santander-TestTests */, + A9078A16582838D0FA5E20E72F39180F /* Pods-Santander-TestUITests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 56285F3447EC0E47169E9F16B26D0E4F /* Support Files */ = { + isa = PBXGroup; + children = ( + E088536B128E3AB516E66A17708F6322 /* StatusAlert.modulemap */, + 3A7155821E550BB48423DD1D5A915B48 /* StatusAlert.xcconfig */, + F52E478305B403B1169C3B03E7EBC07F /* StatusAlert-dummy.m */, + 6778F6C5A30EF7507D609F04AA77C837 /* StatusAlert-Info.plist */, + 7BB4646A4AE5EDF356A6C99B24EE2022 /* StatusAlert-prefix.pch */, + 9BC9DAF4C343EFAC7BEC0603F45F19B3 /* StatusAlert-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/StatusAlert"; + sourceTree = ""; + }; + 582E78C5DF50D88F3BC4886372AF89FE /* Support Files */ = { + isa = PBXGroup; + children = ( + E98DD21778D48883FC8AFC80B446C777 /* AMPopTip.modulemap */, + 0A9370C1B5A38F7E656EE5FAB9855D66 /* AMPopTip.xcconfig */, + 1299D2802DF81035888B1EF8BD097426 /* AMPopTip-dummy.m */, + 5EDE2F0D0D20F8B9666B15E4E4980ABA /* AMPopTip-Info.plist */, + 145218CC34A5DEC0A2852C9D9724C4EB /* AMPopTip-prefix.pch */, + FA9B159763A6EABA1D15D3D6E8BB8EE3 /* AMPopTip-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/AMPopTip"; + sourceTree = ""; + }; + 6A057F5B7E36364DD5B9A90C68150548 /* Support Files */ = { + isa = PBXGroup; + children = ( + 54A5B4CE524043844F286D1E6867FD30 /* Moya.modulemap */, + 4A815150FE659B719E0E5ECD4EC715BA /* Moya.xcconfig */, + 6F7B15147E249F80E5874E77D31A450C /* Moya-dummy.m */, + 8BE8F1E8D7017DC3790579A436C90309 /* Moya-Info.plist */, + BF7E09A17DBD8AB26665BBCDBC2CDFEA /* Moya-prefix.pch */, + 0A64D86776CE0A15D579A12A1E3435FC /* Moya-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Moya"; + sourceTree = ""; + }; + 6CE46993FE8FC7D92C2DAEEE52E274C0 /* AMPopTip */ = { + isa = PBXGroup; + children = ( + 9505602C5BF594EAAC4A87E55F2C370A /* PopTip.swift */, + B9DFC3190B2000CE552C11328102AD09 /* PopTip+Draw.swift */, + ED4C8B5361C64793391D06271956C190 /* PopTip+Transitions.swift */, + 582E78C5DF50D88F3BC4886372AF89FE /* Support Files */, + ); + name = AMPopTip; + path = AMPopTip; + sourceTree = ""; + }; + 845B6CE234ED8174B179EA9F75D1594D /* Support Files */ = { + isa = PBXGroup; + children = ( + 98EE8FF0327A2617229C1DCBE5E3BB81 /* Result.modulemap */, + D1CB1588C635AF0E142CA79C1D791345 /* Result.xcconfig */, + FA89BCF8A139DFA2E60DDAB8DC3BDFE1 /* Result-dummy.m */, + 955E3FDCA8BA841ACF0A01EFD5D71CE5 /* Result-Info.plist */, + E819577D2B1029AE4C9270C33FFFE71E /* Result-prefix.pch */, + 11E428322B8F20B4BD04558E00C12301 /* Result-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Result"; + sourceTree = ""; + }; + 863C6216E2FC106570AB6ECF1BDDC850 /* Pods-Santander-TestTests */ = { + isa = PBXGroup; + children = ( + 3177B689266F9D9900310F56859F5DB2 /* Pods-Santander-TestTests.modulemap */, + 1FA9ACAB0572FE18DB0EFC4D0D489C7B /* Pods-Santander-TestTests-acknowledgements.markdown */, + D02D3D21156144D3F4B936265D29A56A /* Pods-Santander-TestTests-acknowledgements.plist */, + 05E349DAE1260294B909EE4EF74E1CEE /* Pods-Santander-TestTests-dummy.m */, + D90FE54F5D11CC69794D8BED8AA7D675 /* Pods-Santander-TestTests-Info.plist */, + 638AF6E4AD3D6CF6AED6C2F601C4A809 /* Pods-Santander-TestTests-umbrella.h */, + 6009D670E92930F3D826B7B232BAE2DE /* Pods-Santander-TestTests.debug.xcconfig */, + F3708A4B829835EBC49B784D84968130 /* Pods-Santander-TestTests.release.xcconfig */, + ); + name = "Pods-Santander-TestTests"; + path = "Target Support Files/Pods-Santander-TestTests"; + sourceTree = ""; + }; + 8DC894E19E1D4CF5F40B2C6C7A9211D1 /* IQKeyboardManagerSwift */ = { + isa = PBXGroup; + children = ( + 8F79064F1A379876B04975C47909474E /* IQBarButtonItem.swift */, + C23191AA0D756503E4C42654D277AE58 /* IQInvocation.swift */, + 6DECA3076074BA79EE14747097FA4232 /* IQKeyboardManager.swift */, + FE087567C6073974F01DC1BAA34DE25C /* IQKeyboardManagerConstants.swift */, + 3007389BFB5C400ADA99640D19484908 /* IQKeyboardManagerConstantsInternal.swift */, + 27F1E014D4F06349619A808BCC3D021A /* IQKeyboardReturnKeyHandler.swift */, + F8CA6ADC7C7F6B367BAD8CBEBF1343A6 /* IQNSArray+Sort.swift */, + 7B079EB42F083C7BEE0CFAE9F9CBBE17 /* IQPreviousNextView.swift */, + 6E6C381B71ED17C8633C06674859A450 /* IQTextView.swift */, + 157AB990513DA582CA4369A962E2F52F /* IQTitleBarButtonItem.swift */, + 214339B9ACA04BC4CA6C980F9545E964 /* IQToolbar.swift */, + 13E4593C0360CBA8031C9DC2E64BEBC1 /* IQUIScrollView+Additions.swift */, + BFB4BE60C2E697DB2B80B3F13833F5E8 /* IQUITextFieldView+Additions.swift */, + B59FD9D3900B13A0C0C03E7343CB19EF /* IQUIView+Hierarchy.swift */, + 82C69D1A74C5F456C4A803DFAC82CE09 /* IQUIView+IQKeyboardToolbar.swift */, + A45FB62426083CE495268BAF39405DFD /* IQUIViewController+Additions.swift */, + BB46FDFCA3E7D48F2873D5F373BAC289 /* Resources */, + 3EB0C83CAF9F96A61BFFC8BC01F5CAD8 /* Support Files */, + ); + name = IQKeyboardManagerSwift; + path = IQKeyboardManagerSwift; + sourceTree = ""; + }; + A9078A16582838D0FA5E20E72F39180F /* Pods-Santander-TestUITests */ = { + isa = PBXGroup; + children = ( + CD7B595FE6596FB636EB30207B2F428A /* Pods-Santander-TestUITests.modulemap */, + 34718F3B9548C70AF0984731DDCA7580 /* Pods-Santander-TestUITests-acknowledgements.markdown */, + B2D49289BFD83B3A1582148FE3B1FBCD /* Pods-Santander-TestUITests-acknowledgements.plist */, + CD5133133E24C110EF3769A9D93DE159 /* Pods-Santander-TestUITests-dummy.m */, + 27E2BB89559B46B7D51C7E161F1DE20E /* Pods-Santander-TestUITests-Info.plist */, + 1410754A2D8AEF29CBAD3CF4DC4714BB /* Pods-Santander-TestUITests-umbrella.h */, + 72E40C3B6E66E684514A3B0403D07BFE /* Pods-Santander-TestUITests.debug.xcconfig */, + 7A8D526CFB9CE5AE19261B6D3A1AA350 /* Pods-Santander-TestUITests.release.xcconfig */, + ); + name = "Pods-Santander-TestUITests"; + path = "Target Support Files/Pods-Santander-TestUITests"; + sourceTree = ""; + }; + A98624AAFB2B73C576EB4B41F1AA8197 /* iOS */ = { + isa = PBXGroup; + children = ( + 1829CB35B6E3E9121C9CF36E8FCDBAE6 /* CoreGraphics.framework */, + DC70403EFC8F9FE430904EE770C96364 /* Foundation.framework */, + 86F84942F79C4255F7497FC979196A65 /* QuartzCore.framework */, + EFB6498CD7E9F2FB9A16399EB90D28B7 /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + BA0EF3892F9082EED2C4A464C1FF099B /* Pods-Santander-Test */ = { + isa = PBXGroup; + children = ( + AA2C7453145C44C27B00E2138508290D /* Pods-Santander-Test.modulemap */, + 08A81152EC75BCA30190A6265734A2E9 /* Pods-Santander-Test-acknowledgements.markdown */, + 322053D9E9993C774F41A2AA6CB500A7 /* Pods-Santander-Test-acknowledgements.plist */, + 5D9FAA9914E2184A41D32A3A311B1860 /* Pods-Santander-Test-dummy.m */, + 01B4720D4F3A72FEF3A1525F6D268E7A /* Pods-Santander-Test-frameworks.sh */, + 5E1CF47F8BD0FE48ABCF46917BFDB639 /* Pods-Santander-Test-Info.plist */, + 064B8C62456DB7AEFF1AEFF7FC9E39F7 /* Pods-Santander-Test-umbrella.h */, + 1112BA5594638444A8463D9E6BD25F8C /* Pods-Santander-Test.debug.xcconfig */, + 500A039A12C1D4FC4DD1BA43A9523E91 /* Pods-Santander-Test.release.xcconfig */, + ); + name = "Pods-Santander-Test"; + path = "Target Support Files/Pods-Santander-Test"; + sourceTree = ""; + }; + BB46FDFCA3E7D48F2873D5F373BAC289 /* Resources */ = { + isa = PBXGroup; + children = ( + 4E6702644CDF6D2BB61A66FB6113E484 /* IQKeyboardManager.bundle */, + ); + name = Resources; + sourceTree = ""; + }; + BCB6573B8E41B4C4C0480E410B9E0769 /* Core */ = { + isa = PBXGroup; + children = ( + 82244C246EAA54475DC81632EA200415 /* AccessTokenPlugin.swift */, + 47CDDA230D44A9D90E4E680D0F704ECA /* AnyEncodable.swift */, + FC9BF101351D627A2889DC5AC3F84506 /* Cancellable.swift */, + 1EB06C4313AEB9EB7D11D7BC4AC79893 /* CredentialsPlugin.swift */, + 3D55C096C7852AD0FD5900FFDEC056A7 /* Endpoint.swift */, + 7905351DDA208FF2BCDDDB073F863EA1 /* Image.swift */, + FA4B95F0E633A9A9A8873BF0C4903533 /* Moya+Alamofire.swift */, + BF38D7A18F03C664EC6EE0C9B22A7CE4 /* MoyaError.swift */, + 05C078CAC67FA75DFA89DF555E04156A /* MoyaProvider.swift */, + A621FA60DF20FE1423923BA34B6029F1 /* MoyaProvider+Defaults.swift */, + 291574678DD74D639BB420782B57ECFC /* MoyaProvider+Internal.swift */, + 2C381BBCB0AA8AFC6E94A60990BF256E /* MultipartFormData.swift */, + 95F01307A1F45845DBA269F14D919F08 /* MultiTarget.swift */, + D26A8905411437A4096352B51EC87710 /* NetworkActivityPlugin.swift */, + 6BBA6A8B1542C5036781E5CB1AD15100 /* NetworkLoggerPlugin.swift */, + 61C7B2747A259463EF9F583ED0735FFF /* Plugin.swift */, + 731A83702890AE7019DC4C29DB8E4EC9 /* Response.swift */, + 7896B8BF68E862E13985590C6A5FA5E3 /* TargetType.swift */, + 1271F76A652701E7081E1256BCEA38D5 /* Task.swift */, + F8C902A8CC69883010175615A839CBCD /* URL+Moya.swift */, + 6BF013BC64BC6FEC0A17E5BB75E959F7 /* URLRequest+Encoding.swift */, + 9E3AC696856747E97BB0C7036CC738F0 /* ValidationType.swift */, + ); + name = Core; + sourceTree = ""; + }; + BD45C81BD98CE392E5786E7DEECC93A4 /* Products */ = { + isa = PBXGroup; + children = ( + 1101C4E2CAB959A7BFD21FE75E332005 /* Alamofire.framework */, + 62D8E1A78982244825E42655DEF31E2B /* AMPopTip.framework */, + 555CDDB61EE2030B6AA753AE6C16F884 /* IQKeyboardManagerSwift.framework */, + F7EF57EFB1DD70A9C3098E6C3F95C151 /* Moya.framework */, + E1C2681F4C36A1893AAE9A6548862463 /* Pods_Santander_Test.framework */, + E91E237C060ECC2B4366CA83D0CC2C5E /* Pods_Santander_TestTests.framework */, + 94EA58095938F9B3C04D77F29F66A5D0 /* Pods_Santander_TestUITests.framework */, + 9549855D3684432FAA2479D3F48F8D39 /* Result.framework */, + F001F52BA7BA1ED9D50942621A44C511 /* StatusAlert.framework */, + ); + name = Products; + sourceTree = ""; + }; + C002BDB3E66CB8748CFC85763CCBEDFA /* StatusAlert */ = { + isa = PBXGroup; + children = ( + 793D9BD7B815AAF1CE1A5BC097261E71 /* StatusAlert.swift */, + 97D88DA44C23CFA4142EBCFF9B27EFF5 /* StatusAlertUtils.swift */, + EE397110EB17007AD8326CD6E6A3C67E /* UIDevice+Blur.swift */, + 83455889E13854AD8ABDD99CD2CC39DA /* UIDevice+Platform.swift */, + 56285F3447EC0E47169E9F16B26D0E4F /* Support Files */, + ); + name = StatusAlert; + path = StatusAlert; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + 04CC267E9A7BDA107E37B245ACDB31A7 /* Frameworks */, + E21D492D1DCAFF433C7FC3AF8A0AE42D /* Pods */, + BD45C81BD98CE392E5786E7DEECC93A4 /* Products */, + 4E62EEA8F485FE09ADA73F5E5D814811 /* Targets Support Files */, + ); + sourceTree = ""; + }; + D0ACFE16757C4FCD83D3F516DFA35C7B /* Moya */ = { + isa = PBXGroup; + children = ( + BCB6573B8E41B4C4C0480E410B9E0769 /* Core */, + 6A057F5B7E36364DD5B9A90C68150548 /* Support Files */, + ); + name = Moya; + path = Moya; + sourceTree = ""; + }; + E21D492D1DCAFF433C7FC3AF8A0AE42D /* Pods */ = { + isa = PBXGroup; + children = ( + F1A4F2099A92E769CD6E2E01FE4EAD32 /* Alamofire */, + 6CE46993FE8FC7D92C2DAEEE52E274C0 /* AMPopTip */, + 8DC894E19E1D4CF5F40B2C6C7A9211D1 /* IQKeyboardManagerSwift */, + D0ACFE16757C4FCD83D3F516DFA35C7B /* Moya */, + FE011F741BCD2FC6EFC2A01E90EFA247 /* Result */, + C002BDB3E66CB8748CFC85763CCBEDFA /* StatusAlert */, + ); + name = Pods; + sourceTree = ""; + }; + F1A4F2099A92E769CD6E2E01FE4EAD32 /* Alamofire */ = { + isa = PBXGroup; + children = ( + DA5BE578C477043FD77994721DF2CF79 /* AFError.swift */, + 364E4A632F5EC247E27102522F340CDD /* Alamofire.swift */, + 00DACC21DA90E8AA013FAE6339E6E285 /* DispatchQueue+Alamofire.swift */, + B19C5155BECC982BA93863CCE6A88B01 /* MultipartFormData.swift */, + 40F4581CF8577E41F9E26140B86166D4 /* NetworkReachabilityManager.swift */, + 71C48AA44E7FE6CA667F2F4956309ED0 /* Notifications.swift */, + FD6CDA7D7EAF4FC3809223A51A3B6520 /* ParameterEncoding.swift */, + E58D219C426FD2752002E027A43233F2 /* Request.swift */, + D05EA68EF87A95B537922D70C6DC0427 /* Response.swift */, + 3CF22CDAC0AC4795D80AD5F6585FDCB2 /* ResponseSerialization.swift */, + 18C43DAE9908ABB7744297D7506D770A /* Result.swift */, + 5F87DA44039A5D2EC5DB495708C0AB39 /* ServerTrustPolicy.swift */, + 94FBF4B876049E18090503AA42985DEC /* SessionDelegate.swift */, + DF6CAF521EED88DF2040CD240D5F4C3A /* SessionManager.swift */, + 756857999614F2D10AB3F2D2085DB1A7 /* TaskDelegate.swift */, + 0ED490BFE696CCBB0DFAA6B89FB2ACBF /* Timeline.swift */, + B821A0250D510FC0475D60FCF6CA59F8 /* Validation.swift */, + 1A1D14300A58FDC9F70D8885755512A1 /* Support Files */, + ); + name = Alamofire; + path = Alamofire; + sourceTree = ""; + }; + FE011F741BCD2FC6EFC2A01E90EFA247 /* Result */ = { + isa = PBXGroup; + children = ( + 8821CC7B10075A400D45D7905D391703 /* AnyError.swift */, + FAC703355C70EAC3B48FF2F65BEF1DAB /* NoError.swift */, + C7C27ADB3CDDE59F459AB5DF4B23583F /* Result.swift */, + 4E07512BEE542791831615B14890629F /* ResultProtocol.swift */, + 845B6CE234ED8174B179EA9F75D1594D /* Support Files */, + ); + name = Result; + path = Result; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 192512EEECEB8B3F2388EF7CD98FE16A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 117A9A7A12D6466C58337E512984EA09 /* AMPopTip-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1FD6F66D0198C70C4000AE75095B649C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B8FBCA47A659B360E2684C44D970F107 /* Pods-Santander-Test-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23D70D9651451F43E7E8CC1A87B4B7E9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B6F6E4CC26451E91B59FAE0F6841DC1F /* Alamofire-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 24E0A5A25C6279C4B63841C53779093E /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 9774AB3F3E8B65D7F1A644CCA6254D03 /* Pods-Santander-TestTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5FD2C029FC627D78FBEBD0C3829ACA56 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5677C54BF14ABA45675F2DEB98AFD0D1 /* IQKeyboardManagerSwift-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 803D1C8389DC36A273C1596A2371062C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7BEFFCD672FD28B3D77927E392412FB9 /* Result-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 838FDCE6748E1A68E1C0C4C39D6C400A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6CA1BEBC175680F7D37A9C47917203B1 /* Pods-Santander-TestUITests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9996E8B8F3A60AC7419C8E1EC55DE0E7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 58DAB19CBEC891D2FC9C085735782F33 /* StatusAlert-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D7A6588CEA919559E5AE0CF2E072388A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6E04C92190FD45451E6690122ABF62E9 /* Moya-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 3383968E74B5371B20BB519B170DC7FD /* Alamofire */ = { + isa = PBXNativeTarget; + buildConfigurationList = E87124444A44B7DB55208E7FEC21D331 /* Build configuration list for PBXNativeTarget "Alamofire" */; + buildPhases = ( + 23D70D9651451F43E7E8CC1A87B4B7E9 /* Headers */, + A1C8B029F600160149A2404C342F6E50 /* Sources */, + F1626ADC3A7EC435D2722B3173355D36 /* Frameworks */, + 28FF73341543B6F0A7DF3C20CFFEA0AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Alamofire; + productName = Alamofire; + productReference = 1101C4E2CAB959A7BFD21FE75E332005 /* Alamofire.framework */; + productType = "com.apple.product-type.framework"; + }; + 4B0183450F95394DC0C49E326A20B42D /* Result */ = { + isa = PBXNativeTarget; + buildConfigurationList = A6D3B09EB62546694ADEC9AC5F26496A /* Build configuration list for PBXNativeTarget "Result" */; + buildPhases = ( + 803D1C8389DC36A273C1596A2371062C /* Headers */, + 83F0B699A09857193A6E75CBAD21221D /* Sources */, + 6ABC78A038A90F8F48964B9677372CB6 /* Frameworks */, + 3D85A55B40193DED966CBFD7E0EADA17 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Result; + productName = Result; + productReference = 9549855D3684432FAA2479D3F48F8D39 /* Result.framework */; + productType = "com.apple.product-type.framework"; + }; + 692F870FA017FAB61B5C3290C3BC5F54 /* AMPopTip */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6AA4EA7CA97A980F60A253A3DE20B5FF /* Build configuration list for PBXNativeTarget "AMPopTip" */; + buildPhases = ( + 192512EEECEB8B3F2388EF7CD98FE16A /* Headers */, + 66B36FB6F562AD39C0F1F2D736BE0022 /* Sources */, + 89BB5572C584E93D4B27FFE1EE1E6FFF /* Frameworks */, + E6BF1E5F8FECF8F3282B1E52D56E7B04 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AMPopTip; + productName = AMPopTip; + productReference = 62D8E1A78982244825E42655DEF31E2B /* AMPopTip.framework */; + productType = "com.apple.product-type.framework"; + }; + BAAB01D1BA82ABAE2B535579B77ED0CB /* Moya */ = { + isa = PBXNativeTarget; + buildConfigurationList = 79E494A7493F9AB9FE36E8AF4802FA6F /* Build configuration list for PBXNativeTarget "Moya" */; + buildPhases = ( + D7A6588CEA919559E5AE0CF2E072388A /* Headers */, + 87B74512B0978B8E0641A0FB4AAC024E /* Sources */, + 48ACF90A780D9991A643D8E81E6BEAEA /* Frameworks */, + D2F8DBB6DF3B60A84C0D1854859C66CF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 092F8CA03782F6E3D7258BA6B8433776 /* PBXTargetDependency */, + E88EF6EBC52E2364458D88C21074D8D1 /* PBXTargetDependency */, + ); + name = Moya; + productName = Moya; + productReference = F7EF57EFB1DD70A9C3098E6C3F95C151 /* Moya.framework */; + productType = "com.apple.product-type.framework"; + }; + C0E89653A2AC6FA0A38278FEF9AF42FC /* Pods-Santander-TestUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8B48762D66F669B6A88924046CAF966B /* Build configuration list for PBXNativeTarget "Pods-Santander-TestUITests" */; + buildPhases = ( + 838FDCE6748E1A68E1C0C4C39D6C400A /* Headers */, + C4ADDC2FC642C7EE9718A102043F14EC /* Sources */, + B88C9F2B38E16034967AC3610B938A97 /* Frameworks */, + 8A69BC4AB82D9DB217AD832599112A02 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BF10BB821AEF1A2F5B1486B70CCE5CDD /* PBXTargetDependency */, + ); + name = "Pods-Santander-TestUITests"; + productName = "Pods-Santander-TestUITests"; + productReference = 94EA58095938F9B3C04D77F29F66A5D0 /* Pods_Santander_TestUITests.framework */; + productType = "com.apple.product-type.framework"; + }; + C27DC25A308B276373505823128CD197 /* Pods-Santander-TestTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9294D0D9CC1AFEABCE96CA7EB0C2884E /* Build configuration list for PBXNativeTarget "Pods-Santander-TestTests" */; + buildPhases = ( + 24E0A5A25C6279C4B63841C53779093E /* Headers */, + 05AF40A104E98201AAADCA2B4933EDD2 /* Sources */, + D22CBC0AEFD5C611C593833B6DC007E7 /* Frameworks */, + D5C5D83CFC472CA90759510466920ACB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 42DD3FD355AAB38C566F0BC087083F3F /* PBXTargetDependency */, + ); + name = "Pods-Santander-TestTests"; + productName = "Pods-Santander-TestTests"; + productReference = E91E237C060ECC2B4366CA83D0CC2C5E /* Pods_Santander_TestTests.framework */; + productType = "com.apple.product-type.framework"; + }; + EFF87187B46551C36797F2B899C54212 /* IQKeyboardManagerSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3B91B77FBB13EFD33FB1AE6F88ABA7C5 /* Build configuration list for PBXNativeTarget "IQKeyboardManagerSwift" */; + buildPhases = ( + 5FD2C029FC627D78FBEBD0C3829ACA56 /* Headers */, + 50CEEDE322708ABF7722F6D79CCF256B /* Sources */, + B886D41EF763DE44F455CB7E74E9A560 /* Frameworks */, + DC94709994DB7B9EF675DAE0DB5045E8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IQKeyboardManagerSwift; + productName = IQKeyboardManagerSwift; + productReference = 555CDDB61EE2030B6AA753AE6C16F884 /* IQKeyboardManagerSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + F6743F51E8DAAB9AB7DB29A84A37F017 /* Pods-Santander-Test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12C3D604FC184770EF398B1F57BB184F /* Build configuration list for PBXNativeTarget "Pods-Santander-Test" */; + buildPhases = ( + 1FD6F66D0198C70C4000AE75095B649C /* Headers */, + 1320CF76E5AEEA254318118E4D3D0A97 /* Sources */, + 61ADA0C1B2897AA3D357547B127F1565 /* Frameworks */, + DA2E7F4C34C1B07CA61202720BDC55F8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CD16410320C0E6BD9976CFD1BEF09930 /* PBXTargetDependency */, + CB065F770243560D5927D817F60EE29B /* PBXTargetDependency */, + 66B4BB04C1FD5A82AF4EF6F717DDCF1F /* PBXTargetDependency */, + 1152F04DD7A1FAE617A865882EA55C80 /* PBXTargetDependency */, + 2C89A4D93D9F9B29C5280D1EC65BA39C /* PBXTargetDependency */, + DBE3D9CFF3ED902BB38ABAAFE932583E /* PBXTargetDependency */, + ); + name = "Pods-Santander-Test"; + productName = "Pods-Santander-Test"; + productReference = E1C2681F4C36A1893AAE9A6548862463 /* Pods_Santander_Test.framework */; + productType = "com.apple.product-type.framework"; + }; + F6CF1E66B5C83639563339131FB4A512 /* StatusAlert */ = { + isa = PBXNativeTarget; + buildConfigurationList = CFFA59F9F0021FF7452D3FB1A10BCDEB /* Build configuration list for PBXNativeTarget "StatusAlert" */; + buildPhases = ( + 9996E8B8F3A60AC7419C8E1EC55DE0E7 /* Headers */, + 12F4EED5F0E6C0B6EDB218B757511EF6 /* Sources */, + 2FAF1D107A0B9FB6A19D58960CEC7344 /* Frameworks */, + A8CBCC26D088D583FF55601F14F69CE7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = StatusAlert; + productName = StatusAlert; + productReference = F001F52BA7BA1ED9D50942621A44C511 /* StatusAlert.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0930; + LastUpgradeCheck = 0930; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = BD45C81BD98CE392E5786E7DEECC93A4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3383968E74B5371B20BB519B170DC7FD /* Alamofire */, + 692F870FA017FAB61B5C3290C3BC5F54 /* AMPopTip */, + EFF87187B46551C36797F2B899C54212 /* IQKeyboardManagerSwift */, + BAAB01D1BA82ABAE2B535579B77ED0CB /* Moya */, + F6743F51E8DAAB9AB7DB29A84A37F017 /* Pods-Santander-Test */, + C27DC25A308B276373505823128CD197 /* Pods-Santander-TestTests */, + C0E89653A2AC6FA0A38278FEF9AF42FC /* Pods-Santander-TestUITests */, + 4B0183450F95394DC0C49E326A20B42D /* Result */, + F6CF1E66B5C83639563339131FB4A512 /* StatusAlert */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 28FF73341543B6F0A7DF3C20CFFEA0AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3D85A55B40193DED966CBFD7E0EADA17 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8A69BC4AB82D9DB217AD832599112A02 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A8CBCC26D088D583FF55601F14F69CE7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F8DBB6DF3B60A84C0D1854859C66CF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D5C5D83CFC472CA90759510466920ACB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DA2E7F4C34C1B07CA61202720BDC55F8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC94709994DB7B9EF675DAE0DB5045E8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B178B38FCD7F99B07422484FC7FD6861 /* IQKeyboardManager.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E6BF1E5F8FECF8F3282B1E52D56E7B04 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 05AF40A104E98201AAADCA2B4933EDD2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B277AE2A50A7C5F6B1BB9EDA5E1A97B8 /* Pods-Santander-TestTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12F4EED5F0E6C0B6EDB218B757511EF6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B71861435CE67838D5C615C7BF529F8D /* StatusAlert-dummy.m in Sources */, + 4D3F118DF9909DFF62FD2453B5EED984 /* StatusAlert.swift in Sources */, + 077FC20E6644D0CC24318906467DB4EA /* StatusAlertUtils.swift in Sources */, + C089BCD74D2A26B10186867D672DA506 /* UIDevice+Blur.swift in Sources */, + 5A18281634C290F12C5ABF249DEE9564 /* UIDevice+Platform.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1320CF76E5AEEA254318118E4D3D0A97 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8C7CD095FC0252C2920A4204819AF718 /* Pods-Santander-Test-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50CEEDE322708ABF7722F6D79CCF256B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B2A4A7D1BD7C4D59EA0FF0A49163A387 /* IQBarButtonItem.swift in Sources */, + B888C49BC46C9A62CBF03A8FAD7126F1 /* IQInvocation.swift in Sources */, + 633A43974DB81C2CCB1A2C3A7B1BFB37 /* IQKeyboardManager.swift in Sources */, + DD420F096096C518F8E6B44583BF6521 /* IQKeyboardManagerConstants.swift in Sources */, + D8F4B5841E455F5170B1C2FC30863B05 /* IQKeyboardManagerConstantsInternal.swift in Sources */, + 92E9BD908B71CFA1C6EDA2FA86BF1635 /* IQKeyboardManagerSwift-dummy.m in Sources */, + BA61F495C0E45AA2A88D21D51575FF02 /* IQKeyboardReturnKeyHandler.swift in Sources */, + 986EF56798DE2C7C65737AD9334E517D /* IQNSArray+Sort.swift in Sources */, + F0A9F7366B446EDAE4716768BCCADE6C /* IQPreviousNextView.swift in Sources */, + 36DD0B1007BF3C7BFE352704D5C415F7 /* IQTextView.swift in Sources */, + D3A177F9BCAD7042B46E0A36CEDDBF8E /* IQTitleBarButtonItem.swift in Sources */, + D7D49BAE6EAAD5EC2DAF370BCDE783A2 /* IQToolbar.swift in Sources */, + DDAC963022C8D96EBAAD2284780E0276 /* IQUIScrollView+Additions.swift in Sources */, + 4467D1C8AABFA6FC321AC7CD6F327B48 /* IQUITextFieldView+Additions.swift in Sources */, + 4A3294F2FCFF96D15DA6FD2D5D839788 /* IQUIView+Hierarchy.swift in Sources */, + 02FB225950EEA73C12B14A3D74A2096E /* IQUIView+IQKeyboardToolbar.swift in Sources */, + F852C0100AB6D9D2CE3E28FCA487C67E /* IQUIViewController+Additions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 66B36FB6F562AD39C0F1F2D736BE0022 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 32083CD40AE0F52B9C5EE6520F51D866 /* AMPopTip-dummy.m in Sources */, + 2F0E58380E710D15560B27A683FB6FE3 /* PopTip+Draw.swift in Sources */, + 09F37003965A0689DCDB98EEBF35988A /* PopTip+Transitions.swift in Sources */, + 6D5139BE6172B47602185037BB82DF47 /* PopTip.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 83F0B699A09857193A6E75CBAD21221D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6E64C6F2AF1EBF3B28AD5CC90D8D3E2F /* AnyError.swift in Sources */, + 4A42935426AB30760B47ADDE1BB1C39D /* NoError.swift in Sources */, + 5D53EA65636A0B296726611E3C491CF7 /* Result-dummy.m in Sources */, + 80111F83B0BCA39D24985D0F122F0001 /* Result.swift in Sources */, + 6AAA002A59C5011CB71E9B83E6803C6D /* ResultProtocol.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 87B74512B0978B8E0641A0FB4AAC024E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9EBD0113F23A6FBB0CDC07F66E77ED81 /* AccessTokenPlugin.swift in Sources */, + 68BB9452F3012913BABE12BE69C74785 /* AnyEncodable.swift in Sources */, + F44AA7A6E15EFE1927FC41B447D6EFD1 /* Cancellable.swift in Sources */, + 778F9B551E5A346DA84BC46CBFFA1B22 /* CredentialsPlugin.swift in Sources */, + 1B131595178FA51FC0B7C2FB12ED5ADB /* Endpoint.swift in Sources */, + 313895F247F6C04661152D6D4948C100 /* Image.swift in Sources */, + A266363126A240900FCC5CCFE1EDB8BE /* Moya+Alamofire.swift in Sources */, + D348D88FE7939869DE08BC623976B321 /* Moya-dummy.m in Sources */, + FF38A3D897A7C4228CAA5CCA71401376 /* MoyaError.swift in Sources */, + DE6AC2459484032563903F1BFCCD3756 /* MoyaProvider+Defaults.swift in Sources */, + CD321D15156EF51F506D6183152B0E1C /* MoyaProvider+Internal.swift in Sources */, + EB65CF8FE56C314DBAF088FE713C9F88 /* MoyaProvider.swift in Sources */, + 64C35953B1CEDA2F929331C7626611CC /* MultipartFormData.swift in Sources */, + AB037232146E78285C5B8F6394D1A658 /* MultiTarget.swift in Sources */, + 5D97ACC58BF197F2426B2C6853142DF5 /* NetworkActivityPlugin.swift in Sources */, + 1C496E2C46E94DECD60F0B1C7CA511E4 /* NetworkLoggerPlugin.swift in Sources */, + 6499810827FC895E23B2E30263617389 /* Plugin.swift in Sources */, + D0D3B6F6481841FB0B0DC61653217179 /* Response.swift in Sources */, + B1EB1CD6804A333102774EA1DE9F1B94 /* TargetType.swift in Sources */, + 225E880F1459B17432E7DF840D03A3E7 /* Task.swift in Sources */, + 31895E3CF848D69123E3A6DDEDBBF86F /* URL+Moya.swift in Sources */, + 88AE9E6392539A558E0F4417775EEA91 /* URLRequest+Encoding.swift in Sources */, + B642F75A16A71ED991DB8A9DA65AD439 /* ValidationType.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1C8B029F600160149A2404C342F6E50 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E3538A6992A38276764936A9733493D /* AFError.swift in Sources */, + 772911DA6E33D1CBBC30131B7C8BDBB3 /* Alamofire-dummy.m in Sources */, + 0A39AF55285A3A4F7CBABB6D822FA4A3 /* Alamofire.swift in Sources */, + 3E0749AF6C51BCF0E4A41CF1D6A76FED /* DispatchQueue+Alamofire.swift in Sources */, + AEA829AB1A8AF2AD077A808AED6B178A /* MultipartFormData.swift in Sources */, + 73AB05789A4982944AF68DBD013E3EB7 /* NetworkReachabilityManager.swift in Sources */, + 1D29D2ACADF961F69D32B06FA6A09E28 /* Notifications.swift in Sources */, + 700D3D95AF9520CB227846DFD943A2DA /* ParameterEncoding.swift in Sources */, + 355C18EEC82624A06A6CC93965258E33 /* Request.swift in Sources */, + 059D92B7BBFBEC53E9A3B6E11C5C3B3A /* Response.swift in Sources */, + 6EFD003458AE7F689DEA720A2030C261 /* ResponseSerialization.swift in Sources */, + EB0DD8CEA1A69867A30267439C970440 /* Result.swift in Sources */, + 8D2A6A90A6DDAF75EA52D471258545CC /* ServerTrustPolicy.swift in Sources */, + A1EC41966B261DCE460BCDE5124A1DBE /* SessionDelegate.swift in Sources */, + D3FA0AA634AAEA99AB3FABC36BB4958C /* SessionManager.swift in Sources */, + 77E8F0EB9FFBE2E3EB0C77095C644606 /* TaskDelegate.swift in Sources */, + 60B6C2A003864AAD3A426448152F67BE /* Timeline.swift in Sources */, + AA56769D8733D3F3E7976742D5ABA998 /* Validation.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C4ADDC2FC642C7EE9718A102043F14EC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E15EA05765812FBDA4F4428F7B6BBF6D /* Pods-Santander-TestUITests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 092F8CA03782F6E3D7258BA6B8433776 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Alamofire; + target = 3383968E74B5371B20BB519B170DC7FD /* Alamofire */; + targetProxy = 89B7C1F59BE279B9AC2C2AEA832F837B /* PBXContainerItemProxy */; + }; + 1152F04DD7A1FAE617A865882EA55C80 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Moya; + target = BAAB01D1BA82ABAE2B535579B77ED0CB /* Moya */; + targetProxy = A7BE02881176372328240EA60252BB3A /* PBXContainerItemProxy */; + }; + 2C89A4D93D9F9B29C5280D1EC65BA39C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Result; + target = 4B0183450F95394DC0C49E326A20B42D /* Result */; + targetProxy = 7D231DD4F66D54D4FD7A2F8FEBEF1FCC /* PBXContainerItemProxy */; + }; + 42DD3FD355AAB38C566F0BC087083F3F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-Santander-Test"; + target = F6743F51E8DAAB9AB7DB29A84A37F017 /* Pods-Santander-Test */; + targetProxy = 969DCCA076A9B9D70065EA7CB5DB6A86 /* PBXContainerItemProxy */; + }; + 66B4BB04C1FD5A82AF4EF6F717DDCF1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = IQKeyboardManagerSwift; + target = EFF87187B46551C36797F2B899C54212 /* IQKeyboardManagerSwift */; + targetProxy = 49600E263670D867E35C1B1E918A2EEF /* PBXContainerItemProxy */; + }; + BF10BB821AEF1A2F5B1486B70CCE5CDD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-Santander-Test"; + target = F6743F51E8DAAB9AB7DB29A84A37F017 /* Pods-Santander-Test */; + targetProxy = BE2FE96C842D970D743B817C3C29C800 /* PBXContainerItemProxy */; + }; + CB065F770243560D5927D817F60EE29B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Alamofire; + target = 3383968E74B5371B20BB519B170DC7FD /* Alamofire */; + targetProxy = C2484FBCD16255B9BC841E5263AE7033 /* PBXContainerItemProxy */; + }; + CD16410320C0E6BD9976CFD1BEF09930 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = AMPopTip; + target = 692F870FA017FAB61B5C3290C3BC5F54 /* AMPopTip */; + targetProxy = 394DD8541DDFF77A28FBB238EEF723A3 /* PBXContainerItemProxy */; + }; + DBE3D9CFF3ED902BB38ABAAFE932583E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = StatusAlert; + target = F6CF1E66B5C83639563339131FB4A512 /* StatusAlert */; + targetProxy = CB56E2428B8E2A6752A736019B630B48 /* PBXContainerItemProxy */; + }; + E88EF6EBC52E2364458D88C21074D8D1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Result; + target = 4B0183450F95394DC0C49E326A20B42D /* Result */; + targetProxy = 6A4EDBBF08D44260DF0148CD85CDDA29 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0FB9D4D9E1E618B6D43E20521441EB25 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6B0D3C25CAABB853B0E1562B0AB04773 /* Alamofire.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + 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 = 8.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 = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 1384336784C0D8286E29FAA87EC30426 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1112BA5594638444A8463D9E6BD25F8C /* Pods-Santander-Test.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "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-Santander-Test/Pods-Santander-Test-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Santander-Test/Pods-Santander-Test.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; + }; + 16419DA1024545FD12B5A373477D6581 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6B0D3C25CAABB853B0E1562B0AB04773 /* Alamofire.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + 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 = 8.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 = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 187432D34D469D89BE3A3BD89ACC0937 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3A7155821E550BB48423DD1D5A915B48 /* StatusAlert.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "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/StatusAlert/StatusAlert-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/StatusAlert/StatusAlert-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/StatusAlert/StatusAlert.modulemap"; + PRODUCT_MODULE_NAME = StatusAlert; + PRODUCT_NAME = StatusAlert; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 382A284A18BD923F7460324F5D3D0A8A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F3708A4B829835EBC49B784D84968130 /* Pods-Santander-TestTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "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-Santander-TestTests/Pods-Santander-TestTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.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; + }; + 3E485B577D646088BB68D29D2662F8BF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8754AA9A73992CC94C22AC805D068C94 /* IQKeyboardManagerSwift.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap"; + PRODUCT_MODULE_NAME = IQKeyboardManagerSwift; + PRODUCT_NAME = IQKeyboardManagerSwift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 3E7E7BED862F6CC840946052B78589E4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 72E40C3B6E66E684514A3B0403D07BFE /* Pods-Santander-TestUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "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-Santander-TestUITests/Pods-Santander-TestUITests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.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; + }; + 53E2997CF3EED120C980FBF4ED99677E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 500A039A12C1D4FC4DD1BA43A9523E91 /* Pods-Santander-Test.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "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-Santander-Test/Pods-Santander-Test-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Santander-Test/Pods-Santander-Test.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; + }; + 6C1A5B8955D03EDCFC5F5853168E6D3B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D1CB1588C635AF0E142CA79C1D791345 /* Result.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Result/Result-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Result/Result-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Result/Result.modulemap"; + PRODUCT_MODULE_NAME = Result; + PRODUCT_NAME = Result; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 74D317F1EF69F2113B155C077B84BBEE /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7A8D526CFB9CE5AE19261B6D3A1AA350 /* Pods-Santander-TestUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "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-Santander-TestUITests/Pods-Santander-TestUITests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.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; + }; + 7FA1AE79A76BE2186F4AC4D3410315BB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6009D670E92930F3D826B7B232BAE2DE /* Pods-Santander-TestTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "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-Santander-TestTests/Pods-Santander-TestTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.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; + }; + 83573944FBFCC88348188CE22B707DAC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4A815150FE659B719E0E5ECD4EC715BA /* Moya.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Moya/Moya-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Moya/Moya-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Moya/Moya.modulemap"; + PRODUCT_MODULE_NAME = Moya; + PRODUCT_NAME = Moya; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 89A971816ED9B83A9681DFF8C99153B1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3A7155821E550BB48423DD1D5A915B48 /* StatusAlert.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "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/StatusAlert/StatusAlert-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/StatusAlert/StatusAlert-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/StatusAlert/StatusAlert.modulemap"; + PRODUCT_MODULE_NAME = StatusAlert; + PRODUCT_NAME = StatusAlert; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 9E359B9255E9B069A4219B39F99A2B51 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A9370C1B5A38F7E656EE5FAB9855D66 /* AMPopTip.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "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/AMPopTip/AMPopTip-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/AMPopTip/AMPopTip-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/AMPopTip/AMPopTip.modulemap"; + PRODUCT_MODULE_NAME = AMPopTip; + PRODUCT_NAME = AMPopTip; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A105C1752A0A49B30ED621B71E226CDB /* 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_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 = 12.1; + 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 = 4.2; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + B4F7E217DC8509700B9E6CBA20DA233D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8754AA9A73992CC94C22AC805D068C94 /* IQKeyboardManagerSwift.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap"; + PRODUCT_MODULE_NAME = IQKeyboardManagerSwift; + PRODUCT_NAME = IQKeyboardManagerSwift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + C1391485F05B41BD35738A433056B7FA /* 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_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 = 12.1; + 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 = 4.2; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + D383535440703B0108BB82C0AD47665A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D1CB1588C635AF0E142CA79C1D791345 /* Result.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Result/Result-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Result/Result-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Result/Result.modulemap"; + PRODUCT_MODULE_NAME = Result; + PRODUCT_NAME = Result; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DBC53AACE64A36F9A22795C6E178A55E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4A815150FE659B719E0E5ECD4EC715BA /* Moya.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Moya/Moya-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Moya/Moya-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Moya/Moya.modulemap"; + PRODUCT_MODULE_NAME = Moya; + PRODUCT_NAME = Moya; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DD2C9642B0438059A14EDD42E35996FA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A9370C1B5A38F7E656EE5FAB9855D66 /* AMPopTip.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "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/AMPopTip/AMPopTip-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/AMPopTip/AMPopTip-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/AMPopTip/AMPopTip.modulemap"; + PRODUCT_MODULE_NAME = AMPopTip; + PRODUCT_NAME = AMPopTip; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 12C3D604FC184770EF398B1F57BB184F /* Build configuration list for PBXNativeTarget "Pods-Santander-Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1384336784C0D8286E29FAA87EC30426 /* Debug */, + 53E2997CF3EED120C980FBF4ED99677E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3B91B77FBB13EFD33FB1AE6F88ABA7C5 /* Build configuration list for PBXNativeTarget "IQKeyboardManagerSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3E485B577D646088BB68D29D2662F8BF /* Debug */, + B4F7E217DC8509700B9E6CBA20DA233D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C1391485F05B41BD35738A433056B7FA /* Debug */, + A105C1752A0A49B30ED621B71E226CDB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6AA4EA7CA97A980F60A253A3DE20B5FF /* Build configuration list for PBXNativeTarget "AMPopTip" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DD2C9642B0438059A14EDD42E35996FA /* Debug */, + 9E359B9255E9B069A4219B39F99A2B51 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 79E494A7493F9AB9FE36E8AF4802FA6F /* Build configuration list for PBXNativeTarget "Moya" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83573944FBFCC88348188CE22B707DAC /* Debug */, + DBC53AACE64A36F9A22795C6E178A55E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8B48762D66F669B6A88924046CAF966B /* Build configuration list for PBXNativeTarget "Pods-Santander-TestUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3E7E7BED862F6CC840946052B78589E4 /* Debug */, + 74D317F1EF69F2113B155C077B84BBEE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9294D0D9CC1AFEABCE96CA7EB0C2884E /* Build configuration list for PBXNativeTarget "Pods-Santander-TestTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7FA1AE79A76BE2186F4AC4D3410315BB /* Debug */, + 382A284A18BD923F7460324F5D3D0A8A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A6D3B09EB62546694ADEC9AC5F26496A /* Build configuration list for PBXNativeTarget "Result" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6C1A5B8955D03EDCFC5F5853168E6D3B /* Debug */, + D383535440703B0108BB82C0AD47665A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CFFA59F9F0021FF7452D3FB1A10BCDEB /* Build configuration list for PBXNativeTarget "StatusAlert" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 187432D34D469D89BE3A3BD89ACC0937 /* Debug */, + 89A971816ED9B83A9681DFF8C99153B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E87124444A44B7DB55208E7FEC21D331 /* Build configuration list for PBXNativeTarget "Alamofire" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 16419DA1024545FD12B5A373477D6581 /* Debug */, + 0FB9D4D9E1E618B6D43E20521441EB25 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/Santander-Test/Pods/Pods.xcodeproj/xcuserdata/matheusribeiro.xcuserdatad/xcschemes/xcschememanagement.plist b/Santander-Test/Pods/Pods.xcodeproj/xcuserdata/matheusribeiro.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..06118473 --- /dev/null +++ b/Santander-Test/Pods/Pods.xcodeproj/xcuserdata/matheusribeiro.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,56 @@ + + + + + SchemeUserState + + SuppressBuildableAutocreation + + 3383968E74B5371B20BB519B170DC7FD + + primary + + + 4B0183450F95394DC0C49E326A20B42D + + primary + + + 692F870FA017FAB61B5C3290C3BC5F54 + + primary + + + BAAB01D1BA82ABAE2B535579B77ED0CB + + primary + + + C0E89653A2AC6FA0A38278FEF9AF42FC + + primary + + + C27DC25A308B276373505823128CD197 + + primary + + + EFF87187B46551C36797F2B899C54212 + + primary + + + F6743F51E8DAAB9AB7DB29A84A37F017 + + primary + + + F6CF1E66B5C83639563339131FB4A512 + + primary + + + + + diff --git a/Santander-Test/Pods/Result/LICENSE b/Santander-Test/Pods/Result/LICENSE new file mode 100644 index 00000000..3026ee19 --- /dev/null +++ b/Santander-Test/Pods/Result/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Rob Rix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Santander-Test/Pods/Result/README.md b/Santander-Test/Pods/Result/README.md new file mode 100644 index 00000000..c030316c --- /dev/null +++ b/Santander-Test/Pods/Result/README.md @@ -0,0 +1,112 @@ +# Result + +[![Build Status](https://travis-ci.org/antitypical/Result.svg?branch=master)](https://travis-ci.org/antitypical/Result) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![CocoaPods](https://img.shields.io/cocoapods/v/Result.svg)](https://cocoapods.org/) +[![Reference Status](https://www.versioneye.com/objective-c/result/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/result/references) + +This is a Swift µframework providing `Result`. + +`Result` values are either successful (wrapping `Value`) or failed (wrapping `Error`). This is similar to Swift’s native `Optional` type: `success` is like `some`, and `failure` is like `none` except with an associated `Error` value. The addition of an associated `Error` allows errors to be passed along for logging or displaying to the user. + +Using this µframework instead of rolling your own `Result` type allows you to easily interface with other frameworks that also use `Result`. + +## Use + +Use `Result` whenever an operation has the possibility of failure. Consider the following example of a function that tries to extract a `String` for a given key from a JSON `Dictionary`. + +```swift +typealias JSONObject = [String: Any] + +enum JSONError: Error { + case noSuchKey(String) + case typeMismatch +} + +func stringForKey(json: JSONObject, key: String) -> Result { + guard let value = json[key] else { + return .failure(.noSuchKey(key)) + } + + guard let value = value as? String else { + return .failure(.typeMismatch) + } + + return .success(value) +} +``` + +This function provides a more robust wrapper around the default subscripting provided by `Dictionary`. Rather than return `Any?`, it returns a `Result` that either contains the `String` value for the given key, or an `ErrorType` detailing what went wrong. + +One simple way to handle a `Result` is to deconstruct it using a `switch` statement. + +```swift +switch stringForKey(json, key: "email") { + +case let .success(email): + print("The email is \(email)") + +case let .failure(.noSuchKey(key)): + print("\(key) is not a valid key") + +case .failure(.typeMismatch): + print("Didn't have the right type") +} +``` + +Using a `switch` statement allows powerful pattern matching, and ensures all possible results are covered. Swift 2.0 offers new ways to deconstruct enums like the `if-case` statement, but be wary as such methods do not ensure errors are handled. + +Other methods available for processing `Result` are detailed in the [API documentation](http://cocoadocs.org/docsets/Result/). + +## Result vs. Throws + +Swift 2.0 introduces error handling via throwing and catching `Error`. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction enables powerful functionality such as `map` and `flatMap`, making `Result` more composable than `throw`. + +Since dealing with APIs that throw is common, you can convert such functions into a `Result` by using the `materialize` method. Conversely, a `Result` can be used to throw an error by calling `dematerialize`. + +## Higher Order Functions + +`map` and `flatMap` operate the same as `Optional.map` and `Optional.flatMap` except they apply to `Result`. + +`map` transforms a `Result` into a `Result` of a new type. It does this by taking a function that transforms the `Value` type into a new value. This transformation is only applied in the case of a `success`. In the case of a `failure`, the associated error is re-wrapped in the new `Result`. + +```swift +// transforms a Result to a Result +let idResult = intForKey(json, key:"id").map { id in String(id) } +``` + +Here, the final result is either the id as a `String`, or carries over the `failure` from the previous result. + +`flatMap` is similar to `map` in that it transforms the `Result` into another `Result`. However, the function passed into `flatMap` must return a `Result`. + +An in depth discussion of `map` and `flatMap` is beyond the scope of this documentation. If you would like a deeper understanding, read about functors and monads. This article is a good place to [start](http://www.javiersoto.me/post/106875422394). + +## Integration + +### Carthage + +1. Add this repository as a submodule and/or [add it to your Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile) if you’re using [carthage](https://github.com/Carthage/Carthage/) to manage your dependencies. +2. Drag `Result.xcodeproj` into your project or workspace. +3. Link your target against `Result.framework`. +4. Application targets should ensure that the framework gets copied into their application bundle. (Framework targets should instead require the application linking them to include Result.) + +### Cocoapods + +```ruby +pod 'Result', '~> 4.0.0' +``` + +### Swift Package Manager + +```swift +import PackageDescription + +let package = Package( + name: "MyProject", + targets: [], + dependencies: [ + .Package(url: "https://github.com/antitypical/Result.git", + majorVersion: 4) + ] +) +``` diff --git a/Santander-Test/Pods/Result/Result/AnyError.swift b/Santander-Test/Pods/Result/Result/AnyError.swift new file mode 100644 index 00000000..ee018d27 --- /dev/null +++ b/Santander-Test/Pods/Result/Result/AnyError.swift @@ -0,0 +1,46 @@ +import Foundation + +/// A type-erased error which wraps an arbitrary error instance. This should be +/// useful for generic contexts. +public struct AnyError: Swift.Error { + /// The underlying error. + public let error: Swift.Error + + public init(_ error: Swift.Error) { + if let anyError = error as? AnyError { + self = anyError + } else { + self.error = error + } + } +} + +extension AnyError: ErrorConvertible { + public static func error(from error: Error) -> AnyError { + return AnyError(error) + } +} + +extension AnyError: CustomStringConvertible { + public var description: String { + return String(describing: error) + } +} + +extension AnyError: LocalizedError { + public var errorDescription: String? { + return error.localizedDescription + } + + public var failureReason: String? { + return (error as? LocalizedError)?.failureReason + } + + public var helpAnchor: String? { + return (error as? LocalizedError)?.helpAnchor + } + + public var recoverySuggestion: String? { + return (error as? LocalizedError)?.recoverySuggestion + } +} diff --git a/Santander-Test/Pods/Result/Result/NoError.swift b/Santander-Test/Pods/Result/Result/NoError.swift new file mode 100644 index 00000000..a919f510 --- /dev/null +++ b/Santander-Test/Pods/Result/Result/NoError.swift @@ -0,0 +1,10 @@ +/// An “error” that is impossible to construct. +/// +/// This can be used to describe `Result`s where failures will never +/// be generated. For example, `Result` describes a result that +/// contains an `Int`eger and is guaranteed never to be a `failure`. +public enum NoError: Swift.Error, Equatable { + public static func ==(lhs: NoError, rhs: NoError) -> Bool { + return true + } +} diff --git a/Santander-Test/Pods/Result/Result/Result.swift b/Santander-Test/Pods/Result/Result/Result.swift new file mode 100644 index 00000000..75768380 --- /dev/null +++ b/Santander-Test/Pods/Result/Result/Result.swift @@ -0,0 +1,229 @@ +// Copyright (c) 2015 Rob Rix. All rights reserved. + +/// An enum representing either a failure with an explanatory error, or a success with a result value. +public enum Result: ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible { + case success(Value) + case failure(Error) + + /// The compatibility alias for the Swift 5's `Result` in the standard library. + /// + /// See https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md + /// and https://forums.swift.org/t/accepted-with-modifications-se-0235-add-result-to-the-standard-library/18603 + /// for the details. + public typealias Success = Value + /// The compatibility alias for the Swift 5's `Result` in the standard library. + /// + /// See https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md + /// and https://forums.swift.org/t/accepted-with-modifications-se-0235-add-result-to-the-standard-library/18603 + /// for the details. + public typealias Failure = Error + + // MARK: Constructors + + /// Constructs a success wrapping a `value`. + public init(value: Value) { + self = .success(value) + } + + /// Constructs a failure wrapping an `error`. + public init(error: Error) { + self = .failure(error) + } + + /// Constructs a result from an `Optional`, failing with `Error` if `nil`. + public init(_ value: Value?, failWith: @autoclosure () -> Error) { + self = value.map(Result.success) ?? .failure(failWith()) + } + + /// Constructs a result from a function that uses `throw`, failing with `Error` if throws. + public init(_ f: @autoclosure () throws -> Value) { + self.init(catching: f) + } + + /// Constructs a result from a function that uses `throw`, failing with `Error` if throws. + @available(*, deprecated, renamed: "init(catching:)") + public init(attempt f: () throws -> Value) { + self.init(catching: f) + } + + /// The same as `init(attempt:)` aiming for the compatibility with the Swift 5's `Result` in the standard library. + /// + /// See https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md + /// and https://forums.swift.org/t/accepted-with-modifications-se-0235-add-result-to-the-standard-library/18603 + /// for the details. + public init(catching body: () throws -> Success) { + do { + self = .success(try body()) + } catch var error { + if Error.self == AnyError.self { + error = AnyError(error) + } + self = .failure(error as! Error) + } + } + + // MARK: Deconstruction + + /// Returns the value from `success` Results or `throw`s the error. + @available(*, deprecated, renamed: "get()") + public func dematerialize() throws -> Value { + return try get() + } + + /// The same as `dematerialize()` aiming for the compatibility with the Swift 5's `Result` in the standard library. + /// + /// See https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md + /// and https://forums.swift.org/t/accepted-with-modifications-se-0235-add-result-to-the-standard-library/18603 + /// for the details. + public func get() throws -> Success { + switch self { + case let .success(value): + return value + case let .failure(error): + throw error + } + } + + /// Case analysis for Result. + /// + /// Returns the value produced by applying `ifFailure` to `failure` Results, or `ifSuccess` to `success` Results. + public func analysis(ifSuccess: (Value) -> Result, ifFailure: (Error) -> Result) -> Result { + switch self { + case let .success(value): + return ifSuccess(value) + case let .failure(value): + return ifFailure(value) + } + } + + // MARK: Errors + + /// The domain for errors constructed by Result. + public static var errorDomain: String { return "com.antitypical.Result" } + + /// The userInfo key for source functions in errors constructed by Result. + public static var functionKey: String { return "\(errorDomain).function" } + + /// The userInfo key for source file paths in errors constructed by Result. + public static var fileKey: String { return "\(errorDomain).file" } + + /// The userInfo key for source file line numbers in errors constructed by Result. + public static var lineKey: String { return "\(errorDomain).line" } + + /// Constructs an error. + public static func error(_ message: String? = nil, function: String = #function, file: String = #file, line: Int = #line) -> NSError { + var userInfo: [String: Any] = [ + functionKey: function, + fileKey: file, + lineKey: line, + ] + + if let message = message { + userInfo[NSLocalizedDescriptionKey] = message + } + + return NSError(domain: errorDomain, code: 0, userInfo: userInfo) + } + + + // MARK: CustomStringConvertible + + public var description: String { + switch self { + case let .success(value): return ".success(\(value))" + case let .failure(error): return ".failure(\(error))" + } + } + + + // MARK: CustomDebugStringConvertible + + public var debugDescription: String { + return description + } + + // MARK: ResultProtocol + public var result: Result { + return self + } +} + +extension Result where Result.Failure == AnyError { + /// Constructs a result from an expression that uses `throw`, failing with `AnyError` if throws. + public init(_ f: @autoclosure () throws -> Value) { + self.init(attempt: f) + } + + /// Constructs a result from a closure that uses `throw`, failing with `AnyError` if throws. + public init(attempt f: () throws -> Value) { + do { + self = .success(try f()) + } catch { + self = .failure(AnyError(error)) + } + } +} + +// MARK: - Derive result from failable closure + +@available(*, deprecated, renamed: "Result.init(attempt:)") +public func materialize(_ f: () throws -> T) -> Result { + return Result(attempt: f) +} + +@available(*, deprecated, renamed: "Result.init(_:)") +public func materialize(_ f: @autoclosure () throws -> T) -> Result { + return Result(try f()) +} + +// MARK: - ErrorConvertible conformance + +extension NSError: ErrorConvertible { + public static func error(from error: Swift.Error) -> Self { + func cast(_ error: Swift.Error) -> T { + return error as! T + } + + return cast(error) + } +} + +// MARK: - migration support + +@available(*, unavailable, message: "Use the overload which returns `Result` instead") +public func materialize(_ f: () throws -> T) -> Result { + fatalError() +} + +@available(*, unavailable, message: "Use the overload which returns `Result` instead") +public func materialize(_ f: @autoclosure () throws -> T) -> Result { + fatalError() +} + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + +/// Constructs a `Result` with the result of calling `try` with an error pointer. +/// +/// This is convenient for wrapping Cocoa API which returns an object or `nil` + an error, by reference. e.g.: +/// +/// Result.try { NSData(contentsOfURL: URL, options: .dataReadingMapped, error: $0) } +@available(*, unavailable, message: "This has been removed. Use `Result.init(attempt:)` instead. See https://github.com/antitypical/Result/issues/85 for the details.") +public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> T?) -> Result { + fatalError() +} + +/// Constructs a `Result` with the result of calling `try` with an error pointer. +/// +/// This is convenient for wrapping Cocoa API which returns a `Bool` + an error, by reference. e.g.: +/// +/// Result.try { NSFileManager.defaultManager().removeItemAtURL(URL, error: $0) } +@available(*, unavailable, message: "This has been removed. Use `Result.init(attempt:)` instead. See https://github.com/antitypical/Result/issues/85 for the details.") +public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> Bool) -> Result<(), NSError> { + fatalError() +} + +#endif + +// MARK: - + +import Foundation diff --git a/Santander-Test/Pods/Result/Result/ResultProtocol.swift b/Santander-Test/Pods/Result/Result/ResultProtocol.swift new file mode 100644 index 00000000..e3ad0221 --- /dev/null +++ b/Santander-Test/Pods/Result/Result/ResultProtocol.swift @@ -0,0 +1,152 @@ +// Copyright (c) 2015 Rob Rix. All rights reserved. + +/// A protocol that can be used to constrain associated types as `Result`. +public protocol ResultProtocol { + associatedtype Value + associatedtype Error: Swift.Error + + init(value: Value) + init(error: Error) + + var result: Result { get } +} + +extension Result { + /// Returns the value if self represents a success, `nil` otherwise. + public var value: Value? { + switch self { + case let .success(value): return value + case .failure: return nil + } + } + + /// Returns the error if self represents a failure, `nil` otherwise. + public var error: Error? { + switch self { + case .success: return nil + case let .failure(error): return error + } + } + + /// Returns a new Result by mapping `Success`es’ values using `transform`, or re-wrapping `Failure`s’ errors. + public func map(_ transform: (Value) -> U) -> Result { + return flatMap { .success(transform($0)) } + } + + /// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors. + public func flatMap(_ transform: (Value) -> Result) -> Result { + switch self { + case let .success(value): return transform(value) + case let .failure(error): return .failure(error) + } + } + + /// Returns a Result with a tuple of the receiver and `other` values if both + /// are `Success`es, or re-wrapping the error of the earlier `Failure`. + public func fanout(_ other: @autoclosure () -> Result) -> Result<(Value, U), Error> { + return self.flatMap { left in other().map { right in (left, right) } } + } + + /// Returns a new Result by mapping `Failure`'s values using `transform`, or re-wrapping `Success`es’ values. + public func mapError(_ transform: (Error) -> Error2) -> Result { + return flatMapError { .failure(transform($0)) } + } + + /// Returns the result of applying `transform` to `Failure`’s errors, or re-wrapping `Success`es’ values. + public func flatMapError(_ transform: (Error) -> Result) -> Result { + switch self { + case let .success(value): return .success(value) + case let .failure(error): return transform(error) + } + } + + /// Returns a new Result by mapping `Success`es’ values using `success`, and by mapping `Failure`'s values using `failure`. + public func bimap(success: (Value) -> U, failure: (Error) -> Error2) -> Result { + switch self { + case let .success(value): return .success(success(value)) + case let .failure(error): return .failure(failure(error)) + } + } +} + +extension Result { + + // MARK: Higher-order functions + + /// Returns `self.value` if this result is a .Success, or the given value otherwise. Equivalent with `??` + public func recover(_ value: @autoclosure () -> Value) -> Value { + return self.value ?? value() + } + + /// Returns this result if it is a .Success, or the given result otherwise. Equivalent with `??` + public func recover(with result: @autoclosure () -> Result) -> Result { + switch self { + case .success: return self + case .failure: return result() + } + } +} + +/// Protocol used to constrain `tryMap` to `Result`s with compatible `Error`s. +public protocol ErrorConvertible: Swift.Error { + static func error(from error: Swift.Error) -> Self +} + +extension Result where Result.Failure: ErrorConvertible { + + /// Returns the result of applying `transform` to `Success`es’ values, or wrapping thrown errors. + public func tryMap(_ transform: (Value) throws -> U) -> Result { + return flatMap { value in + do { + return .success(try transform(value)) + } + catch { + let convertedError = Error.error(from: error) + // Revisit this in a future version of Swift. https://twitter.com/jckarter/status/672931114944696321 + return .failure(convertedError) + } + } + } +} + +// MARK: - Operators + +extension Result where Result.Success: Equatable, Result.Failure: Equatable { + /// Returns `true` if `left` and `right` are both `Success`es and their values are equal, or if `left` and `right` are both `Failure`s and their errors are equal. + public static func ==(left: Result, right: Result) -> Bool { + if let left = left.value, let right = right.value { + return left == right + } else if let left = left.error, let right = right.error { + return left == right + } + return false + } +} + +#if swift(>=4.1) + extension Result: Equatable where Result.Success: Equatable, Result.Failure: Equatable { } +#else + extension Result where Result.Success: Equatable, Result.Failure: Equatable { + /// Returns `true` if `left` and `right` represent different cases, or if they represent the same case but different values. + public static func !=(left: Result, right: Result) -> Bool { + return !(left == right) + } + } +#endif + +extension Result { + /// Returns the value of `left` if it is a `Success`, or `right` otherwise. Short-circuits. + public static func ??(left: Result, right: @autoclosure () -> Value) -> Value { + return left.recover(right()) + } + + /// Returns `left` if it is a `Success`es, or `right` otherwise. Short-circuits. + public static func ??(left: Result, right: @autoclosure () -> Result) -> Result { + return left.recover(with: right()) + } +} + +// MARK: - migration support + +@available(*, unavailable, renamed: "ErrorConvertible") +public protocol ErrorProtocolConvertible: ErrorConvertible {} diff --git a/Santander-Test/Pods/StatusAlert/LICENSE b/Santander-Test/Pods/StatusAlert/LICENSE new file mode 100644 index 00000000..8d75e171 --- /dev/null +++ b/Santander-Test/Pods/StatusAlert/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017-2018 LowKostKustomz + +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/Santander-Test/Pods/StatusAlert/README.md b/Santander-Test/Pods/StatusAlert/README.md new file mode 100644 index 00000000..bda7d230 --- /dev/null +++ b/Santander-Test/Pods/StatusAlert/README.md @@ -0,0 +1,204 @@ +![Author StatusAlert](https://assets.gitlab-static.net/ZEBSTER/FrameworksAssets/raw/master/StatusAlert/StatusAlertHeader.png) + +

+Swift +Objective-C +Wiki +License +Platform +

+

+Dependency managers +

+ +

+CocoaPods +Carthage +SwiftPackageManager +
+

+ + +

+ Features | + Installation | + Usage | + Customization +

+ +StatusAlert is an iOS framework that displays status alerts similar to Apple's system self-hiding alerts. It is well suited for notifying user without interrupting user flow in iOS-like way. + +It looks very similar to the alerts displayed in Podcasts, Apple Music and News apps. +![System StatusAlert](https://raw.githubusercontent.com/LowKostKustomz/StatusAlert/master/Assets/iPhonesWithSystemAlerts.png) + + +## Features + +* System-like look and feel +* Reduce transparency mode support +* VoiceOver support +* Safe Areas support +* Universal (iPhone & iPad) +* Objective-C support + +## Requirements + +* Xcode 9.0 or later +* iOS 9.0 or later +* Swift 3.2 or later + +## Installation + +### CocoaPods + +To install StatusAlert using [CocoaPods](http://cocoapods.org), add the following line to your `Podfile`: + +```ruby +pod 'StatusAlert', '~> 1.1.0' +``` + +### Carthage + +To install StatusAlert using [Carthage](https://github.com/Carthage/Carthage), add the following line to your `Cartfile`: + +```ruby +github "LowKostKustomz/StatusAlert" ~> 1.1.0 +``` + +### Swift Package Manager + +To install StatusAlert using [Swift Package Manager](https://github.com/apple/swift-package-manager) add this to your dependencies in a `Package.swift` file: + +```swift +dependencies: [ + .package(url: "https://github.com/LowKostKustomz/StatusAlert.git", .exact("1.1.0")) +] +``` + +### Manual installation + +You can also add this project: + * as git submodule + * simply download and copy source files to your project + +### Objective-C integration + +StatusAlert is fully compatible with Objective-C. To import it to your project just add the following line: + +```objectiveс +@import StatusAlert; +``` + +## Demo + +Demo application is included in the `StatusAlert` workspace. To run it clone the repo. + +![Demo StatusAlert](https://raw.githubusercontent.com/LowKostKustomz/StatusAlert/master/Assets/iPhonesWithStatusAlert.png) + +## Usage + +```swift +// Importing framework +import StatusAlert + +// Creating StatusAlert instance +let statusAlert = StatusAlert() +statusAlert.image = UIImage(named: "Some image name") +statusAlert.title = "StatusAlert title" +statusAlert.message = "Message to show beyond title" +statusAlert.canBePickedOrDismissed = isUserInteractionAllowed + +// Presenting created instance +statusAlert.showInKeyWindow() +``` +> All the alert components (`image`, `title`, `message`) are optional, but at least one should be present. Otherwise `show()` method will be ignored. +> +> **IMPORTANT** +> > The alert must be presented only from the main thread, otherwise application will crash with an appropriate error. + +## Customization + +> [Wiki](https://github.com/LowKostKustomz/StatusAlert/wiki) with more content and examples available + +### Different configurations + +Present alert with any set of image, title and message + +### Vertical position + +Display alert anywhere you want, either on the top, in the center or at the bottom of the view, and with any offset. + +### Appearance + +You can customize a single alert's appearance via the `StatusAlert`'s `appearance` property or for all alerts at once with `StatusAlert.Appearance`'s `common` property + +```swift +var titleFont: UIFont +var messageFont: UIFont +var tintColor: UIColor +var backgroundColor: UIColor +var blurStyle: UIBlurEffect.Style +``` + +### Dismissal + +Alert will hide itself after 2 seconds timeout. + +You can change alert showing duration by setting `alertShowingDuration` property. You also can set `canBePickedOrDismissed` property to `true`. After that you will be able to dismiss the alert manually by tapping it and delay dismissal by long tapping the alert. + +## Apps Using _StatusAlert_ + +[BitxfyAppStoreLink]: https://bitxfy.com + +

+ + + +
+ +Bitxfy + +

+ +[![BitxfyScreenShot](https://raw.githubusercontent.com/LowKostKustomz/StatusAlert/master/Assets/BitxfyStatusAlert.png)][BitxfyAppstoreLink] + +> Feel free to submit pull request if you are using this framework in your apps. + +## Author + +[FrameworksRepo]: https://github.com/LowKostKustomz/Frameworks + +[![Author ActionsList](https://assets.gitlab-static.net/ZEBSTER/FrameworksAssets/raw/master/StatusAlert/StatusAlertAuthor.png)][FrameworksRepo] + +

+https://twitter.com/LowKostKustomz +https://www.instagram.com/lowkostkustomz/ +https://stackoverflow.com/users/9076809/lowkostkustomz +https://www.linkedin.com/in/yehor-miroshnychenko +mierosh@gmail.com +https://github.com/LowKostKustomz/Frameworks +

+ +## License + +> The MIT License (MIT) +> +> Copyright (c) 2017-2018 LowKostKustomz +> +> 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/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/Extensions/UIDevice+Blur.swift b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/Extensions/UIDevice+Blur.swift new file mode 100644 index 00000000..a770b569 --- /dev/null +++ b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/Extensions/UIDevice+Blur.swift @@ -0,0 +1,38 @@ +import UIKit + +extension UIDevice { + + /// If the device is able to display blur: + /// + /// - if operation system supports blur; + /// - if "Reduce transparency" mode is disabled; + /// - if device supports blur. + var isBlurAvailable: Bool { + guard operationSystemSupportsBlur, + !reduceTransparencyEnabled, + deviceSupportsBlur + else { + return false + } + + return true + } + + private var operationSystemSupportsBlur: Bool { + if #available(iOS 8.0, *) { + return true + } else { + return false + } + } + + private var reduceTransparencyEnabled: Bool { + return UIAccessibility.isReduceTransparencyEnabled + } + + private var deviceSupportsBlur: Bool { + return platform.isNewerThan(UIDevice.DevicePlatform.iPadModel.iPad3) + || platform.isNewerThan(UIDevice.DevicePlatform.iPodTouchModel.iPodTouch4) + || platform.isNewerThan(UIDevice.DevicePlatform.iPhoneModel.iPhone4) + } +} diff --git a/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/Extensions/UIDevice+Platform.swift b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/Extensions/UIDevice+Platform.swift new file mode 100644 index 00000000..a8c80666 --- /dev/null +++ b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/Extensions/UIDevice+Platform.swift @@ -0,0 +1,190 @@ +import UIKit + +protocol ComparableDevice { + func isNewerThan(_ another: Self) -> Bool +} + +extension UIDevice { + enum DevicePlatform { + + case iPhone(model: iPhoneModel) + case iPad(model: iPadModel) + case iPodTouch(model: iPodTouchModel) + + /// Unknown devices + case other + /// Simulators + case simulator + + enum iPhoneModel: Decimal, ComparableDevice { + case iPhone2G = 1 + case iPhone3G = 10 + case iPhone3GS = 100 + case iPhone4 = 1000 + case iPhone4S = 10000 + + case iPhone5 = 100000 + case iPhone5c = 100000.1 + + case iPhone5S = 1000000 + + case iPhone6 = 10000000 + case iPhone6Plus = 10000000.1 + + case iPhone6S = 100000000 + case iPhone6SPlus = 100000000.1 + case iPhoneSE = 100000000.2 + + /// Newer iPhone + case anotherPhone = 1000000000 + + func isNewerThan(_ another: iPhoneModel) -> Bool { + return rawValue > another.rawValue + } + } + + enum iPodTouchModel: Decimal, ComparableDevice { + // iPod Touch + + case iPodTouch = 2 + case iPodTouch2 = 20 + case iPodTouch3 = 200 + case iPodTouch4 = 2000 + case iPodTouch5 = 20000 + case iPodTouch6 = 200000 + + /// Newer iPod + case anotherPod = 1000000001 + + func isNewerThan(_ another: iPodTouchModel) -> Bool { + return rawValue > another.rawValue + } + } + + enum iPadModel: Decimal, ComparableDevice { + // iPad + + case iPad = 30 + + case iPad2 = 300 + case iPadMini = 300.1 + + case iPad3 = 3000 + + /// Newer iPad + case anotherPad = 1000000002 + + func isNewerThan(_ another: iPadModel) -> Bool { + return rawValue > another.rawValue + } + } + + func isNewerThan(_ another: Device) -> Bool { + switch (self, another) { + + case (.iPhone(let model), let anotherPhone as iPhoneModel): + return model.isNewerThan(anotherPhone) + + case (.iPodTouch(let model), let anotherPod as iPodTouchModel): + return model.isNewerThan(anotherPod) + + case (.iPad(let model), let anotherPad as iPadModel): + return model.isNewerThan(anotherPad) + + default: + return false + + } + } + } + + private var platformString: String { + var sysinfo = utsname() + uname(&sysinfo) + let platform = String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters) + return platform + } + + var platform: DevicePlatform { + switch platformString { + // iPhone + case "iPhone1,1": + return .iPhone(model: .iPhone2G) + case "iPhone1,2": + return .iPhone(model: .iPhone3G) + case "iPhone2,1": + return .iPhone(model: .iPhone3GS) + case "iPhone3,1", + "iPhone3,2", + "iPhone3,3": + return .iPhone(model: .iPhone4) + case "iPhone4,1": + return .iPhone(model: .iPhone4S) + case "iPhone5,1", + "iPhone5,2": + return .iPhone(model: .iPhone5) + case "iPhone5,3", + "iPhone5,4": + return .iPhone(model: .iPhone5c) + case "iPhone6,1", + "iPhone6,2": + return .iPhone(model: .iPhone5S) + case "iPhone7,1": + return .iPhone(model: .iPhone6Plus) + case "iPhone7,2": + return .iPhone(model: .iPhone6) + case "iPhone8,1": + return .iPhone(model: .iPhone6S) + case "iPhone8,2": + return .iPhone(model: .iPhone6SPlus) + case "iPhone8,4": + return .iPhone(model: .iPhoneSE) + // iPod + case "iPod1,1": + return .iPodTouch(model: .iPodTouch) + case "iPod2,1": + return .iPodTouch(model: .iPodTouch2) + case "iPod3,1": + return .iPodTouch(model: .iPodTouch3) + case "iPod4,1": + return .iPodTouch(model: .iPodTouch4) + case "iPod5,1": + return .iPodTouch(model: .iPodTouch5) + case "iPod7,1": + return .iPodTouch(model: .iPodTouch6) + // iPad + case "iPad1,1", + "iPad1,2": + return .iPad(model: .iPad) + case "iPad2,1", + "iPad2,2", + "iPad2,3", + "iPad2,4": + return .iPad(model: .iPad2) + case "iPad2,5", + "iPad2,6", + "iPad2,7": + return .iPad(model: .iPadMini) + case "iPad3,1", + "iPad3,2", + "iPad3,3": + return .iPad(model: .iPad3) + // Simulator + case "x86_64", + "i386": + return .simulator + // iAnother + default: + if platformString.contains("iPhone") { + return .iPhone(model: .anotherPhone) + } + if platformString.contains("iPad") { + return .iPad(model: .anotherPad) + } + if platformString.contains("iPod") { + return .iPodTouch(model: .anotherPod) + } + return .other + } + } +} diff --git a/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/StatusAlert.swift b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/StatusAlert.swift new file mode 100644 index 00000000..091ac2fb --- /dev/null +++ b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/StatusAlert.swift @@ -0,0 +1,794 @@ +// +// StatusAlert +// Copyright © 2017-2018 Yegor Miroshnichenko. Licensed under the MIT license. +// + +import UIKit + +@objc public final class StatusAlert: UIView { + + // MARK: - Public fields - + + /// - Note: Do not change to save system look + /// - Note: Changes while showing will have no effect + @objc public var appearance: Appearance = Appearance.copyCommon() + + /// - Note: Do not change to save system look + /// - Note: Changes while showing will have no effect + @objc public var sizesAndDistances: SizesAndDistances = SizesAndDistances.copyCommon() + + /// Announced to VoiceOver when the alert gets presented + @objc public var accessibilityAnnouncement: String? = nil + + /// How long StatusAlert should be on screen. + /// + /// - Note: This time should include fade animation duration (which is `NavigationControllerHideShowBarDuration`) + /// - Note: Changes while showing will have no effect + @objc public var alertShowingDuration: TimeInterval = 2 + + /// Multiple presentation requests behavior + @objc public static var multiplePresentationsBehavior: MultiplePresentationsBehavior = .ignoreIfAlreadyPresenting + + /// @1x should be 90*90 by default + @objc public var image: UIImage? + + @objc public var title: String? + + @objc public var message: String? + + /// Determines whether `StatusAlert` can be picked or dismissed by tap + @objc public var canBePickedOrDismissed: Bool { + get { return self.contentView.isUserInteractionEnabled } + set { self.contentView.isUserInteractionEnabled = newValue } + } + + // MARK: - Private fields - + + /// Used to present only one `StatusAlert` at once if `multiplePresentationsBehavior` is `ignoreIfAlreadyPresenting` + /// or to dismiss currently presented alerts if `multiplePresentationsBehavior` is `dismissCurrentlyPresented` + private static var currentlyPresentedStatusAlerts: [StatusAlert] = [] + + private static var alertToPresent: StatusAlert? = nil + private static var dismissing: Bool { + return alertToPresent != nil + } + + private let defaultFadeAnimationDuration: TimeInterval = TimeInterval(NavigationControllerHideShowBarDuration) + private lazy var blurEffect: UIBlurEffect = { + return UIBlurEffect(style: self.appearance.blurStyle) + }() + + private let contentView: UIVisualEffectView = UIVisualEffectView() + private let contentStackView: UIStackView = UIStackView() + + private var imageView: UIImageView? = nil + private var titleLabel: UILabel? = nil + private var messageLabel: UILabel? = nil + + private var contentStackViewConstraints: [NSLayoutConstraint] = [] + private var reusableObjectsConstraints: [NSLayoutConstraint] = [] + + private var timer: Timer? + + /// Determines whether `StatusAlert` has at least one item to show + private var isContentEmpty: Bool { + return self.image == nil + && self.title == nil + && self.message == nil + } + + /// Determines whether blur is available + private var isBlurAvailable: Bool { + return UIDevice.current.isBlurAvailable + } + + private var pickGesture: UILongPressGestureRecognizer? + + // MARK: - Interaction methods + + @objc private func pick() { + guard self.canBePickedOrDismissed else { + return + } + if self.pickGesture?.state == .cancelled + || self.pickGesture?.state == .ended + || self.pickGesture?.state == .failed { + + self.dismiss(completion: nil) + } + } + + // MARK: - Initialization - + + @objc public override init(frame: CGRect) { + super.init(frame: frame) + + self.commonInit() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.enqueueReusableObjects() + } + + // MARK: - Show methods - + + /// Shows `StatusAlert` in the center of the `keyWindow` + /// - Note: must be called from the main thread only + @objc public func showInKeyWindow() { + + self.show() + } + + /// Shows `StatusAlert` in the center of `presenter` + /// + /// - Parameters: + /// - presenter: view present `StatusAlert` in + /// - Note: must be called from the main thread only + @objc(showInView:) + public func show(in presenter: UIView) { + + self.show(inPresenter: presenter) + } + + /// Shows `StatusAlert` in `keyWindow` + /// + /// - Parameters: + /// - verticalPosition: `StatusAlert` position in `keyWindow` + /// - Note: must be called from the main thread only + @objc(showWithVerticalPosition:) + public func show(withVerticalPosition verticalPosition: VerticalPosition) { + + self.show(with: verticalPosition, offset: 0) + } + + /// Shows `StatusAlert` in the center of `keyWindow` with `offset` + /// + /// - Parameters: + /// - offset: offset from center of `keyWindow` + /// - Note: must be called from the main thread only + @objc(showWithOffset:) + public func show(withOffset offset: CGFloat) { + + self.show(offset: offset) + } + + /// Shows `StatusAlert` in `presenter` + /// + /// - Parameters: + /// - presenter: view present `StatusAlert` in + /// - verticalPosition: `StatusAlert` position in `presenter` + /// - Note: must be called from the main thread only + @objc(showInView:withVerticalPosition:) + public func show( + in presenter: UIView, + withVerticalPosition verticalPosition: VerticalPosition + ) { + + self.show(inPresenter: presenter, with: verticalPosition) + } + + /// Shows `StatusAlert` in the center of `presenter` + /// + /// - Parameters: + /// - presenter: view present `StatusAlert` in + /// - offset: offset from center in `presenter` + /// - Note: must be called from the main thread only + @objc(showInView:withOffset:) + public func show( + in presenter: UIView, + withOffset offset: CGFloat + ) { + + self.show(inPresenter: presenter, offset: offset) + } + + /// Shows `StatusAlert` in `keyWindow` + /// + /// - Parameters: + /// - verticalPosition: `StatusAlert` position in `keyWindow` + /// - offset: offset for `verticalPosition` in `keyWindow` + /// - Note: must be called from the main thread only + @objc(showWithVerticalPosition:offset:) + public func show( + withVerticalPosition verticalPosition: VerticalPosition, + offset: CGFloat + ) { + + self.show(with: verticalPosition, offset: offset) + } + + /// Shows `StatusAlert` in `presenter` + /// + /// - Parameters: + /// - presenter: view present `StatusAlert` in + /// - verticalPosition: `StatusAlert` position in `presenter` + /// - offset: offset for `verticalPosition` in `presenter`. To use default offset see the same method but without offset parameter. + /// - Note: must be called from the main thread only + @objc(showInView:withVerticalPosition:offset:) + public func show( + in presenter: UIView, + withVerticalPosition verticalPosition: VerticalPosition, + offset: CGFloat + ) { + + self.show( + inPresenter: presenter, + with: verticalPosition, + offset: offset + ) + } + + // MARK: - Private methods - + + private func show( + inPresenter presenter: UIView = UIApplication.shared.keyWindow ?? UIView(), + with verticalPosition: VerticalPosition = .center, + offset: CGFloat? = nil + ) { + + self.assertIsMainThread() + guard !self.isContentEmpty else { return } + + self.prepareForPresentation { [weak self] in + self?.prepareContent() + self?.positionAlert( + inPresenter: presenter, + withVerticalPosition: verticalPosition, + offset: offset + ) + self?.setupContentViewBackground() + self?.observeReduceTransparencyStatus() + self?.performPresentation() + } + } + + private func commonInit() { + self.setupView() + self.setupContentView() + self.setupContentStackView() + + self.layoutViews() + + self.setupPickGestureRecognizer() + self.setupAccessibilityProperties() + } + + private func setupView() { + self.translatesAutoresizingMaskIntoConstraints = false + } + + private func setupContentView() { + if self.isBlurAvailable { + if #available(iOS 11, *) { + self.alpha = 0 + } else { + self.contentView.contentView.alpha = 0 + } + } else { + self.alpha = 0 + } + + self.contentView.clipsToBounds = true + self.contentView.layer.cornerRadius = self.sizesAndDistances.cornerRadius + } + + private func setupContentStackView() { + self.contentStackView.axis = .vertical + self.contentStackView.distribution = .fill + self.contentStackView.alignment = .center + self.contentStackView.spacing = 0 + } + + private func layoutViews() { + self.addSubview(self.contentView) + self.contentView.contentView.addSubview(self.contentStackView) + + self.contentView.translatesAutoresizingMaskIntoConstraints = false + self.contentView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true + self.contentView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true + self.contentView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + self.contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true + + self.contentStackView.translatesAutoresizingMaskIntoConstraints = false + self.contentStackView.leftAnchor.constraint( + equalTo: self.contentView.leftAnchor, + constant: self.sizesAndDistances.stackViewSideSpace + ).isActive = true + self.contentStackView.rightAnchor.constraint( + equalTo: self.contentView.rightAnchor, + constant: -self.sizesAndDistances.stackViewSideSpace + ).isActive = true + self.contentStackView.bottomAnchor.constraint( + greaterThanOrEqualTo: self.contentView.bottomAnchor, + constant: -self.sizesAndDistances.minimumStackViewBottomSpace + ).isActive = true + self.contentStackView.bottomAnchor.constraint( + lessThanOrEqualTo: self.contentView.bottomAnchor + ).isActive = true + self.contentStackView.centerXAnchor.constraint( + equalTo: self.contentView.centerXAnchor + ).isActive = true + } + + private func setupPickGestureRecognizer() { + self.pickGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.pick)) + if let gesture = self.pickGesture { + gesture.allowableMovement = CGFloat.greatestFiniteMagnitude + gesture.minimumPressDuration = 0 + gesture.cancelsTouchesInView = true + self.contentView.addGestureRecognizer(gesture) + } + } + + private func observeReduceTransparencyStatus() { + NotificationCenter.default.addObserver( + self, + selector: #selector(self.reduceTransparencyStatusDidChange), + name: AccessibilityReduceTransparencyStatusDidChangeNotificationName, + object: nil + ) + } + + private func setupAccessibilityProperties() { + self.isAccessibilityElement = false + self.accessibilityElementsHidden = true + self.accessibilityTraits = AccessibilityTraitNone + } + + private func resetView() { + self.deactivateConstraints(&self.contentStackViewConstraints) + + self.enqueueReusableObjects() + } + + private func enqueueReusableObjects() { + self.deactivateConstraints(&self.reusableObjectsConstraints) + if let imageView = self.imageView { + imageView.removeFromSuperview() + StatusAlert.reusableImageViewsManager.enqueueReusable(imageView) + self.imageView = nil + } + if let titleLabel = self.titleLabel { + titleLabel.removeFromSuperview() + StatusAlert.reusableLabelsManager.enqueueReusable(titleLabel) + self.titleLabel = nil + } + if let messageLabel = self.messageLabel { + messageLabel.removeFromSuperview() + StatusAlert.reusableLabelsManager.enqueueReusable(messageLabel) + self.messageLabel = nil + } + } + + private func deactivateConstraints(_ array: inout [NSLayoutConstraint]) { + NSLayoutConstraint.deactivate(array) + array = [] + } + + // MARK: Creation methods + + /// Must be called before the `StatusAlert` presenting + private func prepareContent() { + self.completeContentStackViewConstraints() + + self.imageView = self.createImageViewIfNeeded() + if let imageView = self.imageView { + let customSpace: CGFloat + + if self.title != nil && self.message != nil { + customSpace = self.sizesAndDistances.imageBottomSpace + } else if self.title == nil { + customSpace = self.sizesAndDistances.imageToMessageSpace + } else { + customSpace = self.sizesAndDistances.titleBottomSpace + } + + self.contentStackView.addArrangedSubview(imageView) + if #available(iOS 11.0, *) { + self.contentStackView.setCustomSpacing(customSpace, after: imageView) + } else if self.title != nil || self.message != nil { + let spaceView = self.createSpaceView(withHeight: customSpace) + self.contentStackView.addArrangedSubview(spaceView) + } + } + + self.titleLabel = self.createTitleLabelIfNeeded() + if let titleLabel = self.titleLabel { + self.contentStackView.addArrangedSubview(titleLabel) + if #available(iOS 11.0, *) { + self.contentStackView.setCustomSpacing(self.sizesAndDistances.titleBottomSpace, after: titleLabel) + } else if self.message != nil { + let spaceView = self.createSpaceView(withHeight: self.sizesAndDistances.titleBottomSpace) + self.contentStackView.addArrangedSubview(spaceView) + } + } + + self.messageLabel = self.createMessageLabelIfNeeded() + if let messageLabel = self.messageLabel { + self.contentStackView.addArrangedSubview(messageLabel) + } + + NSLayoutConstraint.activate(self.reusableObjectsConstraints) + } + + private func positionAlert( + inPresenter presenter: UIView, + withVerticalPosition verticalPosition: VerticalPosition, + offset: CGFloat? + ) { + + presenter.addSubview(self) + + self.centerXAnchor.constraint(equalTo: presenter.centerXAnchor).isActive = true + + switch verticalPosition { + case .center: + self.centerYAnchor.constraint( + equalTo: presenter.centerYAnchor, + constant: offset ?? 0 + ).isActive = true + case .top: + if #available(iOS 11, *) { + self.topAnchor.constraint( + equalTo: presenter.safeAreaLayoutGuide.topAnchor, + constant: offset ?? self.sizesAndDistances.topOffset + ).isActive = true + } else { + self.topAnchor.constraint( + equalTo: presenter.topAnchor, + constant: offset ?? self.sizesAndDistances.topOffset + ).isActive = true + } + case .bottom: + if #available(iOS 11, *) { + self.bottomAnchor.constraint( + equalTo: presenter.safeAreaLayoutGuide.bottomAnchor, + constant: offset ?? -self.sizesAndDistances.bottomOffset + ).isActive = true + } else { + self.bottomAnchor.constraint( + equalTo: presenter.bottomAnchor, + constant: offset ?? -self.sizesAndDistances.bottomOffset + ).isActive = true + } + } + } + + private func completeContentStackViewConstraints() { + var constraints: [NSLayoutConstraint] = [] + if self.image != nil + && (self.title != nil || self.message != nil) { + + constraints.append(self.contentView.heightAnchor.constraint( + greaterThanOrEqualToConstant: self.sizesAndDistances.minimumAlertHeight + )) + constraints.append(self.contentView.widthAnchor.constraint( + equalToConstant: self.sizesAndDistances.alertWidth + )) + constraints.append(self.contentStackView.topAnchor.constraint( + greaterThanOrEqualTo: self.contentView.topAnchor, + constant: self.sizesAndDistances.minimumStackViewTopSpace + )) + constraints.append(self.contentStackView.centerYAnchor.constraint( + equalTo: self.contentView.centerYAnchor, + constant: (self.sizesAndDistances.minimumStackViewTopSpace - self.sizesAndDistances.minimumStackViewBottomSpace) / 2 + )) + } else { + if self.image == nil { + constraints.append(self.contentView.widthAnchor.constraint( + equalToConstant: self.sizesAndDistances.alertWidth + )) + } + constraints.append(self.contentStackView.topAnchor.constraint( + greaterThanOrEqualTo: self.contentView.topAnchor, + constant: self.sizesAndDistances.minimumStackViewBottomSpace + )) + constraints.append(self.contentStackView.centerYAnchor.constraint( + equalTo: self.contentView.centerYAnchor + )) + } + + self.contentStackViewConstraints.append(contentsOf: constraints) + NSLayoutConstraint.activate(self.contentStackViewConstraints) + } + + @objc private func reduceTransparencyStatusDidChange() { + self.setupContentViewBackground() + } + + private func setupContentViewBackground() { + if self.isBlurAvailable { + self.contentView.backgroundColor = nil + if #available(iOS 11, *) { + self.contentView.effect = self.blurEffect + } else if StatusAlert.currentlyPresentedStatusAlerts.contains(self) { + self.contentView.effect = self.blurEffect + } + } else { + self.contentView.effect = nil + self.contentView.backgroundColor = self.appearance.backgroundColor + } + } + + private func createSpaceView( + withHeight height: CGFloat + ) -> UIView { + + let spaceView = StatusAlert.reusableSpaceViewsManager.dequeueReusable() + let constraint = spaceView.heightAnchor.constraint(equalToConstant: height) + self.reusableObjectsConstraints.append(constraint) + return spaceView + } + + private func createImageViewIfNeeded() -> UIImageView? { + guard let image = self.image else { return nil } + + let imageView = StatusAlert.reusableImageViewsManager.dequeueReusable() + imageView.image = image + imageView.tintColor = self.appearance.tintColor + let widthConstraint = imageView.widthAnchor.constraint(equalToConstant: sizesAndDistances.imageWidth) + self.reusableObjectsConstraints.append(widthConstraint) + let heightConstraint = imageView.heightAnchor.constraint(equalToConstant: sizesAndDistances.imageWidth) + self.reusableObjectsConstraints.append(heightConstraint) + + return imageView + } + + private func createTitleLabelIfNeeded() -> UILabel? { + guard let title = self.title else { return nil } + + let titleLabel = self.createBaseLabel() + titleLabel.font = self.appearance.titleFont + + let attributedText = NSAttributedString( + string: title, + attributes: [KernAttributeName: 0.01] + ) + titleLabel.attributedText = attributedText + + return titleLabel + } + + private func createMessageLabelIfNeeded() -> UILabel? { + guard let message = self.message else { return nil } + + let messageLabel = self.createBaseLabel() + messageLabel.font = self.appearance.messageFont + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 3 + paragraphStyle.alignment = .center + let attributedText = NSAttributedString( + string: message, + attributes: [ + KernAttributeName: 0.01, + ParagraphStyleAttributeName: paragraphStyle + ] + ) + messageLabel.attributedText = attributedText + + return messageLabel + } + + private func createBaseLabel() -> UILabel { + let label = StatusAlert.reusableLabelsManager.dequeueReusable() + label.textColor = self.appearance.tintColor + return label + } + + // MARK: Presentation methods + + private func prepareForPresentation( + onPrepared: @escaping () -> Void + ) { + + switch StatusAlert.multiplePresentationsBehavior { + case .ignoreIfAlreadyPresenting: + guard StatusAlert.currentlyPresentedStatusAlerts.isEmpty else { return } + onPrepared() + case .showMultiple: + guard !StatusAlert.currentlyPresentedStatusAlerts.contains(self) else { return } + onPrepared() + case .dismissCurrentlyPresented: + guard !StatusAlert.dismissing else { return } + if !StatusAlert.currentlyPresentedStatusAlerts.isEmpty { + StatusAlert.alertToPresent = self + let group = DispatchGroup() + for alert in StatusAlert.currentlyPresentedStatusAlerts { + group.enter() + alert.dismiss { + group.leave() + } + } + group.notify(queue: DispatchQueue.main) { + onPrepared() + StatusAlert.alertToPresent = nil + } + } else { + onPrepared() + } + } + } + + private func performPresentation() { + StatusAlert.currentlyPresentedStatusAlerts.append(self) + + let scale: CGFloat = self.sizesAndDistances.initialScale + let timer = Timer.scheduledTimer( + timeInterval: self.alertShowingDuration - self.defaultFadeAnimationDuration, + target: self, + selector: #selector(self.dismissByTimer), + userInfo: nil, + repeats: false) + RunLoop.main.add( + timer, + forMode: RunLoopCommonMode + ) + self.timer = timer + self.contentView.transform = CGAffineTransform.identity.scaledBy(x: scale, y: scale) + + UIView.animate( + withDuration: self.defaultFadeAnimationDuration, + delay: 0, + options: .curveEaseOut, + animations: { + if self.isBlurAvailable { + if #available(iOS 11, *) { + self.alpha = 1 + } else { + self.contentView.contentView.alpha = 1 + self.contentView.effect = self.blurEffect + } + } else { + self.alpha = 1 + } + self.contentView.transform = CGAffineTransform.identity + }, + completion: { [weak self] (_) in + self?.postAccessibilityAnnouncement() + }) + } + + private func postAccessibilityAnnouncement() { + let announcement = self.accessibilityAnnouncement + #if swift(>=4.2) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: announcement) + #else + UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement) + #endif + } + + @objc private func dismissByTimer() { + self.dismiss(completion: nil) + } + + private func dismiss(completion: (() -> Void)?) { + let scale: CGFloat = self.sizesAndDistances.initialScale + self.timer?.invalidate() + + if self.pickGesture?.state != .changed + && self.pickGesture?.state != .began { + + self.isUserInteractionEnabled = false + UIView.animate( + withDuration: self.defaultFadeAnimationDuration, + delay: 0, + options: [.curveEaseOut, .beginFromCurrentState], + animations: { + if self.isBlurAvailable { + if #available(iOS 11, *) { + self.alpha = 0 + } else { + self.alpha = 0 + self.contentView.contentView.alpha = 0 + } + } else { + self.alpha = 0 + } + self.contentView.transform = CGAffineTransform.identity.scaledBy(x: scale, y: scale) + }, + completion: { [weak self] (_) in + if let strongSelf = self, + let index = StatusAlert.currentlyPresentedStatusAlerts.index(of: strongSelf) { + StatusAlert.currentlyPresentedStatusAlerts.remove(at: index) + } + self?.removeFromSuperview() + self?.resetView() + completion?() + }) + } + } + + // MARK: Utils + + private func assertIsMainThread() { + precondition(Thread.isMainThread, "`StatusAlert` must only be used from the main thread.") + } + + // MARK: - Reusable elements - + + // MARK: UIImageView + + private static let reusableImageViewsManager: ReusablesManager = ReusablesManager( + createReusableClosure: { () -> UIImageView in + return StatusAlert.reusableImageView() + }, + prepareForReuseClosure: nil, + maximumReusablesNumber: 5 + ) + + private static func reusableImageView() -> UIImageView { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.isAccessibilityElement = false + imageView.accessibilityTraits = AccessibilityTraitNone + + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + } + + // MARK: UILabel + + private static let reusableLabelsManager: ReusablesManager = ReusablesManager( + createReusableClosure: { () -> UILabel in + return StatusAlert.reusableLabel() + }, + prepareForReuseClosure: nil, + maximumReusablesNumber: 5 + ) + + private static func reusableLabel() -> UILabel { + let label = UILabel() + label.textAlignment = .center + label.numberOfLines = 0 + label.isAccessibilityElement = false + label.accessibilityTraits = AccessibilityTraitNone + return label + } + + // MARK: - SpaceView + + private static let reusableSpaceViewsManager: ReusablesManager = ReusablesManager( + createReusableClosure: { () -> UIView in + return StatusAlert.reusableSpaceView() + }, + prepareForReuseClosure: nil, + maximumReusablesNumber: 10 + ) + + private static func reusableSpaceView() -> UIView { + let spaceView = UIView() + spaceView.backgroundColor = UIColor.clear + spaceView.translatesAutoresizingMaskIntoConstraints = false + return spaceView + } +} + +// Compatibility + +#if swift(>=4.2) +private let KernAttributeName = NSAttributedString.Key.kern +private let ParagraphStyleAttributeName = NSAttributedString.Key.paragraphStyle +#elseif swift(>=4.0) +private let KernAttributeName = NSAttributedStringKey.kern +private let ParagraphStyleAttributeName = NSAttributedStringKey.paragraphStyle +#else +private let KernAttributeName = NSKernAttributeName +private let ParagraphStyleAttributeName = NSParagraphStyleAttributeName +#endif + +#if swift(>=4.2) +private let NavigationControllerHideShowBarDuration = UINavigationController.hideShowBarDuration +private let AccessibilityReduceTransparencyStatusDidChangeNotificationName = UIAccessibility.reduceTransparencyStatusDidChangeNotification +private let AccessibilityTraitNone = UIAccessibilityTraits.none +private let RunLoopCommonMode = RunLoop.Mode.common +#else +private let NavigationControllerHideShowBarDuration = UINavigationControllerHideShowBarDuration +private let AccessibilityReduceTransparencyStatusDidChangeNotificationName = NSNotification.Name.UIAccessibilityReduceTransparencyStatusDidChange +private let AccessibilityTraitNone = UIAccessibilityTraitNone +private let RunLoopCommonMode = RunLoopMode.commonModes +#endif diff --git a/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/StatusAlertUtils.swift b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/StatusAlertUtils.swift new file mode 100644 index 00000000..d250a1d1 --- /dev/null +++ b/Santander-Test/Pods/StatusAlert/Sources/StatusAlert/StatusAlertUtils.swift @@ -0,0 +1,207 @@ +// +// StatusAlert +// Copyright © 2017-2018 Yegor Miroshnichenko. Licensed under the MIT license. +// + +import UIKit + +@objc extension StatusAlert { + + @objc(StatusAlertMultiplePresentationsBehavior) + public enum MultiplePresentationsBehavior: Int { + + /// Not more than one StatusAlert will be shown at once + case ignoreIfAlreadyPresenting + + /// Currently presented StatusAlerts will be dismissed before presenting another one + case dismissCurrentlyPresented + + /// All requested StatusAlerts will be shown + case showMultiple + } + + @objc(StatusAlertAppearance) + public final class Appearance: NSObject { + + @objc public static let common: Appearance = Appearance() + + /// - Note: Do not change to save system look + @objc public var titleFont: UIFont = UIFont.systemFont(ofSize: 23, weight: FontWeightSemibold) + + /// - Note: Do not change to save system look + @objc public var messageFont: UIFont = UIFont.systemFont(ofSize: 16, weight: FontWeightRegular) + + /// - Note: Do not change to save system look + @objc public var tintColor: UIColor = UIColor.darkGray + + /// Used if device does not support blur or if `Reduce Transparency` toggle + /// in `General->Accessibility->Increase Contrast` is on + /// + /// - Note: Do not change to save system look + @objc public var backgroundColor: UIColor = UIColor.groupTableViewBackground + + /// - Note: Do not change to save system look + @objc public var blurStyle: UIBlurEffect.Style = .light + + @objc public static func copyCommon() -> Appearance { + let common = Appearance.common + let copy = Appearance() + copy.titleFont = common.titleFont + copy.messageFont = common.messageFont + copy.tintColor = common.tintColor + copy.backgroundColor = common.backgroundColor + copy.blurStyle = common.blurStyle + return copy + } + } + + @objc(StatusAlertVerticalPosition) + public enum VerticalPosition: Int { + + /// Position in the center of the view + case center + + /// Position on the top of the view + case top + + /// Position at the bottom of the view + case bottom + } + + @objc (StatusAlertSizesAndDistances) + public final class SizesAndDistances: NSObject { + + @objc public static let common: SizesAndDistances = SizesAndDistances() + + @available(*, deprecated, renamed: "initialScale") + @objc public var defaultInitialScale: CGFloat { + get { return self.initialScale } + set { self.initialScale = newValue } + } + @available(*, deprecated, renamed: "cornerRadius") + @objc public var defaultCornerRadius: CGFloat { + get { return self.cornerRadius } + set { self.cornerRadius = newValue } + } + @available(*, deprecated, renamed: "topOffset") + @objc public var defaultTopOffset: CGFloat { + get { return self.topOffset } + set { self.topOffset = newValue } + } + @available(*, deprecated, renamed: "bottomOffset") + @objc public var defaultBottomOffset: CGFloat { + get { return self.bottomOffset } + set { self.bottomOffset = newValue } + } + @available(*, deprecated, renamed: "imageWidth") + @objc public var defaultImageWidth: CGFloat { + get { return self.imageWidth } + set { self.imageWidth = newValue } + } + @available(*, deprecated, renamed: "alertWidth") + @objc public var defaultAlertWidth: CGFloat { + get { return self.alertWidth } + set { self.alertWidth = newValue } + } + @available(*, deprecated, renamed: "imageBottomSpace") + @objc public var defaultImageBottomSpace: CGFloat { + get { return self.imageBottomSpace } + set { self.imageBottomSpace = newValue } + } + @available(*, deprecated, renamed: "titleBottomSpace") + @objc public var defaultTitleBottomSpace: CGFloat { + get { return self.titleBottomSpace } + set { self.titleBottomSpace = newValue } + } + @available(*, deprecated, renamed: "imageToMessageSpace") + @objc public var defaultImageToMessageSpace: CGFloat { + get { return self.imageToMessageSpace } + set { self.imageToMessageSpace = newValue } + } + + @objc public var initialScale: CGFloat = 0.9 + @objc public var cornerRadius: CGFloat = 10 + + @objc public var topOffset: CGFloat = 32 + @objc public var bottomOffset: CGFloat = 32 + + @objc public var imageWidth: CGFloat = 90 + @objc public var alertWidth: CGFloat = 258 + @objc public var minimumAlertHeight: CGFloat = 240 + + @objc public var minimumStackViewTopSpace: CGFloat = 44 + @objc public var minimumStackViewBottomSpace: CGFloat = 24 + @objc public var stackViewSideSpace: CGFloat = 24 + + @objc public var imageBottomSpace: CGFloat = 30 + @objc public var titleBottomSpace: CGFloat = 5 + @objc public var imageToMessageSpace: CGFloat = 24 + + @objc public static func copyCommon() -> SizesAndDistances { + let common = SizesAndDistances.common + let copy = SizesAndDistances() + + copy.initialScale = common.initialScale + copy.cornerRadius = common.cornerRadius + copy.topOffset = common.topOffset + copy.bottomOffset = common.bottomOffset + copy.imageWidth = common.imageWidth + copy.alertWidth = common.alertWidth + copy.minimumAlertHeight = common.minimumAlertHeight + copy.minimumStackViewTopSpace = common.minimumStackViewTopSpace + copy.minimumStackViewBottomSpace = common.minimumStackViewBottomSpace + copy.stackViewSideSpace = common.stackViewSideSpace + copy.imageBottomSpace = common.imageBottomSpace + copy.titleBottomSpace = common.titleBottomSpace + copy.imageToMessageSpace = common.imageToMessageSpace + return copy + } + } +} + +internal class ReusablesManager { + typealias PrepareForReuse = (Reusable) -> Void + typealias CreateReusableClosure = () -> Reusable + + private var reusables: [Reusable] = [] + private let maximumReusablesNumber: Int + private let createReusableClosure: CreateReusableClosure + private let prepareForReuseClosure: PrepareForReuse? + + init( + createReusableClosure: @escaping CreateReusableClosure, + prepareForReuseClosure: PrepareForReuse?, + maximumReusablesNumber: Int + ) { + + self.createReusableClosure = createReusableClosure + self.prepareForReuseClosure = prepareForReuseClosure + self.maximumReusablesNumber = maximumReusablesNumber + } + + func dequeueReusable() -> Reusable { + if let reusable = self.reusables.first { + self.reusables.removeFirst() + self.prepareForReuseClosure?(reusable) + return reusable + } + let reusable = self.createReusableClosure() + self.enqueueReusable(reusable) + return self.dequeueReusable() + } + + func enqueueReusable(_ object: Reusable) { + guard self.reusables.count < self.maximumReusablesNumber else { return } + self.reusables.append(object) + } +} + +// Compatibility + +#if swift(>=4.0) +private let FontWeightSemibold = UIFont.Weight.semibold +private let FontWeightRegular = UIFont.Weight.regular +#else +private let FontWeightSemibold = UIFontWeightSemibold +private let FontWeightRegular = UIFontWeightRegular +#endif diff --git a/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-Info.plist b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-Info.plist new file mode 100644 index 00000000..fd3425ec --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-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.6.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-dummy.m b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-dummy.m new file mode 100644 index 00000000..58610775 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_AMPopTip : NSObject +@end +@implementation PodsDummy_AMPopTip +@end diff --git a/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-prefix.pch b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-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/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-umbrella.h b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-umbrella.h new file mode 100644 index 00000000..89d1d10c --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip-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 AMPopTipVersionNumber; +FOUNDATION_EXPORT const unsigned char AMPopTipVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip.modulemap b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip.modulemap new file mode 100644 index 00000000..5d9bb932 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip.modulemap @@ -0,0 +1,6 @@ +framework module AMPopTip { + umbrella header "AMPopTip-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip.xcconfig b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip.xcconfig new file mode 100644 index 00000000..dad67f0f --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/AMPopTip/AMPopTip.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +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}/AMPopTip +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-Info.plist b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-Info.plist new file mode 100644 index 00000000..4a946be6 --- /dev/null +++ b/Santander-Test/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 + 4.8.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-dummy.m b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-dummy.m new file mode 100644 index 00000000..a6c45942 --- /dev/null +++ b/Santander-Test/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/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Santander-Test/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/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h new file mode 100644 index 00000000..00014e3c --- /dev/null +++ b/Santander-Test/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/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire.modulemap b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire.modulemap new file mode 100644 index 00000000..d1f125fa --- /dev/null +++ b/Santander-Test/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/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire.xcconfig b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire.xcconfig new file mode 100644 index 00000000..12a14508 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Alamofire/Alamofire.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +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 +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist new file mode 100644 index 00000000..45024a7b --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 6.2.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-dummy.m b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-dummy.m new file mode 100644 index 00000000..7937f277 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_IQKeyboardManagerSwift : NSObject +@end +@implementation PodsDummy_IQKeyboardManagerSwift +@end diff --git a/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-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/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-umbrella.h b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-umbrella.h new file mode 100644 index 00000000..e95b3987 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-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 IQKeyboardManagerSwiftVersionNumber; +FOUNDATION_EXPORT const unsigned char IQKeyboardManagerSwiftVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap new file mode 100644 index 00000000..6d9b3439 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap @@ -0,0 +1,6 @@ +framework module IQKeyboardManagerSwift { + umbrella header "IQKeyboardManagerSwift-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.xcconfig b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.xcconfig new file mode 100644 index 00000000..ddaa6fad --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +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}/IQKeyboardManagerSwift +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Santander-Test/Pods/Target Support Files/Moya/Moya-Info.plist b/Santander-Test/Pods/Target Support Files/Moya/Moya-Info.plist new file mode 100644 index 00000000..9dfe2ef6 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Moya/Moya-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 12.0.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Santander-Test/Pods/Target Support Files/Moya/Moya-dummy.m b/Santander-Test/Pods/Target Support Files/Moya/Moya-dummy.m new file mode 100644 index 00000000..260473f2 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Moya/Moya-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Moya : NSObject +@end +@implementation PodsDummy_Moya +@end diff --git a/Santander-Test/Pods/Target Support Files/Moya/Moya-prefix.pch b/Santander-Test/Pods/Target Support Files/Moya/Moya-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Moya/Moya-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/Santander-Test/Pods/Target Support Files/Moya/Moya-umbrella.h b/Santander-Test/Pods/Target Support Files/Moya/Moya-umbrella.h new file mode 100644 index 00000000..8d810473 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Moya/Moya-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 MoyaVersionNumber; +FOUNDATION_EXPORT const unsigned char MoyaVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/Moya/Moya.modulemap b/Santander-Test/Pods/Target Support Files/Moya/Moya.modulemap new file mode 100644 index 00000000..7b84cdb9 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Moya/Moya.modulemap @@ -0,0 +1,6 @@ +framework module Moya { + umbrella header "Moya-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/Moya/Moya.xcconfig b/Santander-Test/Pods/Target Support Files/Moya/Moya.xcconfig new file mode 100644 index 00000000..05ef63e1 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Moya/Moya.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Moya +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/Result" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +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}/Moya +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-Info.plist b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-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/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-acknowledgements.markdown b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-acknowledgements.markdown new file mode 100644 index 00000000..bc9f70a3 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-acknowledgements.markdown @@ -0,0 +1,148 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## AMPopTip + +The MIT License (MIT) + +Copyright (c) 2016 Andrea Mazzini + +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. + + +## Alamofire + +Copyright (c) 2014 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. + + +## IQKeyboardManagerSwift + +MIT License + +Copyright (c) 2013-2017 Iftekhar Qurashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +## Moya + +The MIT License (MIT) + +Copyright (c) 2014-present Artsy, Ash Furrow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +## Result + +The MIT License (MIT) + +Copyright (c) 2014 Rob Rix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## StatusAlert + +Copyright (c) 2017-2018 LowKostKustomz + +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. + +Generated by CocoaPods - https://cocoapods.org diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-acknowledgements.plist b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-acknowledgements.plist new file mode 100644 index 00000000..c77529a7 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-acknowledgements.plist @@ -0,0 +1,210 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2016 Andrea Mazzini + +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 + AMPopTip + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2014 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 + MIT License + +Copyright (c) 2013-2017 Iftekhar Qurashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + License + MIT + Title + IQKeyboardManagerSwift + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2014-present Artsy, Ash Furrow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + License + MIT + Title + Moya + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2014 Rob Rix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + License + MIT + Title + Result + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2017-2018 LowKostKustomz <mierosh@gmail.com> + +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 + StatusAlert + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-dummy.m b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-dummy.m new file mode 100644 index 00000000..958df9c5 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Santander_Test : NSObject +@end +@implementation PodsDummy_Pods_Santander_Test +@end diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-frameworks.sh b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-frameworks.sh new file mode 100755 index 00000000..bc4bfd5a --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-frameworks.sh @@ -0,0 +1,173 @@ +#!/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}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# 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 + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --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" + if [ -r "$source" ]; then + # Copy the dSYM into a 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 .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --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[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.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. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# 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 +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # 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 + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + 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=1 +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/AMPopTip/AMPopTip.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Result/Result.framework" + install_framework "${BUILT_PRODUCTS_DIR}/StatusAlert/StatusAlert.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/AMPopTip/AMPopTip.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" + install_framework "${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Result/Result.framework" + install_framework "${BUILT_PRODUCTS_DIR}/StatusAlert/StatusAlert.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-umbrella.h b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-umbrella.h new file mode 100644 index 00000000..b6ea7096 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-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_Santander_TestVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_Santander_TestVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.debug.xcconfig b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.debug.xcconfig new file mode 100644 index 00000000..cd7548aa --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.debug.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Result" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip/AMPopTip.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Result/Result.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert/StatusAlert.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "AMPopTip" -framework "Alamofire" -framework "CoreGraphics" -framework "Foundation" -framework "IQKeyboardManagerSwift" -framework "Moya" -framework "QuartzCore" -framework "Result" -framework "StatusAlert" -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 diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.modulemap b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.modulemap new file mode 100644 index 00000000..075e8816 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.modulemap @@ -0,0 +1,6 @@ +framework module Pods_Santander_Test { + umbrella header "Pods-Santander-Test-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.release.xcconfig b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.release.xcconfig new file mode 100644 index 00000000..cd7548aa --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-Test/Pods-Santander-Test.release.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Result" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip/AMPopTip.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Result/Result.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert/StatusAlert.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "AMPopTip" -framework "Alamofire" -framework "CoreGraphics" -framework "Foundation" -framework "IQKeyboardManagerSwift" -framework "Moya" -framework "QuartzCore" -framework "Result" -framework "StatusAlert" -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 diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-Info.plist b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-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/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-acknowledgements.markdown b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-acknowledgements.markdown new file mode 100644 index 00000000..102af753 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-acknowledgements.plist b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-acknowledgements.plist new file mode 100644 index 00000000..7acbad1e --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-dummy.m b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-dummy.m new file mode 100644 index 00000000..7c07c6a8 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Santander_TestTests : NSObject +@end +@implementation PodsDummy_Pods_Santander_TestTests +@end diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-umbrella.h b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-umbrella.h new file mode 100644 index 00000000..951842d9 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests-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_Santander_TestTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_Santander_TestTestsVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.debug.xcconfig b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.debug.xcconfig new file mode 100644 index 00000000..143eadc5 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.debug.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Result" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip/AMPopTip.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Result/Result.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert/StatusAlert.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "AMPopTip" -framework "Alamofire" -framework "CoreGraphics" -framework "Foundation" -framework "IQKeyboardManagerSwift" -framework "Moya" -framework "QuartzCore" -framework "Result" -framework "StatusAlert" -framework "UIKit" +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 diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.modulemap b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.modulemap new file mode 100644 index 00000000..eb9e407f --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_Santander_TestTests { + umbrella header "Pods-Santander-TestTests-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.release.xcconfig b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.release.xcconfig new file mode 100644 index 00000000..143eadc5 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.release.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Result" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip/AMPopTip.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Result/Result.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert/StatusAlert.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "AMPopTip" -framework "Alamofire" -framework "CoreGraphics" -framework "Foundation" -framework "IQKeyboardManagerSwift" -framework "Moya" -framework "QuartzCore" -framework "Result" -framework "StatusAlert" -framework "UIKit" +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 diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-Info.plist b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-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/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-acknowledgements.markdown b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-acknowledgements.markdown new file mode 100644 index 00000000..102af753 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-acknowledgements.plist b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-acknowledgements.plist new file mode 100644 index 00000000..7acbad1e --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-dummy.m b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-dummy.m new file mode 100644 index 00000000..d432ce4f --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Santander_TestUITests : NSObject +@end +@implementation PodsDummy_Pods_Santander_TestUITests +@end diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-umbrella.h b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-umbrella.h new file mode 100644 index 00000000..51307d78 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests-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_Santander_TestUITestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_Santander_TestUITestsVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.debug.xcconfig b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.debug.xcconfig new file mode 100644 index 00000000..143eadc5 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.debug.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Result" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip/AMPopTip.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Result/Result.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert/StatusAlert.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "AMPopTip" -framework "Alamofire" -framework "CoreGraphics" -framework "Foundation" -framework "IQKeyboardManagerSwift" -framework "Moya" -framework "QuartzCore" -framework "Result" -framework "StatusAlert" -framework "UIKit" +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 diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.modulemap b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.modulemap new file mode 100644 index 00000000..aa00e249 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_Santander_TestUITests { + umbrella header "Pods-Santander-TestUITests-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.release.xcconfig b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.release.xcconfig new file mode 100644 index 00000000..143eadc5 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.release.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Result" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AMPopTip/AMPopTip.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Result/Result.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert/StatusAlert.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "AMPopTip" -framework "Alamofire" -framework "CoreGraphics" -framework "Foundation" -framework "IQKeyboardManagerSwift" -framework "Moya" -framework "QuartzCore" -framework "Result" -framework "StatusAlert" -framework "UIKit" +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 diff --git a/Santander-Test/Pods/Target Support Files/Result/Result-Info.plist b/Santander-Test/Pods/Target Support Files/Result/Result-Info.plist new file mode 100644 index 00000000..c26f36f0 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Result/Result-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/Santander-Test/Pods/Target Support Files/Result/Result-dummy.m b/Santander-Test/Pods/Target Support Files/Result/Result-dummy.m new file mode 100644 index 00000000..ba47f618 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Result/Result-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Result : NSObject +@end +@implementation PodsDummy_Result +@end diff --git a/Santander-Test/Pods/Target Support Files/Result/Result-prefix.pch b/Santander-Test/Pods/Target Support Files/Result/Result-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Result/Result-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/Santander-Test/Pods/Target Support Files/Result/Result-umbrella.h b/Santander-Test/Pods/Target Support Files/Result/Result-umbrella.h new file mode 100644 index 00000000..25f5eb12 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Result/Result-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 ResultVersionNumber; +FOUNDATION_EXPORT const unsigned char ResultVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/Result/Result.modulemap b/Santander-Test/Pods/Target Support Files/Result/Result.modulemap new file mode 100644 index 00000000..c5e6a4a2 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Result/Result.modulemap @@ -0,0 +1,6 @@ +framework module Result { + umbrella header "Result-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/Result/Result.xcconfig b/Santander-Test/Pods/Target Support Files/Result/Result.xcconfig new file mode 100644 index 00000000..4ca6b3d9 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/Result/Result.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Result +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +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}/Result +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-Info.plist b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-Info.plist new file mode 100644 index 00000000..21a30b4a --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-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.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-dummy.m b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-dummy.m new file mode 100644 index 00000000..173ee2bd --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_StatusAlert : NSObject +@end +@implementation PodsDummy_StatusAlert +@end diff --git a/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-prefix.pch b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-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/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-umbrella.h b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-umbrella.h new file mode 100644 index 00000000..8b389b5d --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert-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 StatusAlertVersionNumber; +FOUNDATION_EXPORT const unsigned char StatusAlertVersionString[]; + diff --git a/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert.modulemap b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert.modulemap new file mode 100644 index 00000000..f2c3867b --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert.modulemap @@ -0,0 +1,6 @@ +framework module StatusAlert { + umbrella header "StatusAlert-umbrella.h" + + export * + module * { export * } +} diff --git a/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert.xcconfig b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert.xcconfig new file mode 100644 index 00000000..38bc402c --- /dev/null +++ b/Santander-Test/Pods/Target Support Files/StatusAlert/StatusAlert.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/StatusAlert +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +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}/StatusAlert +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Santander-Test/Santander-Test.xcodeproj/project.pbxproj b/Santander-Test/Santander-Test.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a0968315 --- /dev/null +++ b/Santander-Test/Santander-Test.xcodeproj/project.pbxproj @@ -0,0 +1,1150 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 493342D14E83E8F2E5CA3C31 /* Pods_Santander_TestTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0815CC0FAEEAF8D6E86F6807 /* Pods_Santander_TestTests.framework */; }; + 965C2F0722895E2500015140 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F0622895E2500015140 /* AppDelegate.swift */; }; + 965C2F1122895E2800015140 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F0F22895E2800015140 /* LaunchScreen.storyboard */; }; + 965C2F2722895E2800015140 /* Santander_TestUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F2622895E2800015140 /* Santander_TestUITests.swift */; }; + 965C2F36228966AF00015140 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F35228966AF00015140 /* Constants.swift */; }; + 965C2F382289681F00015140 /* FundsServiceApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F372289681F00015140 /* FundsServiceApi.swift */; }; + 965C2F3B228974F500015140 /* FundsServiceApiManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F3A228974F500015140 /* FundsServiceApiManager.swift */; }; + 965C2F3E2289776F00015140 /* FormCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F3D2289776F00015140 /* FormCell.swift */; }; + 965C2F4022897AF800015140 /* Fund.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F3F22897AF800015140 /* Fund.swift */; }; + 965C2F55228A891900015140 /* FormPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F4F228A891900015140 /* FormPresenter.swift */; }; + 965C2F56228A891900015140 /* FormWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F50228A891900015140 /* FormWorker.swift */; }; + 965C2F57228A891900015140 /* FormRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F51228A891900015140 /* FormRouter.swift */; }; + 965C2F58228A891900015140 /* FormModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F52228A891900015140 /* FormModels.swift */; }; + 965C2F59228A891900015140 /* FormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F53228A891900015140 /* FormViewController.swift */; }; + 965C2F5A228A891900015140 /* FormInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F54228A891900015140 /* FormInteractor.swift */; }; + 965C2F5C228A8CC200015140 /* Form.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F5B228A8CC200015140 /* Form.storyboard */; }; + 965C2F64228A9B5800015140 /* FieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F63228A9B5800015140 /* FieldCell.xib */; }; + 965C2F66228A9DFC00015140 /* FieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F65228A9DFC00015140 /* FieldCell.swift */; }; + 965C2F69228B6D6400015140 /* TextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F68228B6D6400015140 /* TextCell.xib */; }; + 965C2F6B228B6E1E00015140 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F6A228B6E1E00015140 /* TextCell.swift */; }; + 965C2F6E228B6EB100015140 /* CheckboxCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F6D228B6EB100015140 /* CheckboxCell.xib */; }; + 965C2F70228B6EBE00015140 /* CheckboxCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F6F228B6EBE00015140 /* CheckboxCell.swift */; }; + 965C2F73228B9A4C00015140 /* SendCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F72228B9A4C00015140 /* SendCell.swift */; }; + 965C2F75228B9A8000015140 /* SendCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F74228B9A8000015140 /* SendCell.xib */; }; + 965C2F78228BBDB300015140 /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F77228BBDB300015140 /* UIColorExtensions.swift */; }; + 965C2F82228CAB1000015140 /* FieldValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F81228CAB1000015140 /* FieldValidation.swift */; }; + 965C2F84228D409C00015140 /* MainTabBar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F83228D409C00015140 /* MainTabBar.storyboard */; }; + 965C2F86228D474300015140 /* MainTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F85228D474300015140 /* MainTabBar.swift */; }; + 965C2F91228D502200015140 /* SuccessRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F8B228D502200015140 /* SuccessRouter.swift */; }; + 965C2F93228D502200015140 /* SuccessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2F8D228D502200015140 /* SuccessViewController.swift */; }; + 965C2F96228D503300015140 /* Success.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F95228D503300015140 /* Success.storyboard */; }; + 965C2FA1228D5BF000015140 /* DINPro-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F98228D5BF000015140 /* DINPro-Light.otf */; }; + 965C2FA2228D5BF000015140 /* DINMittelschriftStd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F99228D5BF000015140 /* DINMittelschriftStd.otf */; }; + 965C2FA3228D5BF000015140 /* DINPro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F9A228D5BF000015140 /* DINPro-Black.otf */; }; + 965C2FA4228D5BF000015140 /* DINPro-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F9B228D5BF000015140 /* DINPro-Bold.otf */; }; + 965C2FA5228D5BF000015140 /* DINPro-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F9C228D5BF000015140 /* DINPro-Regular.otf */; }; + 965C2FA6228D5BF000015140 /* DINEngschriftStd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F9D228D5BF000015140 /* DINEngschriftStd.otf */; }; + 965C2FA7228D5BF000015140 /* DINPro-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F9E228D5BF000015140 /* DINPro-Medium.otf */; }; + 965C2FA8228D5BF000015140 /* DINNeuzeitGroteskStd-BdCond.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2F9F228D5BF000015140 /* DINNeuzeitGroteskStd-BdCond.otf */; }; + 965C2FA9228D5BF000015140 /* DINNeuzeitGroteskStd-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FA0228D5BF000015140 /* DINNeuzeitGroteskStd-Light.otf */; }; + 965C2FAB228DF72A00015140 /* SendButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FAA228DF72A00015140 /* SendButton.swift */; }; + 965C2FBF228E128E00015140 /* FundDetailPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FB9228E128E00015140 /* FundDetailPresenter.swift */; }; + 965C2FC0228E128E00015140 /* FundDetailWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FBA228E128E00015140 /* FundDetailWorker.swift */; }; + 965C2FC1228E128E00015140 /* FundDetailRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FBB228E128E00015140 /* FundDetailRouter.swift */; }; + 965C2FC2228E128E00015140 /* FundDetailModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FBC228E128E00015140 /* FundDetailModels.swift */; }; + 965C2FC3228E128E00015140 /* FundDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FBD228E128E00015140 /* FundDetailViewController.swift */; }; + 965C2FC4228E128E00015140 /* FundDetailInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FBE228E128E00015140 /* FundDetailInteractor.swift */; }; + 965C2FC8228E1E0700015140 /* FundHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FC7228E1E0700015140 /* FundHeader.xib */; }; + 965C2FCA228E244900015140 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FC9228E244900015140 /* Assets.xcassets */; }; + 965C2FCC228E26A900015140 /* FundHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FCB228E26A900015140 /* FundHeader.swift */; }; + 965C2FD0228E4C5A00015140 /* FundRisk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FCF228E4C5A00015140 /* FundRisk.swift */; }; + 965C2FD2228E4D0D00015140 /* FundRisk.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FD1228E4D0D00015140 /* FundRisk.xib */; }; + 965C2FD6228E5C4100015140 /* FundDetail.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FD5228E5C4100015140 /* FundDetail.storyboard */; }; + 965C2FD9228E9D4600015140 /* FundMoreInfoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FD8228E9D4600015140 /* FundMoreInfoCell.xib */; }; + 965C2FDB228E9D5A00015140 /* FundMoreInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FDA228E9D5A00015140 /* FundMoreInfoCell.swift */; }; + 965C2FDE228EA9BD00015140 /* FundInfoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FDD228EA9BD00015140 /* FundInfoCell.xib */; }; + 965C2FE0228EA9CB00015140 /* FundInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FDF228EA9CB00015140 /* FundInfoCell.swift */; }; + 965C2FE3228EACC300015140 /* FundDownInfoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 965C2FE2228EACC300015140 /* FundDownInfoCell.xib */; }; + 965C2FE5228EACD400015140 /* FundDownInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FE4228EACD400015140 /* FundDownInfoCell.swift */; }; + 965C2FE7228EE8CC00015140 /* UIViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FE6228EE8CC00015140 /* UIViewControllerExtensions.swift */; }; + 965C2FED2292614600015140 /* UIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FEC2292614600015140 /* UIViewExtensions.swift */; }; + 965C2FEF2292BA6C00015140 /* CheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C2FEE2292BA6C00015140 /* CheckBox.swift */; }; + 965C302B2292ED7800015140 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C302A2292ED7800015140 /* StringExtensions.swift */; }; + 965C30342292F51100015140 /* FieldValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C30332292F51100015140 /* FieldValidationTests.swift */; }; + 965C30362292F81300015140 /* FloatExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C30352292F81300015140 /* FloatExtensions.swift */; }; + 965C30382292F8E000015140 /* FloatExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C30372292F8E000015140 /* FloatExtensionsTests.swift */; }; + 973161DFBDDB30BB534A86DA /* Pods_Santander_TestUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 878E0A16E793237D34137EC7 /* Pods_Santander_TestUITests.framework */; }; + C4FCFE13C06111F180515DFA /* Pods_Santander_Test.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F563E135B794CADBF7401F8 /* Pods_Santander_Test.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 965C2F1822895E2800015140 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 965C2EFB22895E2500015140 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 965C2F0222895E2500015140; + remoteInfo = "Santander-Test"; + }; + 965C2F2322895E2800015140 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 965C2EFB22895E2500015140 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 965C2F0222895E2500015140; + remoteInfo = "Santander-Test"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0815CC0FAEEAF8D6E86F6807 /* Pods_Santander_TestTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Santander_TestTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 21E66E0DEC791ED96A6B4107 /* Pods-Santander-TestUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santander-TestUITests.release.xcconfig"; path = "Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.release.xcconfig"; sourceTree = ""; }; + 39073D8A59D67805EB72C3BC /* Pods-Santander-TestTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santander-TestTests.debug.xcconfig"; path = "Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.debug.xcconfig"; sourceTree = ""; }; + 6C7F8C512B2B46A97F1AA7C2 /* Pods-Santander-TestTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santander-TestTests.release.xcconfig"; path = "Target Support Files/Pods-Santander-TestTests/Pods-Santander-TestTests.release.xcconfig"; sourceTree = ""; }; + 878E0A16E793237D34137EC7 /* Pods_Santander_TestUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Santander_TestUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 88AD971CB0FCD8230DDD854A /* Pods-Santander-Test.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santander-Test.release.xcconfig"; path = "Target Support Files/Pods-Santander-Test/Pods-Santander-Test.release.xcconfig"; sourceTree = ""; }; + 8F563E135B794CADBF7401F8 /* Pods_Santander_Test.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Santander_Test.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 944E346D8B737070B6AC9E04 /* Pods-Santander-TestUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santander-TestUITests.debug.xcconfig"; path = "Target Support Files/Pods-Santander-TestUITests/Pods-Santander-TestUITests.debug.xcconfig"; sourceTree = ""; }; + 965C2F0322895E2500015140 /* Santander-Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Santander-Test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 965C2F0622895E2500015140 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 965C2F1022895E2800015140 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 965C2F1222895E2800015140 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 965C2F1722895E2800015140 /* Santander-TestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Santander-TestTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 965C2F1D22895E2800015140 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 965C2F2222895E2800015140 /* Santander-TestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Santander-TestUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 965C2F2622895E2800015140 /* Santander_TestUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Santander_TestUITests.swift; sourceTree = ""; }; + 965C2F2822895E2800015140 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 965C2F35228966AF00015140 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 965C2F372289681F00015140 /* FundsServiceApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundsServiceApi.swift; sourceTree = ""; }; + 965C2F3A228974F500015140 /* FundsServiceApiManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundsServiceApiManager.swift; sourceTree = ""; }; + 965C2F3D2289776F00015140 /* FormCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormCell.swift; sourceTree = ""; }; + 965C2F3F22897AF800015140 /* Fund.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fund.swift; sourceTree = ""; }; + 965C2F4F228A891900015140 /* FormPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormPresenter.swift; sourceTree = ""; }; + 965C2F50228A891900015140 /* FormWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormWorker.swift; sourceTree = ""; }; + 965C2F51228A891900015140 /* FormRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormRouter.swift; sourceTree = ""; }; + 965C2F52228A891900015140 /* FormModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormModels.swift; sourceTree = ""; }; + 965C2F53228A891900015140 /* FormViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormViewController.swift; sourceTree = ""; }; + 965C2F54228A891900015140 /* FormInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormInteractor.swift; sourceTree = ""; }; + 965C2F5B228A8CC200015140 /* Form.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Form.storyboard; sourceTree = ""; }; + 965C2F63228A9B5800015140 /* FieldCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FieldCell.xib; sourceTree = ""; }; + 965C2F65228A9DFC00015140 /* FieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldCell.swift; sourceTree = ""; }; + 965C2F68228B6D6400015140 /* TextCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextCell.xib; sourceTree = ""; }; + 965C2F6A228B6E1E00015140 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = ""; }; + 965C2F6D228B6EB100015140 /* CheckboxCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CheckboxCell.xib; sourceTree = ""; }; + 965C2F6F228B6EBE00015140 /* CheckboxCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxCell.swift; sourceTree = ""; }; + 965C2F72228B9A4C00015140 /* SendCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendCell.swift; sourceTree = ""; }; + 965C2F74228B9A8000015140 /* SendCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SendCell.xib; sourceTree = ""; }; + 965C2F77228BBDB300015140 /* UIColorExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = ""; }; + 965C2F81228CAB1000015140 /* FieldValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldValidation.swift; sourceTree = ""; }; + 965C2F83228D409C00015140 /* MainTabBar.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MainTabBar.storyboard; sourceTree = ""; }; + 965C2F85228D474300015140 /* MainTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBar.swift; sourceTree = ""; }; + 965C2F8B228D502200015140 /* SuccessRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessRouter.swift; sourceTree = ""; }; + 965C2F8D228D502200015140 /* SuccessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessViewController.swift; sourceTree = ""; }; + 965C2F95228D503300015140 /* Success.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Success.storyboard; sourceTree = ""; }; + 965C2F98228D5BF000015140 /* DINPro-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINPro-Light.otf"; path = "20. Din [1926 - Ludwig Goller]/DINPro-Light.otf"; sourceTree = ""; }; + 965C2F99228D5BF000015140 /* DINMittelschriftStd.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = DINMittelschriftStd.otf; path = "20. Din [1926 - Ludwig Goller]/DINMittelschriftStd.otf"; sourceTree = ""; }; + 965C2F9A228D5BF000015140 /* DINPro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINPro-Black.otf"; path = "20. Din [1926 - Ludwig Goller]/DINPro-Black.otf"; sourceTree = ""; }; + 965C2F9B228D5BF000015140 /* DINPro-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINPro-Bold.otf"; path = "20. Din [1926 - Ludwig Goller]/DINPro-Bold.otf"; sourceTree = ""; }; + 965C2F9C228D5BF000015140 /* DINPro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINPro-Regular.otf"; path = "20. Din [1926 - Ludwig Goller]/DINPro-Regular.otf"; sourceTree = ""; }; + 965C2F9D228D5BF000015140 /* DINEngschriftStd.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = DINEngschriftStd.otf; path = "20. Din [1926 - Ludwig Goller]/DINEngschriftStd.otf"; sourceTree = ""; }; + 965C2F9E228D5BF000015140 /* DINPro-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINPro-Medium.otf"; path = "20. Din [1926 - Ludwig Goller]/DINPro-Medium.otf"; sourceTree = ""; }; + 965C2F9F228D5BF000015140 /* DINNeuzeitGroteskStd-BdCond.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINNeuzeitGroteskStd-BdCond.otf"; path = "20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-BdCond.otf"; sourceTree = ""; }; + 965C2FA0228D5BF000015140 /* DINNeuzeitGroteskStd-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINNeuzeitGroteskStd-Light.otf"; path = "20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-Light.otf"; sourceTree = ""; }; + 965C2FAA228DF72A00015140 /* SendButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendButton.swift; sourceTree = ""; }; + 965C2FB9228E128E00015140 /* FundDetailPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDetailPresenter.swift; sourceTree = ""; }; + 965C2FBA228E128E00015140 /* FundDetailWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDetailWorker.swift; sourceTree = ""; }; + 965C2FBB228E128E00015140 /* FundDetailRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDetailRouter.swift; sourceTree = ""; }; + 965C2FBC228E128E00015140 /* FundDetailModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDetailModels.swift; sourceTree = ""; }; + 965C2FBD228E128E00015140 /* FundDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDetailViewController.swift; sourceTree = ""; }; + 965C2FBE228E128E00015140 /* FundDetailInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDetailInteractor.swift; sourceTree = ""; }; + 965C2FC7228E1E0700015140 /* FundHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FundHeader.xib; sourceTree = ""; }; + 965C2FC9228E244900015140 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 965C2FCB228E26A900015140 /* FundHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundHeader.swift; sourceTree = ""; }; + 965C2FCF228E4C5A00015140 /* FundRisk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundRisk.swift; sourceTree = ""; }; + 965C2FD1228E4D0D00015140 /* FundRisk.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FundRisk.xib; sourceTree = ""; }; + 965C2FD5228E5C4100015140 /* FundDetail.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = FundDetail.storyboard; sourceTree = ""; }; + 965C2FD8228E9D4600015140 /* FundMoreInfoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FundMoreInfoCell.xib; sourceTree = ""; }; + 965C2FDA228E9D5A00015140 /* FundMoreInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundMoreInfoCell.swift; sourceTree = ""; }; + 965C2FDD228EA9BD00015140 /* FundInfoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FundInfoCell.xib; sourceTree = ""; }; + 965C2FDF228EA9CB00015140 /* FundInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundInfoCell.swift; sourceTree = ""; }; + 965C2FE2228EACC300015140 /* FundDownInfoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FundDownInfoCell.xib; sourceTree = ""; }; + 965C2FE4228EACD400015140 /* FundDownInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FundDownInfoCell.swift; sourceTree = ""; }; + 965C2FE6228EE8CC00015140 /* UIViewControllerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExtensions.swift; sourceTree = ""; }; + 965C2FEC2292614600015140 /* UIViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtensions.swift; sourceTree = ""; }; + 965C2FEE2292BA6C00015140 /* CheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBox.swift; sourceTree = ""; }; + 965C302A2292ED7800015140 /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; + 965C30332292F51100015140 /* FieldValidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldValidationTests.swift; sourceTree = ""; }; + 965C30352292F81300015140 /* FloatExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatExtensions.swift; sourceTree = ""; }; + 965C30372292F8E000015140 /* FloatExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatExtensionsTests.swift; sourceTree = ""; }; + A512F1F10FEC63C067350139 /* Pods-Santander-Test.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santander-Test.debug.xcconfig"; path = "Target Support Files/Pods-Santander-Test/Pods-Santander-Test.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 965C2F0022895E2500015140 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C4FCFE13C06111F180515DFA /* Pods_Santander_Test.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 965C2F1422895E2800015140 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 493342D14E83E8F2E5CA3C31 /* Pods_Santander_TestTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 965C2F1F22895E2800015140 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 973161DFBDDB30BB534A86DA /* Pods_Santander_TestUITests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19CF96ADE36429C54AA25925 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8F563E135B794CADBF7401F8 /* Pods_Santander_Test.framework */, + 0815CC0FAEEAF8D6E86F6807 /* Pods_Santander_TestTests.framework */, + 878E0A16E793237D34137EC7 /* Pods_Santander_TestUITests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 965C2EFA22895E2500015140 = { + isa = PBXGroup; + children = ( + 965C2F0522895E2500015140 /* Santander-Test */, + 965C2F1A22895E2800015140 /* Santander-TestTests */, + 965C2F2522895E2800015140 /* Santander-TestUITests */, + 965C2F0422895E2500015140 /* Products */, + C03B33535A1371552140448D /* Pods */, + 19CF96ADE36429C54AA25925 /* Frameworks */, + ); + sourceTree = ""; + }; + 965C2F0422895E2500015140 /* Products */ = { + isa = PBXGroup; + children = ( + 965C2F0322895E2500015140 /* Santander-Test.app */, + 965C2F1722895E2800015140 /* Santander-TestTests.xctest */, + 965C2F2222895E2800015140 /* Santander-TestUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 965C2F0522895E2500015140 /* Santander-Test */ = { + isa = PBXGroup; + children = ( + 965C2F5D228A8EDF00015140 /* GenericScenes */, + 965C2F4D228A848400015140 /* Scenes */, + 965C2F3C2289767900015140 /* Models */, + 965C2F342289668000015140 /* Supporting Files */, + 965C2F0622895E2500015140 /* AppDelegate.swift */, + 965C2F0F22895E2800015140 /* LaunchScreen.storyboard */, + 965C2F1222895E2800015140 /* Info.plist */, + 965C2FC9228E244900015140 /* Assets.xcassets */, + ); + path = "Santander-Test"; + sourceTree = ""; + }; + 965C2F1A22895E2800015140 /* Santander-TestTests */ = { + isa = PBXGroup; + children = ( + 965C2F1D22895E2800015140 /* Info.plist */, + 965C30332292F51100015140 /* FieldValidationTests.swift */, + 965C30372292F8E000015140 /* FloatExtensionsTests.swift */, + ); + path = "Santander-TestTests"; + sourceTree = ""; + }; + 965C2F2522895E2800015140 /* Santander-TestUITests */ = { + isa = PBXGroup; + children = ( + 965C2F2622895E2800015140 /* Santander_TestUITests.swift */, + 965C2F2822895E2800015140 /* Info.plist */, + ); + path = "Santander-TestUITests"; + sourceTree = ""; + }; + 965C2F342289668000015140 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 965C2F97228D5B8600015140 /* Fonts */, + 965C2F7D228C4B8600015140 /* API */, + 965C2F76228BBDA100015140 /* Extensions */, + 965C2F35228966AF00015140 /* Constants.swift */, + 965C2F81228CAB1000015140 /* FieldValidation.swift */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; + 965C2F3C2289767900015140 /* Models */ = { + isa = PBXGroup; + children = ( + 965C2F3D2289776F00015140 /* FormCell.swift */, + 965C2F3F22897AF800015140 /* Fund.swift */, + ); + path = Models; + sourceTree = ""; + }; + 965C2F4D228A848400015140 /* Scenes */ = { + isa = PBXGroup; + children = ( + 965C2FAC228E118700015140 /* FundDetail */, + 965C2F88228D4FD700015140 /* Success */, + 965C2F87228D494900015140 /* MainTabBar */, + 965C2F4E228A848E00015140 /* Form */, + ); + path = Scenes; + sourceTree = ""; + }; + 965C2F4E228A848E00015140 /* Form */ = { + isa = PBXGroup; + children = ( + 965C2F4F228A891900015140 /* FormPresenter.swift */, + 965C2F50228A891900015140 /* FormWorker.swift */, + 965C2F51228A891900015140 /* FormRouter.swift */, + 965C2F52228A891900015140 /* FormModels.swift */, + 965C2F53228A891900015140 /* FormViewController.swift */, + 965C2F54228A891900015140 /* FormInteractor.swift */, + 965C2F5B228A8CC200015140 /* Form.storyboard */, + ); + path = Form; + sourceTree = ""; + }; + 965C2F5D228A8EDF00015140 /* GenericScenes */ = { + isa = PBXGroup; + children = ( + 965C2FC6228E1B8F00015140 /* Fund */, + 965C2FC5228E1B3700015140 /* Form */, + ); + path = GenericScenes; + sourceTree = ""; + }; + 965C2F5E228A8EF200015140 /* Field */ = { + isa = PBXGroup; + children = ( + 965C2F63228A9B5800015140 /* FieldCell.xib */, + 965C2F65228A9DFC00015140 /* FieldCell.swift */, + ); + path = Field; + sourceTree = ""; + }; + 965C2F67228B6C4300015140 /* Text */ = { + isa = PBXGroup; + children = ( + 965C2F68228B6D6400015140 /* TextCell.xib */, + 965C2F6A228B6E1E00015140 /* TextCell.swift */, + ); + path = Text; + sourceTree = ""; + }; + 965C2F6C228B6E8C00015140 /* Checkbox */ = { + isa = PBXGroup; + children = ( + 965C2F6D228B6EB100015140 /* CheckboxCell.xib */, + 965C2F6F228B6EBE00015140 /* CheckboxCell.swift */, + 965C2FEE2292BA6C00015140 /* CheckBox.swift */, + ); + path = Checkbox; + sourceTree = ""; + }; + 965C2F71228B9A2500015140 /* Send */ = { + isa = PBXGroup; + children = ( + 965C2F72228B9A4C00015140 /* SendCell.swift */, + 965C2F74228B9A8000015140 /* SendCell.xib */, + 965C2FAA228DF72A00015140 /* SendButton.swift */, + ); + path = Send; + sourceTree = ""; + }; + 965C2F76228BBDA100015140 /* Extensions */ = { + isa = PBXGroup; + children = ( + 965C2F77228BBDB300015140 /* UIColorExtensions.swift */, + 965C2FE6228EE8CC00015140 /* UIViewControllerExtensions.swift */, + 965C2FEC2292614600015140 /* UIViewExtensions.swift */, + 965C302A2292ED7800015140 /* StringExtensions.swift */, + 965C30352292F81300015140 /* FloatExtensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 965C2F7D228C4B8600015140 /* API */ = { + isa = PBXGroup; + children = ( + 965C2F7E228C4B9200015140 /* FundsService */, + ); + path = API; + sourceTree = ""; + }; + 965C2F7E228C4B9200015140 /* FundsService */ = { + isa = PBXGroup; + children = ( + 965C2F372289681F00015140 /* FundsServiceApi.swift */, + 965C2F3A228974F500015140 /* FundsServiceApiManager.swift */, + ); + path = FundsService; + sourceTree = ""; + }; + 965C2F87228D494900015140 /* MainTabBar */ = { + isa = PBXGroup; + children = ( + 965C2F83228D409C00015140 /* MainTabBar.storyboard */, + 965C2F85228D474300015140 /* MainTabBar.swift */, + ); + path = MainTabBar; + sourceTree = ""; + }; + 965C2F88228D4FD700015140 /* Success */ = { + isa = PBXGroup; + children = ( + 965C2F8B228D502200015140 /* SuccessRouter.swift */, + 965C2F8D228D502200015140 /* SuccessViewController.swift */, + 965C2F95228D503300015140 /* Success.storyboard */, + ); + path = Success; + sourceTree = ""; + }; + 965C2F97228D5B8600015140 /* Fonts */ = { + isa = PBXGroup; + children = ( + 965C2F9D228D5BF000015140 /* DINEngschriftStd.otf */, + 965C2F99228D5BF000015140 /* DINMittelschriftStd.otf */, + 965C2F9F228D5BF000015140 /* DINNeuzeitGroteskStd-BdCond.otf */, + 965C2FA0228D5BF000015140 /* DINNeuzeitGroteskStd-Light.otf */, + 965C2F9A228D5BF000015140 /* DINPro-Black.otf */, + 965C2F9B228D5BF000015140 /* DINPro-Bold.otf */, + 965C2F98228D5BF000015140 /* DINPro-Light.otf */, + 965C2F9E228D5BF000015140 /* DINPro-Medium.otf */, + 965C2F9C228D5BF000015140 /* DINPro-Regular.otf */, + ); + path = Fonts; + sourceTree = ""; + }; + 965C2FAC228E118700015140 /* FundDetail */ = { + isa = PBXGroup; + children = ( + 965C2FB9228E128E00015140 /* FundDetailPresenter.swift */, + 965C2FBA228E128E00015140 /* FundDetailWorker.swift */, + 965C2FBB228E128E00015140 /* FundDetailRouter.swift */, + 965C2FBC228E128E00015140 /* FundDetailModels.swift */, + 965C2FBD228E128E00015140 /* FundDetailViewController.swift */, + 965C2FBE228E128E00015140 /* FundDetailInteractor.swift */, + 965C2FD5228E5C4100015140 /* FundDetail.storyboard */, + ); + path = FundDetail; + sourceTree = ""; + }; + 965C2FC5228E1B3700015140 /* Form */ = { + isa = PBXGroup; + children = ( + 965C2F71228B9A2500015140 /* Send */, + 965C2F6C228B6E8C00015140 /* Checkbox */, + 965C2F67228B6C4300015140 /* Text */, + 965C2F5E228A8EF200015140 /* Field */, + ); + path = Form; + sourceTree = ""; + }; + 965C2FC6228E1B8F00015140 /* Fund */ = { + isa = PBXGroup; + children = ( + 965C2FE1228EAC9300015140 /* DownInfo */, + 965C2FDC228EA9A900015140 /* Info */, + 965C2FD7228E9D1200015140 /* MoreInfo */, + 965C2FCE228E4BF800015140 /* Risk */, + 965C2FCD228E4BE600015140 /* Header */, + ); + path = Fund; + sourceTree = ""; + }; + 965C2FCD228E4BE600015140 /* Header */ = { + isa = PBXGroup; + children = ( + 965C2FC7228E1E0700015140 /* FundHeader.xib */, + 965C2FCB228E26A900015140 /* FundHeader.swift */, + ); + path = Header; + sourceTree = ""; + }; + 965C2FCE228E4BF800015140 /* Risk */ = { + isa = PBXGroup; + children = ( + 965C2FCF228E4C5A00015140 /* FundRisk.swift */, + 965C2FD1228E4D0D00015140 /* FundRisk.xib */, + ); + path = Risk; + sourceTree = ""; + }; + 965C2FD7228E9D1200015140 /* MoreInfo */ = { + isa = PBXGroup; + children = ( + 965C2FD8228E9D4600015140 /* FundMoreInfoCell.xib */, + 965C2FDA228E9D5A00015140 /* FundMoreInfoCell.swift */, + ); + path = MoreInfo; + sourceTree = ""; + }; + 965C2FDC228EA9A900015140 /* Info */ = { + isa = PBXGroup; + children = ( + 965C2FDD228EA9BD00015140 /* FundInfoCell.xib */, + 965C2FDF228EA9CB00015140 /* FundInfoCell.swift */, + ); + path = Info; + sourceTree = ""; + }; + 965C2FE1228EAC9300015140 /* DownInfo */ = { + isa = PBXGroup; + children = ( + 965C2FE2228EACC300015140 /* FundDownInfoCell.xib */, + 965C2FE4228EACD400015140 /* FundDownInfoCell.swift */, + ); + path = DownInfo; + sourceTree = ""; + }; + C03B33535A1371552140448D /* Pods */ = { + isa = PBXGroup; + children = ( + A512F1F10FEC63C067350139 /* Pods-Santander-Test.debug.xcconfig */, + 88AD971CB0FCD8230DDD854A /* Pods-Santander-Test.release.xcconfig */, + 39073D8A59D67805EB72C3BC /* Pods-Santander-TestTests.debug.xcconfig */, + 6C7F8C512B2B46A97F1AA7C2 /* Pods-Santander-TestTests.release.xcconfig */, + 944E346D8B737070B6AC9E04 /* Pods-Santander-TestUITests.debug.xcconfig */, + 21E66E0DEC791ED96A6B4107 /* Pods-Santander-TestUITests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 965C2F0222895E2500015140 /* Santander-Test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 965C2F2B22895E2800015140 /* Build configuration list for PBXNativeTarget "Santander-Test" */; + buildPhases = ( + A350BA9599603A07693E4C33 /* [CP] Check Pods Manifest.lock */, + 965C2EFF22895E2500015140 /* Sources */, + 965C2F0022895E2500015140 /* Frameworks */, + 965C2F0122895E2500015140 /* Resources */, + FFA4C426367581086C433180 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Santander-Test"; + productName = "Santander-Test"; + productReference = 965C2F0322895E2500015140 /* Santander-Test.app */; + productType = "com.apple.product-type.application"; + }; + 965C2F1622895E2800015140 /* Santander-TestTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 965C2F2E22895E2800015140 /* Build configuration list for PBXNativeTarget "Santander-TestTests" */; + buildPhases = ( + E0BD940A6A3E42133978040E /* [CP] Check Pods Manifest.lock */, + 965C2F1322895E2800015140 /* Sources */, + 965C2F1422895E2800015140 /* Frameworks */, + 965C2F1522895E2800015140 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 965C2F1922895E2800015140 /* PBXTargetDependency */, + ); + name = "Santander-TestTests"; + productName = "Santander-TestTests"; + productReference = 965C2F1722895E2800015140 /* Santander-TestTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 965C2F2122895E2800015140 /* Santander-TestUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 965C2F3122895E2800015140 /* Build configuration list for PBXNativeTarget "Santander-TestUITests" */; + buildPhases = ( + 70AA8CD8B4BB476952C6F02C /* [CP] Check Pods Manifest.lock */, + 965C2F1E22895E2800015140 /* Sources */, + 965C2F1F22895E2800015140 /* Frameworks */, + 965C2F2022895E2800015140 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 965C2F2422895E2800015140 /* PBXTargetDependency */, + ); + name = "Santander-TestUITests"; + productName = "Santander-TestUITests"; + productReference = 965C2F2222895E2800015140 /* Santander-TestUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 965C2EFB22895E2500015140 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1010; + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = "Matheus Ribeiro"; + TargetAttributes = { + 965C2F0222895E2500015140 = { + CreatedOnToolsVersion = 10.1; + }; + 965C2F1622895E2800015140 = { + CreatedOnToolsVersion = 10.1; + TestTargetID = 965C2F0222895E2500015140; + }; + 965C2F2122895E2800015140 = { + CreatedOnToolsVersion = 10.1; + TestTargetID = 965C2F0222895E2500015140; + }; + }; + }; + buildConfigurationList = 965C2EFE22895E2500015140 /* Build configuration list for PBXProject "Santander-Test" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 965C2EFA22895E2500015140; + productRefGroup = 965C2F0422895E2500015140 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 965C2F0222895E2500015140 /* Santander-Test */, + 965C2F1622895E2800015140 /* Santander-TestTests */, + 965C2F2122895E2800015140 /* Santander-TestUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 965C2F0122895E2500015140 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 965C2F75228B9A8000015140 /* SendCell.xib in Resources */, + 965C2F1122895E2800015140 /* LaunchScreen.storyboard in Resources */, + 965C2FA7228D5BF000015140 /* DINPro-Medium.otf in Resources */, + 965C2FCA228E244900015140 /* Assets.xcassets in Resources */, + 965C2FA1228D5BF000015140 /* DINPro-Light.otf in Resources */, + 965C2F96228D503300015140 /* Success.storyboard in Resources */, + 965C2F5C228A8CC200015140 /* Form.storyboard in Resources */, + 965C2FD2228E4D0D00015140 /* FundRisk.xib in Resources */, + 965C2FA3228D5BF000015140 /* DINPro-Black.otf in Resources */, + 965C2FD6228E5C4100015140 /* FundDetail.storyboard in Resources */, + 965C2FDE228EA9BD00015140 /* FundInfoCell.xib in Resources */, + 965C2FC8228E1E0700015140 /* FundHeader.xib in Resources */, + 965C2FD9228E9D4600015140 /* FundMoreInfoCell.xib in Resources */, + 965C2FA6228D5BF000015140 /* DINEngschriftStd.otf in Resources */, + 965C2FE3228EACC300015140 /* FundDownInfoCell.xib in Resources */, + 965C2F69228B6D6400015140 /* TextCell.xib in Resources */, + 965C2F84228D409C00015140 /* MainTabBar.storyboard in Resources */, + 965C2FA9228D5BF000015140 /* DINNeuzeitGroteskStd-Light.otf in Resources */, + 965C2FA2228D5BF000015140 /* DINMittelschriftStd.otf in Resources */, + 965C2FA8228D5BF000015140 /* DINNeuzeitGroteskStd-BdCond.otf in Resources */, + 965C2F6E228B6EB100015140 /* CheckboxCell.xib in Resources */, + 965C2FA4228D5BF000015140 /* DINPro-Bold.otf in Resources */, + 965C2FA5228D5BF000015140 /* DINPro-Regular.otf in Resources */, + 965C2F64228A9B5800015140 /* FieldCell.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 965C2F1522895E2800015140 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 965C2F2022895E2800015140 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 70AA8CD8B4BB476952C6F02C /* [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-Santander-TestUITests-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; + }; + A350BA9599603A07693E4C33 /* [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-Santander-Test-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; + }; + E0BD940A6A3E42133978040E /* [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-Santander-TestTests-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; + }; + FFA4C426367581086C433180 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/AMPopTip/AMPopTip.framework", + "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", + "${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework", + "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework", + "${BUILT_PRODUCTS_DIR}/Result/Result.framework", + "${BUILT_PRODUCTS_DIR}/StatusAlert/StatusAlert.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AMPopTip.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IQKeyboardManagerSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Result.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/StatusAlert.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Santander-Test/Pods-Santander-Test-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 965C2EFF22895E2500015140 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 965C2F3B228974F500015140 /* FundsServiceApiManager.swift in Sources */, + 965C2FED2292614600015140 /* UIViewExtensions.swift in Sources */, + 965C2F3E2289776F00015140 /* FormCell.swift in Sources */, + 965C2F86228D474300015140 /* MainTabBar.swift in Sources */, + 965C2FC0228E128E00015140 /* FundDetailWorker.swift in Sources */, + 965C2F70228B6EBE00015140 /* CheckboxCell.swift in Sources */, + 965C2FCC228E26A900015140 /* FundHeader.swift in Sources */, + 965C2FC1228E128E00015140 /* FundDetailRouter.swift in Sources */, + 965C2F73228B9A4C00015140 /* SendCell.swift in Sources */, + 965C2FE0228EA9CB00015140 /* FundInfoCell.swift in Sources */, + 965C2FBF228E128E00015140 /* FundDetailPresenter.swift in Sources */, + 965C2FC4228E128E00015140 /* FundDetailInteractor.swift in Sources */, + 965C2F55228A891900015140 /* FormPresenter.swift in Sources */, + 965C2F82228CAB1000015140 /* FieldValidation.swift in Sources */, + 965C2FC3228E128E00015140 /* FundDetailViewController.swift in Sources */, + 965C2F91228D502200015140 /* SuccessRouter.swift in Sources */, + 965C2F0722895E2500015140 /* AppDelegate.swift in Sources */, + 965C2F36228966AF00015140 /* Constants.swift in Sources */, + 965C2F59228A891900015140 /* FormViewController.swift in Sources */, + 965C2FE7228EE8CC00015140 /* UIViewControllerExtensions.swift in Sources */, + 965C302B2292ED7800015140 /* StringExtensions.swift in Sources */, + 965C2FEF2292BA6C00015140 /* CheckBox.swift in Sources */, + 965C2FD0228E4C5A00015140 /* FundRisk.swift in Sources */, + 965C2FAB228DF72A00015140 /* SendButton.swift in Sources */, + 965C2F78228BBDB300015140 /* UIColorExtensions.swift in Sources */, + 965C2F66228A9DFC00015140 /* FieldCell.swift in Sources */, + 965C30362292F81300015140 /* FloatExtensions.swift in Sources */, + 965C2FE5228EACD400015140 /* FundDownInfoCell.swift in Sources */, + 965C2FDB228E9D5A00015140 /* FundMoreInfoCell.swift in Sources */, + 965C2FC2228E128E00015140 /* FundDetailModels.swift in Sources */, + 965C2F57228A891900015140 /* FormRouter.swift in Sources */, + 965C2F5A228A891900015140 /* FormInteractor.swift in Sources */, + 965C2F58228A891900015140 /* FormModels.swift in Sources */, + 965C2F6B228B6E1E00015140 /* TextCell.swift in Sources */, + 965C2F382289681F00015140 /* FundsServiceApi.swift in Sources */, + 965C2F56228A891900015140 /* FormWorker.swift in Sources */, + 965C2F4022897AF800015140 /* Fund.swift in Sources */, + 965C2F93228D502200015140 /* SuccessViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 965C2F1322895E2800015140 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 965C30382292F8E000015140 /* FloatExtensionsTests.swift in Sources */, + 965C30342292F51100015140 /* FieldValidationTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 965C2F1E22895E2800015140 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 965C2F2722895E2800015140 /* Santander_TestUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 965C2F1922895E2800015140 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 965C2F0222895E2500015140 /* Santander-Test */; + targetProxy = 965C2F1822895E2800015140 /* PBXContainerItemProxy */; + }; + 965C2F2422895E2800015140 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 965C2F0222895E2500015140 /* Santander-Test */; + targetProxy = 965C2F2322895E2800015140 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 965C2F0F22895E2800015140 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 965C2F1022895E2800015140 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 965C2F2922895E2800015140 /* 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_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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + 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 = ( + "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 = 12.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 965C2F2A22895E2800015140 /* 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_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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 965C2F2C22895E2800015140 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A512F1F10FEC63C067350139 /* Pods-Santander-Test.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ZJ9A79V896; + INFOPLIST_FILE = "Santander-Test/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "br.com.matheus.Santander-Test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 965C2F2D22895E2800015140 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 88AD971CB0FCD8230DDD854A /* Pods-Santander-Test.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ZJ9A79V896; + INFOPLIST_FILE = "Santander-Test/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "br.com.matheus.Santander-Test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 965C2F2F22895E2800015140 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 39073D8A59D67805EB72C3BC /* Pods-Santander-TestTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ZJ9A79V896; + INFOPLIST_FILE = "Santander-TestTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "br.com.matheus.Santander-TestTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Santander-Test.app/Santander-Test"; + }; + name = Debug; + }; + 965C2F3022895E2800015140 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6C7F8C512B2B46A97F1AA7C2 /* Pods-Santander-TestTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ZJ9A79V896; + INFOPLIST_FILE = "Santander-TestTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "br.com.matheus.Santander-TestTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Santander-Test.app/Santander-Test"; + }; + name = Release; + }; + 965C2F3222895E2800015140 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 944E346D8B737070B6AC9E04 /* Pods-Santander-TestUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ZJ9A79V896; + INFOPLIST_FILE = "Santander-TestUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "br.com.matheus.Santander-TestUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Santander-Test"; + }; + name = Debug; + }; + 965C2F3322895E2800015140 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 21E66E0DEC791ED96A6B4107 /* Pods-Santander-TestUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ZJ9A79V896; + INFOPLIST_FILE = "Santander-TestUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "br.com.matheus.Santander-TestUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Santander-Test"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 965C2EFE22895E2500015140 /* Build configuration list for PBXProject "Santander-Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 965C2F2922895E2800015140 /* Debug */, + 965C2F2A22895E2800015140 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 965C2F2B22895E2800015140 /* Build configuration list for PBXNativeTarget "Santander-Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 965C2F2C22895E2800015140 /* Debug */, + 965C2F2D22895E2800015140 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 965C2F2E22895E2800015140 /* Build configuration list for PBXNativeTarget "Santander-TestTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 965C2F2F22895E2800015140 /* Debug */, + 965C2F3022895E2800015140 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 965C2F3122895E2800015140 /* Build configuration list for PBXNativeTarget "Santander-TestUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 965C2F3222895E2800015140 /* Debug */, + 965C2F3322895E2800015140 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 965C2EFB22895E2500015140 /* Project object */; +} diff --git a/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..f583a102 --- /dev/null +++ b/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/UserInterfaceState.xcuserstate b/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..e4a48e94 Binary files /dev/null and b/Santander-Test/Santander-Test.xcodeproj/project.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Santander-Test/Santander-Test.xcodeproj/xcuserdata/matheusribeiro.xcuserdatad/xcschemes/xcschememanagement.plist b/Santander-Test/Santander-Test.xcodeproj/xcuserdata/matheusribeiro.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..473dbb87 --- /dev/null +++ b/Santander-Test/Santander-Test.xcodeproj/xcuserdata/matheusribeiro.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Santander-Test.xcscheme_^#shared#^_ + + orderHint + 9 + + + + diff --git a/Santander-Test/Santander-Test.xcworkspace/contents.xcworkspacedata b/Santander-Test/Santander-Test.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..501042af --- /dev/null +++ b/Santander-Test/Santander-Test.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Santander-Test/Santander-Test.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Santander-Test/Santander-Test.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Santander-Test/Santander-Test.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Santander-Test/Santander-Test.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/UserInterfaceState.xcuserstate b/Santander-Test/Santander-Test.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..8bdaaeeb Binary files /dev/null and b/Santander-Test/Santander-Test.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Santander-Test/Santander-Test.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Santander-Test/Santander-Test.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 00000000..ed9a9b4d --- /dev/null +++ b/Santander-Test/Santander-Test.xcworkspace/xcuserdata/matheusribeiro.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/Santander-Test/Santander-Test/AppDelegate.swift b/Santander-Test/Santander-Test/AppDelegate.swift new file mode 100644 index 00000000..cbd214ec --- /dev/null +++ b/Santander-Test/Santander-Test/AppDelegate.swift @@ -0,0 +1,49 @@ +// +// AppDelegate.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit +import IQKeyboardManagerSwift + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + IQKeyboardManager.shared.enable = true + + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/Santander-Test/Santander-Test/Assets.xcassets/AppIcon.appiconset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/checkbox_selected.imageset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_selected.imageset/Contents.json new file mode 100644 index 00000000..74e660df --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_selected.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "checkbox_selected.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/checkbox_selected.imageset/checkbox_selected.png b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_selected.imageset/checkbox_selected.png new file mode 100644 index 00000000..18ef1968 Binary files /dev/null and b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_selected.imageset/checkbox_selected.png differ diff --git a/Santander-Test/Santander-Test/Assets.xcassets/checkbox_unselected.imageset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_unselected.imageset/Contents.json new file mode 100644 index 00000000..2927f4a4 --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_unselected.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "checkbox_unselected.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/checkbox_unselected.imageset/checkbox_unselected.png b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_unselected.imageset/checkbox_unselected.png new file mode 100644 index 00000000..66242cdb Binary files /dev/null and b/Santander-Test/Santander-Test/Assets.xcassets/checkbox_unselected.imageset/checkbox_unselected.png differ diff --git a/Santander-Test/Santander-Test/Assets.xcassets/download_arrow.imageset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/download_arrow.imageset/Contents.json new file mode 100644 index 00000000..df7b739b --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/download_arrow.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "download_arrow.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/download_arrow.imageset/download_arrow.png b/Santander-Test/Santander-Test/Assets.xcassets/download_arrow.imageset/download_arrow.png new file mode 100644 index 00000000..6d74ebdb Binary files /dev/null and b/Santander-Test/Santander-Test/Assets.xcassets/download_arrow.imageset/download_arrow.png differ diff --git a/Santander-Test/Santander-Test/Assets.xcassets/risk_pointer.imageset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/risk_pointer.imageset/Contents.json new file mode 100644 index 00000000..fb9b19f9 --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/risk_pointer.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "risk_pointer.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/risk_pointer.imageset/risk_pointer.png b/Santander-Test/Santander-Test/Assets.xcassets/risk_pointer.imageset/risk_pointer.png new file mode 100644 index 00000000..f2a74051 Binary files /dev/null and b/Santander-Test/Santander-Test/Assets.xcassets/risk_pointer.imageset/risk_pointer.png differ diff --git a/Santander-Test/Santander-Test/Assets.xcassets/separator.imageset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/separator.imageset/Contents.json new file mode 100644 index 00000000..77ecf613 --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/separator.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "separator.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/separator.imageset/separator.png b/Santander-Test/Santander-Test/Assets.xcassets/separator.imageset/separator.png new file mode 100644 index 00000000..bd4a293f Binary files /dev/null and b/Santander-Test/Santander-Test/Assets.xcassets/separator.imageset/separator.png differ diff --git a/Santander-Test/Santander-Test/Assets.xcassets/share.imageset/Contents.json b/Santander-Test/Santander-Test/Assets.xcassets/share.imageset/Contents.json new file mode 100644 index 00000000..bfbe1c11 --- /dev/null +++ b/Santander-Test/Santander-Test/Assets.xcassets/share.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "share.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Santander-Test/Santander-Test/Assets.xcassets/share.imageset/share.png b/Santander-Test/Santander-Test/Assets.xcassets/share.imageset/share.png new file mode 100644 index 00000000..0aa9fc9a Binary files /dev/null and b/Santander-Test/Santander-Test/Assets.xcassets/share.imageset/share.png differ diff --git a/Santander-Test/Santander-Test/Base.lproj/LaunchScreen.storyboard b/Santander-Test/Santander-Test/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/Santander-Test/Santander-Test/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckBox.swift b/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckBox.swift new file mode 100644 index 00000000..76c54dd4 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckBox.swift @@ -0,0 +1,46 @@ +// +// Checkbox.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 20/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +protocol CheckBoxDelegate: class { + func valueDidChange(value: Bool) +} + +class CheckBox: UIButton { + + var isChecked: Bool = true { + didSet{ + updateCheckBoxImage() + } + } + weak var delegate: CheckBoxDelegate? + + override func awakeFromNib() { + super.awakeFromNib() + setupLayout() + } + + private func setupLayout() { + backgroundColor = .clear + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + isChecked.toggle() + delegate?.valueDidChange(value: isChecked) + } + + private func updateCheckBoxImage() { + if isChecked { + setImage(UIImage(named: "checkbox_selected"), for: .normal) + } else { + setImage(UIImage(named: "checkbox_unselected"), for: .normal) + } + } + +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckboxCell.swift b/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckboxCell.swift new file mode 100644 index 00000000..f5e1a05a --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckboxCell.swift @@ -0,0 +1,68 @@ +// +// CheckboxCell.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +protocol CheckboxCellDelegate: class { + func didChoose(value: Bool, _ cell: CheckboxCell) +} + +class CheckboxCell: UITableViewCell { + + @IBOutlet weak var checkboxOuterView: CheckBox! + @IBOutlet weak var label: UILabel! + + @IBOutlet weak var topConstraint: NSLayoutConstraint! + + static let reuseIdentifier = "CheckboxCell" + + weak var delegate: CheckboxCellDelegate? + + var viewModel: CheckboxCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + + configureLayout() + setupCheckboxDelegate() + } + + private func configureLayout() { + label.textColor = UIColor.getColorDarkGray + } + + private func setupCheckboxDelegate() { + checkboxOuterView.delegate = self + } + + private func didSetViewModel() { + checkboxOuterView.isChecked = false + guard let viewModel = viewModel else { return } + label.text = viewModel.message ?? "-" + topConstraint.constant = CGFloat(viewModel.topSpace ?? 8) + } +} + +extension CheckboxCell: CheckBoxDelegate { + func valueDidChange(value: Bool) { + delegate?.didChoose(value: value, self) + } + +} + +extension CheckboxCell { + struct ViewModel { + let message: String? + let topSpace: Int? + let show: Int? + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckboxCell.xib b/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckboxCell.xib new file mode 100644 index 00000000..3883eb59 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Checkbox/CheckboxCell.xib @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + DINPro-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Field/FieldCell.swift b/Santander-Test/Santander-Test/GenericScenes/Form/Field/FieldCell.swift new file mode 100644 index 00000000..8e9db0ea --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Field/FieldCell.swift @@ -0,0 +1,77 @@ +// +// FieldCell.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +protocol FieldCellDelegate: class { + func textDidChange(for indexPath: IndexPath, text: String, typeField: TypeField) +} + +class FieldCell: UITableViewCell { + + @IBOutlet weak var label: UILabel! + @IBOutlet weak var textField: UITextField! + @IBOutlet weak var lineView: UIView! + + @IBOutlet weak var topConstraint: NSLayoutConstraint! + + static let reuseIdentifier = "FieldCell" + + weak var delegate: FieldCellDelegate? + + var viewModel: FieldCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + configureLayout() + } + + private func configureLayout() { + label.textColor = UIColor.getColorDarkGray + lineView.backgroundColor = UIColor.getColorLightGray + } + + private func didSetViewModel() { + guard let viewModel = viewModel else { return } + label.text = viewModel.message ?? "-" + topConstraint.constant = CGFloat(viewModel.topSpace ?? 8) + if viewModel.typeField == .telNumber { + textField.keyboardType = .decimalPad + } + } + + @IBAction func textFieldDidChange(_ sender: Any) { + guard + let indexPath = viewModel?.indexPath, + let text = textField.text, + let typeField = viewModel?.typeField + else { return } + + if typeField == .telNumber { + let convertedText = text.convertToPhoneNumberFormat() ?? text + textField.text = convertedText + delegate?.textDidChange(for: indexPath, text: convertedText, typeField: typeField) + } else { + delegate?.textDidChange(for: indexPath, text: text, typeField: typeField) + } + } + +} + +extension FieldCell { + struct ViewModel { + let message: String? + let topSpace: Int? + let typeField: TypeField? + let indexPath: IndexPath + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Field/FieldCell.xib b/Santander-Test/Santander-Test/GenericScenes/Form/Field/FieldCell.xib new file mode 100644 index 00000000..8f01ad2c --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Field/FieldCell.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + DINPro-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendButton.swift b/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendButton.swift new file mode 100644 index 00000000..9d034f49 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendButton.swift @@ -0,0 +1,53 @@ +// +// SendButton.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +protocol SendButtonDelegate: class { + func buttonPressed() +} + +class SendButton: UIButton { + + weak var delegate: SendButtonDelegate? + + private var originalWidth: CGFloat = 0 + private var originalHeight: CGFloat = 0 + + override func awakeFromNib() { + super.awakeFromNib() + configureLayout() + } + + private func configureLayout() { + backgroundColor = UIColor.getColorDarkRed + layer.cornerRadius = 25 + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + + originalWidth = frame.size.width + originalHeight = frame.size.height + + let buttonCenter = center + frame.size = CGSize(width: originalWidth * 0.9, height: originalHeight * 0.9) + center = buttonCenter + layer.cornerRadius = 22.5 + backgroundColor = UIColor.getColorLightRed + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + + let buttonCenter = center + frame.size = CGSize(width: originalWidth, height: originalHeight) + center = buttonCenter + layer.cornerRadius = 25 + backgroundColor = UIColor.getColorDarkRed + delegate?.buttonPressed() + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendCell.swift b/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendCell.swift new file mode 100644 index 00000000..4a69a02e --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendCell.swift @@ -0,0 +1,63 @@ +// +// SendCell.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +protocol SendCellDelegate: class { + func buttonPressed() +} + +class SendCell: UITableViewCell { + + @IBOutlet weak var button: SendButton! + + @IBOutlet weak var topConstraint: NSLayoutConstraint! + @IBOutlet weak var bottomConstraint: NSLayoutConstraint! + + static let reuseIdentifier = "SendCell" + + weak var delegate: SendCellDelegate? + + var viewModel: SendCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + button.delegate = self + } + + private func didSetViewModel() { + guard let viewModel = viewModel else { return } + button.setTitle(viewModel.message ?? "-", for: .normal) + topConstraint.constant = CGFloat(viewModel.topSpace ?? 8) + bottomConstraint.constant = CGFloat(viewModel.bottomSpace) + } +} + +extension SendCell: SendButtonDelegate { + func buttonPressed() { + delegate?.buttonPressed() + } +} + +extension SendCell { + struct ViewModel { + let message: String? + let topSpace: Int? + let bottomSpace: Int + + init(message: String?, topSpace: Int?, bottomSpace: Int = 16) { + self.message = message + self.topSpace = topSpace + self.bottomSpace = bottomSpace + } + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendCell.xib b/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendCell.xib new file mode 100644 index 00000000..65933b68 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Send/SendCell.xib @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + DINPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Text/TextCell.swift b/Santander-Test/Santander-Test/GenericScenes/Form/Text/TextCell.swift new file mode 100644 index 00000000..2018d44d --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Text/TextCell.swift @@ -0,0 +1,42 @@ +// +// TextCell.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +class TextCell: UITableViewCell { + + @IBOutlet weak var label: UILabel! + + @IBOutlet weak var topConstraint: NSLayoutConstraint! + + static let reuseIdentifier = "TextCell" + + var viewModel: TextCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + + } + + private func didSetViewModel() { + guard let viewModel = viewModel else { return } + label.text = viewModel.message ?? "-" + topConstraint.constant = CGFloat(viewModel.topSpace ?? 8) + } +} + +extension TextCell { + struct ViewModel { + let message: String? + let topSpace: Int? + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Form/Text/TextCell.xib b/Santander-Test/Santander-Test/GenericScenes/Form/Text/TextCell.xib new file mode 100644 index 00000000..6111df2a --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Form/Text/TextCell.xib @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + DINPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/DownInfo/FundDownInfoCell.swift b/Santander-Test/Santander-Test/GenericScenes/Fund/DownInfo/FundDownInfoCell.swift new file mode 100644 index 00000000..336631ef --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/DownInfo/FundDownInfoCell.swift @@ -0,0 +1,55 @@ +// +// FundDownInfoCell.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 17/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +protocol FundDownInfoCellDelegate: class { + func buttonPressed() +} + +class FundDownInfoCell: UITableViewCell { + + @IBOutlet weak var downInfoNameLabel: UILabel! + @IBOutlet weak var button: UIButton! + + static let reuseIdentifier = "FundDownInfoCell" + + weak var delegate: FundDownInfoCellDelegate? + + var viewModel: FundDownInfoCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + + configureLayout() + } + + private func configureLayout() { + downInfoNameLabel.textColor = UIColor.getColorDarkGray + button.tintColor = UIColor.getColorDarkRed + } + + private func didSetViewModel() { + guard let viewModel = viewModel else { return } + downInfoNameLabel.text = viewModel.downInfoName ?? "-" + } + + @IBAction func downloadButton(_ sender: Any) { + delegate?.buttonPressed() + } +} + +extension FundDownInfoCell { + struct ViewModel { + let downInfoName: String? + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/DownInfo/FundDownInfoCell.xib b/Santander-Test/Santander-Test/GenericScenes/Fund/DownInfo/FundDownInfoCell.xib new file mode 100644 index 00000000..3dfd0bc6 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/DownInfo/FundDownInfoCell.xib @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + DINPro-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/Header/FundHeader.swift b/Santander-Test/Santander-Test/GenericScenes/Fund/Header/FundHeader.swift new file mode 100644 index 00000000..64f54855 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/Header/FundHeader.swift @@ -0,0 +1,55 @@ +// +// FundHeader.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +class FundHeader: UITableViewCell { + + @IBOutlet weak var title: UILabel! + @IBOutlet weak var fundName: UILabel! + @IBOutlet weak var whatIs: UILabel! + @IBOutlet weak var definition: UILabel! + + static let reuseIdentifier = "FundHeader" + + var viewModel: FundHeader.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + configureLayout() + } + + private func configureLayout() { + title.textColor = UIColor.getColorDarkGray + whatIs.textColor = UIColor.getColorDarkGray + definition.textColor = UIColor.getColorDarkGray + } + + private func didSetViewModel() { + guard let viewModel = viewModel else { return } + + title.text = viewModel.title + fundName.text = viewModel.fundName + whatIs.text = viewModel.whatIs + definition.text = viewModel.definition + } + +} + +extension FundHeader { + struct ViewModel { + var title: String + var fundName: String + var whatIs: String + var definition: String + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/Header/FundHeader.xib b/Santander-Test/Santander-Test/GenericScenes/Fund/Header/FundHeader.xib new file mode 100644 index 00000000..433bdd54 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/Header/FundHeader.xib @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + DINPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/Info/FundInfoCell.swift b/Santander-Test/Santander-Test/GenericScenes/Fund/Info/FundInfoCell.swift new file mode 100644 index 00000000..bfa7cac3 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/Info/FundInfoCell.swift @@ -0,0 +1,46 @@ +// +// InfoCel.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 17/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +class FundInfoCell: UITableViewCell { + + @IBOutlet weak var infoName: UILabel! + @IBOutlet weak var infoData: UILabel! + + static let reuseIdentifier = "FundInfoCell" + + var viewModel: FundInfoCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + + configureLayout() + } + + private func configureLayout() { + infoName.textColor = UIColor.getColorDarkGray + } + + private func didSetViewModel() { + guard let viewModel = viewModel else { return } + infoName.text = viewModel.infoName ?? "-" + infoData.text = viewModel.infoData ?? "-" + } +} + +extension FundInfoCell { + struct ViewModel { + let infoName: String? + let infoData: String? + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/Info/FundInfoCell.xib b/Santander-Test/Santander-Test/GenericScenes/Fund/Info/FundInfoCell.xib new file mode 100644 index 00000000..98e749d0 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/Info/FundInfoCell.xib @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + DINPro-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/MoreInfo/FundMoreInfoCell.swift b/Santander-Test/Santander-Test/GenericScenes/Fund/MoreInfo/FundMoreInfoCell.swift new file mode 100644 index 00000000..42099459 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/MoreInfo/FundMoreInfoCell.swift @@ -0,0 +1,124 @@ +// +// MoreInfo.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 17/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +class FundMoreInfoCell: UITableViewCell { + + @IBOutlet weak var moreInfoTitleLabel: UILabel! + @IBOutlet weak var monthLabel: UILabel! + @IBOutlet weak var yearLabel: UILabel! + @IBOutlet weak var twelveMonthsLabel: UILabel! + + @IBOutlet weak var FundLabel: UILabel! + @IBOutlet weak var cdiLabel: UILabel! + + @IBOutlet weak var monthFundLabel: UILabel! + @IBOutlet weak var yearFundLabel: UILabel! + @IBOutlet weak var twelveMonthsFundLabel: UILabel! + @IBOutlet weak var monthCdiLabel: UILabel! + @IBOutlet weak var yearCdiLabel: UILabel! + @IBOutlet weak var twelveMonthsCdiLabel: UILabel! + + @IBOutlet weak var lineView: UIView! + + static let reuseIdentifier = "FundMoreInfoCell" + + var viewModel: FundMoreInfoCell.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + + configureLayout() + } + + private func configureLayout() { + moreInfoTitleLabel.textColor = UIColor.getColorDarkGray + monthLabel.textColor = UIColor.getColorDarkGray + yearLabel.textColor = UIColor.getColorDarkGray + twelveMonthsLabel.textColor = UIColor.getColorDarkGray + FundLabel.textColor = UIColor.getColorDarkGray + cdiLabel.textColor = UIColor.getColorDarkGray + lineView.backgroundColor = UIColor.getColorLightGray + } + + private func didSetViewModel() { + guard let fundMoreInfo = viewModel?.fundMoreInfo else { return } + + let monthFund: String = { + guard + let monthFund = fundMoreInfo.month?.fund, + let monthFundFormatted = monthFund.getBrazilianPercentFormat() else { + return "-" + } + return monthFundFormatted + }() + + let yearFund: String = { + guard + let yearFund = fundMoreInfo.year?.fund, + let yearFundFormatted = yearFund.getBrazilianPercentFormat() else { + return "-" + } + return yearFundFormatted + }() + + let twelveMonthsFund: String = { + guard + let twelveMonthsFund = fundMoreInfo.twelveMonths?.fund, + let twelveMonthsFundFormatted = twelveMonthsFund.getBrazilianPercentFormat() else { + return "-" + } + return twelveMonthsFundFormatted + }() + + let monthCdi: String = { + guard + let monthCdi = fundMoreInfo.month?.CDI, + let monthCdiFormatted = monthCdi.getBrazilianPercentFormat() else { + return "-" + } + return monthCdiFormatted + }() + + let yearCdi: String = { + guard + let yearCdi = fundMoreInfo.year?.CDI, + let yearCdiFormatted = yearCdi.getBrazilianPercentFormat() else { + return "-" + } + return yearCdiFormatted + }() + + let twelveMonthsCdi: String = { + guard + let twelveMonthsCdi = fundMoreInfo.twelveMonths?.CDI, + let twelveMonthsCdiFormatted = twelveMonthsCdi.getBrazilianPercentFormat() else { + return "-" + } + return twelveMonthsCdiFormatted + }() + + monthFundLabel.text = monthFund + yearFundLabel.text = yearFund + twelveMonthsFundLabel.text = twelveMonthsFund + monthCdiLabel.text = monthCdi + yearCdiLabel.text = yearCdi + twelveMonthsCdiLabel.text = twelveMonthsCdi + } +} + +extension FundMoreInfoCell { + struct ViewModel { + let fundMoreInfo: FundMoreInfo? + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/MoreInfo/FundMoreInfoCell.xib b/Santander-Test/Santander-Test/GenericScenes/Fund/MoreInfo/FundMoreInfoCell.xib new file mode 100644 index 00000000..055b2321 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/MoreInfo/FundMoreInfoCell.xib @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + DINPro-Medium + + + DINPro-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/Risk/FundRisk.swift b/Santander-Test/Santander-Test/GenericScenes/Fund/Risk/FundRisk.swift new file mode 100644 index 00000000..5dee1e2a --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/Risk/FundRisk.swift @@ -0,0 +1,98 @@ +// +// FundRisk.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +class FundRisk: UITableViewCell { + + @IBOutlet weak var label: UILabel! + + @IBOutlet weak var lightGreenView: UIView! + @IBOutlet weak var darkGreenView: UIView! + @IBOutlet weak var darkYellowView: UIView! + @IBOutlet weak var darkOrangeView: UIView! + @IBOutlet weak var darkRedView: UIView! + + @IBOutlet weak var riskPointer: UIImageView! + @IBOutlet weak var riskPointerCenterConstraint: NSLayoutConstraint! + + static let reuseIdentifier = "FundRisk" + + var viewModel: FundRisk.ViewModel? { + didSet { + didSetViewModel() + } + } + + override func awakeFromNib() { + super.awakeFromNib() + configureLayout() + } + + private func configureLayout() { + label.textColor = UIColor.getColorDarkGray + lightGreenView.backgroundColor = UIColor.getColorRiskLightGreen + darkGreenView.backgroundColor = UIColor.getColorRiskDarkGreen + darkYellowView.backgroundColor = UIColor.getColorRiskDarkYellow + darkOrangeView.backgroundColor = UIColor.getColorRiskDarkOrange + darkRedView.backgroundColor = UIColor.getColorRiskDarkRed + lightGreenView.roundCorners(corners: [.bottomLeft, .topLeft], radius: 3.0) + darkRedView.roundCorners(corners: [.bottomRight, .topRight], radius: 3.0) + } + + private func didSetViewModel() { + + selectRiskView() + } + + private func selectRiskView() { + guard + let viewModelRisk = viewModel?.risk, + 1...5 ~= viewModelRisk + else { return } + + switch viewModelRisk { + case 1: + highlightRiskView(riskView: lightGreenView) + setupRiskPointer(aboveRiskView: lightGreenView) + case 2: + highlightRiskView(riskView: darkGreenView) + setupRiskPointer(aboveRiskView: darkGreenView) + case 3: + highlightRiskView(riskView: darkYellowView) + setupRiskPointer(aboveRiskView: darkYellowView) + case 4: + highlightRiskView(riskView: darkOrangeView) + setupRiskPointer(aboveRiskView: darkOrangeView) + case 5: + highlightRiskView(riskView: darkRedView) + setupRiskPointer(aboveRiskView: darkRedView) + default: + return + } + + } + + private func highlightRiskView(riskView: UIView) { + riskView.constraints.forEach { (constraint) in + if constraint.firstAttribute == .height { + constraint.constant = 10 + } + } + } + + private func setupRiskPointer(aboveRiskView riskView: UIView) { + riskPointerCenterConstraint.constant = riskView.frame.midX - darkYellowView.frame.midX + } +} + +extension FundRisk { + struct ViewModel { + let risk: Int? + } +} diff --git a/Santander-Test/Santander-Test/GenericScenes/Fund/Risk/FundRisk.xib b/Santander-Test/Santander-Test/GenericScenes/Fund/Risk/FundRisk.xib new file mode 100644 index 00000000..006878a6 --- /dev/null +++ b/Santander-Test/Santander-Test/GenericScenes/Fund/Risk/FundRisk.xib @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + DINPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/Info.plist b/Santander-Test/Santander-Test/Info.plist new file mode 100644 index 00000000..bc515eb8 --- /dev/null +++ b/Santander-Test/Santander-Test/Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIAppFonts + + DINPro-Medium.otf + DINPro-Regular.otf + DINPro-Light.otf + DINPro-Bold.otf + DINPro-Black.otf + DINNeuzeitGroteskStd-Light.otf + DINNeuzeitGroteskStd-BdCond.otf + DINMittelschriftStd.otf + DINEngschriftStd.otf + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + MainTabBar + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Santander-Test/Santander-Test/Models/FormCell.swift b/Santander-Test/Santander-Test/Models/FormCell.swift new file mode 100644 index 00000000..6baf4600 --- /dev/null +++ b/Santander-Test/Santander-Test/Models/FormCell.swift @@ -0,0 +1,70 @@ +// +// FormCell.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Foundation + +enum TypeField: Int { + case text = 1 + case telNumber = 2 + case email = 3 +} + +enum Type: Int { + case field = 1 + case text = 2 + case image = 3 + case checkbox = 4 + case send = 5 +} + +struct FormCell { + let id: Int? + let type: Type? + let message: String? + let typefield: TypeField? + let hidden: Bool? + let topSpacing: Int? + let show: Int? + let required: Bool? +} + +struct FormCellsList: Decodable { + let cells: [FormCellModel] +} + +struct FormCellModel: Decodable { + let id: Int? + let type: Int? + let message: String? + let typefield: AnyDecodable? + let hidden: Bool? + let topSpacing: Int? + let show: Int? + let required: Bool? +} + +public struct AnyDecodable: Decodable { + + let value: Any + + public init(_ value: T?) { + self.value = value ?? () + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + if let string = try? container.decode(String.self) { + self.init(string) + } else if let int = try? container.decode(Int.self) { + self.init(int) + } else { + self.init(()) + } + } +} diff --git a/Santander-Test/Santander-Test/Models/Fund.swift b/Santander-Test/Santander-Test/Models/Fund.swift new file mode 100644 index 00000000..65c716cf --- /dev/null +++ b/Santander-Test/Santander-Test/Models/Fund.swift @@ -0,0 +1,49 @@ +// +// Fund.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Foundation + +struct FundsList: Decodable { + let screen: Fund? +} + + +struct Fund: Decodable { + let title: String? + let fundName: String? + let whatIs: String? + let definition: String? + let riskTitle: String? + let risk: Int? + let infoTitle: String? + let moreInfo: FundMoreInfo? + let info: [FundInfo]? + let downInfo: [FundInfo]? +} + +struct FundMoreInfo: Decodable { + let month: FundMoreInfoPeriod? + let year: FundMoreInfoPeriod? + let twelveMonths: FundMoreInfoPeriod? + + enum CodingKeys: String, CodingKey { + case month + case year + case twelveMonths = "12months" + } +} + +struct FundMoreInfoPeriod: Decodable { + let fund: Float? + let CDI: Float? +} + +struct FundInfo: Decodable { + let name: String? + let data: String? +} diff --git a/Santander-Test/Santander-Test/Scenes/Form/Form.storyboard b/Santander-Test/Santander-Test/Scenes/Form/Form.storyboard new file mode 100644 index 00000000..8acdad01 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/Form.storyboard @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/Scenes/Form/FormInteractor.swift b/Santander-Test/Santander-Test/Scenes/Form/FormInteractor.swift new file mode 100644 index 00000000..ae0b8547 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/FormInteractor.swift @@ -0,0 +1,71 @@ +// +// FormInteractor.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +protocol FormBusinessLogic { + func getFormCells(request: Form.GetFormCells.Request) + func validateField(request: Form.FieldValidation.Request) + func validateAllFields(request: Form.AllFieldsValidation.Request) +} + +protocol FormDataStore { + //var name: String { get set } +} + +class FormInteractor: FormBusinessLogic, FormDataStore { + var presenter: FormPresentationLogic? + var worker: FormWorker? + //var name: String = "" + + func getFormCells(request: Form.GetFormCells.Request) { + worker = FormWorker() + worker?.getFormCells(completion: { [unowned self] (formCells, error) in + guard let formCells = formCells else { + guard let error = error else { + let domain = "Ocorreu um erro inesperado!" + let error = NSError(domain: domain, code: 200, userInfo: nil) + let response = Form.FormError.Response(error: error, errorType: .missingCells) + self.presenter?.presentError(response: response) + return + } + let response = Form.FormError.Response(error: error as NSError, errorType: .getFormCells) + self.presenter?.presentError(response: response) + return + } + let response = Form.GetFormCells.Response(formCells: formCells) + self.presenter?.presentFormCells(response: response) + }) + } + + func validateField(request: Form.FieldValidation.Request) { + worker = FormWorker() + let isValid = worker?.validateField(text: request.text, typeField: request.typeField) ?? false + let response = Form.FieldValidation.Response(indexPath: request.indexPath, isValid: isValid) + presenter?.presentFieldValidation(response: response) + } + + func validateAllFields(request: Form.AllFieldsValidation.Request) { + worker = FormWorker() + let tableView = request.tableView + worker?.validateAllFields( + tableView: tableView, + completion: { (isValid, indexPath, message) in + let response = Form.AllFieldsValidation.Response( + isValid: isValid, + indexPath: indexPath, + message: message + ) + presenter?.presentAllFieldsValidation(response: response) + }) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Form/FormModels.swift b/Santander-Test/Santander-Test/Scenes/Form/FormModels.swift new file mode 100644 index 00000000..6a75552f --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/FormModels.swift @@ -0,0 +1,86 @@ +// +// FormModels.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +enum Form { + + enum ErrorType { + case getFormCells + case missingCells + } + + enum GetFormCells { + struct Request {} + struct Response { + let formCells: [FormCell] + } + struct ViewModel { + struct DisplayViewModel { + let id: Int + let type: Type + let message: String? + let typeField: TypeField? + var hidden: Bool + let topSpacing: Int + let show: Int? + let required: Bool + } + + var displayedFormCells: [DisplayViewModel] = [] + } + } + + enum FormError { + struct Request {} + struct Response { + let error: NSError + let errorType: ErrorType + } + struct ViewModel { + let message: String + let errorType: ErrorType + } + } + + enum FieldValidation { + struct Request { + let text: String + let indexPath: IndexPath + let typeField: TypeField + } + struct Response { + let indexPath: IndexPath + let isValid: Bool + } + struct ViewModel { + let indexPath: IndexPath + let isValid: Bool + } + } + + enum AllFieldsValidation { + struct Request { + let tableView: UITableView + } + struct Response { + let isValid: Bool + let indexPath: IndexPath? + let message: String? + } + struct ViewModel { + let isValid: Bool + let indexPath: IndexPath? + let message: String? + } + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Form/FormPresenter.swift b/Santander-Test/Santander-Test/Scenes/Form/FormPresenter.swift new file mode 100644 index 00000000..b7df5724 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/FormPresenter.swift @@ -0,0 +1,89 @@ +// +// FormPresenter.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +protocol FormPresentationLogic { + func presentFormCells(response: Form.GetFormCells.Response) + func presentError(response: Form.FormError.Response) + func presentFieldValidation(response: Form.FieldValidation.Response) + func presentAllFieldsValidation(response: Form.AllFieldsValidation.Response) +} + +class FormPresenter: FormPresentationLogic { + weak var viewController: FormDisplayLogic? + + func presentFormCells(response: Form.GetFormCells.Response) { + + var displayedCells: [Form.GetFormCells.ViewModel.DisplayViewModel] = [] + for formCell in response.formCells { + + guard + let type = formCell.type, + let id = formCell.id + else { + continue + } + let message = formCell.message + let typeField = formCell.typefield + let hidden = formCell.hidden ?? false + let topSpacing = formCell.topSpacing ?? 20 + let show = formCell.show + let required = formCell.required ?? false + + let displayedCell = Form.GetFormCells.ViewModel.DisplayViewModel( + id: id, + type: type, + message: message, + typeField: typeField, + hidden: hidden, + topSpacing: topSpacing, + show: show, + required: required + ) + + displayedCells.append(displayedCell) + } + let viewModel = Form.GetFormCells.ViewModel(displayedFormCells: displayedCells) + viewController?.displayFormCells(viewModel: viewModel) + } + + func presentFieldValidation(response: Form.FieldValidation.Response) { + let viewModel = Form.FieldValidation.ViewModel(indexPath: response.indexPath, isValid: response.isValid) + viewController?.displayFieldValidation(viewModel: viewModel) + } + + func presentAllFieldsValidation(response: Form.AllFieldsValidation.Response) { + let isValid = response.isValid + let indexPath = response.indexPath + let message = response.message + let viewModel = Form.AllFieldsValidation.ViewModel( + isValid: isValid, + indexPath: indexPath, + message: message + ) + viewController?.displayAllFieldsValidation(viewModel: viewModel) + } + + func presentError(response: Form.FormError.Response) { + + var message: String + switch response.errorType { + case .getFormCells: + message = response.error.localizedDescription + case .missingCells: + message = response.error.domain + } + let viewModel = Form.FormError.ViewModel(message: message, errorType: response.errorType) + viewController?.displayError(viewModel: viewModel) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Form/FormRouter.swift b/Santander-Test/Santander-Test/Scenes/Form/FormRouter.swift new file mode 100644 index 00000000..880a7556 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/FormRouter.swift @@ -0,0 +1,36 @@ +// +// FormRouter.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +@objc protocol FormRoutingLogic { + func routeToSuccess(segue: UIStoryboardSegue?) +} + +protocol FormDataPassing { + var dataStore: FormDataStore? { get } +} + +class FormRouter: NSObject, FormRoutingLogic, FormDataPassing { + weak var viewController: FormViewController? + var dataStore: FormDataStore? + + func routeToSuccess(segue: UIStoryboardSegue?) { + let storyboard = UIStoryboard(name: "Success", bundle: nil) + let destinationVC = storyboard.instantiateViewController(withIdentifier: "Success") as! SuccessViewController + navigateToSuccess(source: viewController!, destination: destinationVC) + } + + func navigateToSuccess(source: FormViewController, destination: SuccessViewController) { + viewController?.navigationController?.pushViewController(destination, animated: true) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Form/FormViewController.swift b/Santander-Test/Santander-Test/Scenes/Form/FormViewController.swift new file mode 100644 index 00000000..ea81b8cd --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/FormViewController.swift @@ -0,0 +1,260 @@ +// +// FormViewController.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit +import AMPopTip + +protocol FormDisplayLogic: class { + func displayFormCells(viewModel: Form.GetFormCells.ViewModel) + func displayError(viewModel: Form.FormError.ViewModel) + func displayFieldValidation(viewModel: Form.FieldValidation.ViewModel) + func displayAllFieldsValidation(viewModel: Form.AllFieldsValidation.ViewModel) +} + +class FormViewController: UIViewController { + + @IBOutlet weak var tableView: UITableView! + + private var displayedFormCells: [Form.GetFormCells.ViewModel.DisplayViewModel] = [] + + var interactor: FormBusinessLogic? + var router: (NSObjectProtocol & FormRoutingLogic & FormDataPassing)? + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + private func setup() { + let viewController = self + let interactor = FormInteractor() + let presenter = FormPresenter() + let router = FormRouter() + viewController.interactor = interactor + viewController.router = router + interactor.presenter = presenter + presenter.viewController = viewController + router.viewController = viewController + router.dataStore = interactor + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let scene = segue.identifier { + let selector = NSSelectorFromString("routeTo\(scene)WithSegue:") + if let router = router, router.responds(to: selector) { + router.perform(selector, with: segue) + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + setupTableView() + requestData() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + setupTabBar() + } + + private func setupTableView() { + registerTableViewCells() + } + + private func setupTabBar() { + tabBarController?.tabBar.backgroundImage = UIImage( + named: MainTabBar.BackgroundImageName.form.rawValue + ) + } + + private func requestData() { + let request = Form.GetFormCells.Request() + interactor?.getFormCells(request: request) + } + + private func registerTableViewCells() { + let sendCell = UINib(nibName: "SendCell", bundle: nil) + tableView.register( + sendCell, + forCellReuseIdentifier: SendCell.reuseIdentifier + ) + + let checkboxCell = UINib(nibName: "CheckboxCell", bundle: nil) + tableView.register( + checkboxCell, + forCellReuseIdentifier: CheckboxCell.reuseIdentifier + ) + + let textCell = UINib(nibName: "TextCell", bundle: nil) + tableView.register( + textCell, + forCellReuseIdentifier: TextCell.reuseIdentifier + ) + + let fieldCell = UINib(nibName: "FieldCell", bundle: nil) + tableView.register( + fieldCell, + forCellReuseIdentifier: FieldCell.reuseIdentifier + ) + } + + private func showPopUpTip(_ tip: String, inView view: UIView) { + let superView = view.superview! + PopTip().show(text: tip, direction: .up, maxWidth: 250, in: superView, from: view.frame, duration: 4) + } + + private func hideAndDishideFields(value: Bool, _ cell: CheckboxCell) { + guard let cellToHide = cell.viewModel?.show else { return } + for count in 0.. Int { + return displayedFormCells.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + let formCell = displayedFormCells[indexPath.row] + switch formCell.type { + case Type.field: + let cell = tableView.dequeueReusableCell( + withIdentifier: FieldCell.reuseIdentifier + ) as! FieldCell + cell.viewModel = FieldCell.ViewModel( + message: formCell.message, + topSpace: formCell.topSpacing, + typeField: formCell.typeField, + indexPath: indexPath + ) + cell.delegate = self + cell.isHidden = formCell.hidden + return cell + case Type.text: + let cell = tableView.dequeueReusableCell( + withIdentifier: TextCell.reuseIdentifier + ) as! TextCell + cell.viewModel = TextCell.ViewModel( + message: formCell.message, + topSpace: formCell.topSpacing + ) + cell.isHidden = formCell.hidden + return cell + case Type.image: + return UITableViewCell() + case Type.checkbox: + let cell = tableView.dequeueReusableCell( + withIdentifier: CheckboxCell.reuseIdentifier + ) as! CheckboxCell + cell.viewModel = CheckboxCell.ViewModel( + message: formCell.message, + topSpace: formCell.topSpacing, + show: formCell.show + ) + cell.delegate = self + cell.isHidden = formCell.hidden + return cell + case Type.send: + let cell = tableView.dequeueReusableCell( + withIdentifier: SendCell.reuseIdentifier + ) as! SendCell + cell.viewModel = SendCell.ViewModel( + message: formCell.message, + topSpace: formCell.topSpacing + ) + cell.delegate = self + cell.isHidden = formCell.hidden + return cell + } + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + let displayedFormCell = displayedFormCells[indexPath.row] + + if displayedFormCell.hidden { + return 0 + } + return tableView.estimatedRowHeight + } +} + +extension FormViewController: FieldCellDelegate { + func textDidChange(for indexPath: IndexPath, text: String, typeField: TypeField) { + let request = Form.FieldValidation.Request(text: text, indexPath: indexPath, typeField: typeField) + interactor?.validateField(request: request) + } +} + +extension FormViewController: SendCellDelegate { + func buttonPressed() { + let request = Form.AllFieldsValidation.Request( + tableView: tableView + ) + interactor?.validateAllFields(request: request) + } +} + +extension FormViewController: CheckboxCellDelegate { + func didChoose(value: Bool, _ cell: CheckboxCell) { + hideAndDishideFields(value: value, cell) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Form/FormWorker.swift b/Santander-Test/Santander-Test/Scenes/Form/FormWorker.swift new file mode 100644 index 00000000..55e978f9 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Form/FormWorker.swift @@ -0,0 +1,86 @@ +// +// FormWorker.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 14/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +class FormWorker { + + func getFormCells(completion: @escaping ([FormCell]?, Error?) -> Void) { + let apiManager = FundsServiceApiManager.shared + apiManager.getFormCells { (formCells, error) in + + guard error == nil else { + completion(nil, error) + return + } + + guard let formCells = formCells else { + let domain = "Ocorreu um erro inesperado!" + let responseError = NSError(domain: domain, code: 200, userInfo: nil) + completion(nil, responseError) + return + } + + completion(formCells, nil) + } + } + + func validateField(text: String, typeField: TypeField) -> Bool { + switch typeField { + case .email: + return FieldValidation.isValid(email: text) + case .telNumber: + return FieldValidation.isValid(phoneNumber: text) + case .text: + return FieldValidation.isValid(text: text) + } + } + + func validateAllFields( + tableView: UITableView, + completion: (Bool, IndexPath?, String?) -> Void) { + + var response: Bool = true + var responseIndexPath: IndexPath? + var responseMessage: String? + + for count in 0.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailInteractor.swift b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailInteractor.swift new file mode 100644 index 00000000..525851ae --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailInteractor.swift @@ -0,0 +1,45 @@ +// +// FundDetailInteractor.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +protocol FundDetailBusinessLogic { + func getFundDetail(request: FundDetail.GetFundDetail.Request) +} + +protocol FundDetailDataStore {} + +class FundDetailInteractor: FundDetailBusinessLogic, FundDetailDataStore { + var presenter: FundDetailPresentationLogic? + var worker: FundDetailWorker? + + func getFundDetail(request: FundDetail.GetFundDetail.Request) { + worker = FundDetailWorker() + worker?.getFund(completion: { [unowned self] (fund, error) in + guard error == nil else { + let response = FundDetail.FundDetailError.Response(error: error! as NSError, errorType: .getFundDetail) + self.presenter?.presentError(response: response) + return + } + + guard let fund = fund else { + let domain = "Não há fundo de investimento a ser exibido" + let responseError = NSError(domain: domain, code: 200, userInfo: nil) + let response = FundDetail.FundDetailError.Response(error: responseError, errorType: .missingFund) + self.presenter?.presentError(response: response) + return + } + let response = FundDetail.GetFundDetail.Response(fund: fund) + self.presenter?.presentFundDetail(response: response) + }) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailModels.swift b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailModels.swift new file mode 100644 index 00000000..8e1e0430 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailModels.swift @@ -0,0 +1,54 @@ +// +// FundDetailModels.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +enum FundDetail { + + enum ErrorType { + case getFundDetail + case missingFund + } + + enum GetFundDetail { + struct Request {} + struct Response { + let fund: Fund + } + struct ViewModel { + let title: String + let fundName: String + let whatIs: String + let definition: String + let riskTitle: String + let risk: Int? + let infoTitle: String + let moreInfo: FundMoreInfo? + let info: [FundInfo] + let downInfo: [FundInfo] + let buttonMessage: String + let buttonTopSpace: Int + } + } + + enum FundDetailError { + struct Request {} + struct Response { + let error: NSError + let errorType: ErrorType + } + struct ViewModel { + let message: String + let errorType: ErrorType + } + } +} diff --git a/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailPresenter.swift b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailPresenter.swift new file mode 100644 index 00000000..61a4e6b1 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailPresenter.swift @@ -0,0 +1,55 @@ +// +// FundDetailPresenter.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +protocol FundDetailPresentationLogic { + // func presentSomething(response: FundDetail.Something.Response) + func presentFundDetail(response: FundDetail.GetFundDetail.Response) + func presentError(response: FundDetail.FundDetailError.Response) +} + +class FundDetailPresenter: FundDetailPresentationLogic { + weak var viewController: FundDetailDisplayLogic? + + func presentFundDetail(response: FundDetail.GetFundDetail.Response) { + + let fund = response.fund + let viewModel = FundDetail.GetFundDetail.ViewModel( + title: fund.title ?? "-", + fundName: fund.fundName ?? "-", + whatIs: fund.whatIs ?? "-", + definition: fund.definition ?? "-", + riskTitle: fund.riskTitle ?? "-", + risk: fund.risk, + infoTitle: fund.infoTitle ?? "-", + moreInfo: fund.moreInfo, + info: fund.info ?? [], + downInfo: fund.downInfo ?? [], + buttonMessage: "Investir", + buttonTopSpace: 45 + ) + viewController?.displayFundDetail(viewModel: viewModel) + } + + func presentError(response: FundDetail.FundDetailError.Response) { + var message: String! + switch response.errorType { + case .getFundDetail: + message = response.error.localizedDescription + case .missingFund: + message = response.error.domain + } + let viewModel = FundDetail.FundDetailError.ViewModel(message: message, errorType: response.errorType) + viewController?.displayError(viewModel: viewModel) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailRouter.swift b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailRouter.swift new file mode 100644 index 00000000..6720f673 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailRouter.swift @@ -0,0 +1,60 @@ +// +// FundDetailRouter.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +@objc protocol FundDetailRoutingLogic +{ + //func routeToSomewhere(segue: UIStoryboardSegue?) +} + +protocol FundDetailDataPassing +{ + var dataStore: FundDetailDataStore? { get } +} + +class FundDetailRouter: NSObject, FundDetailRoutingLogic, FundDetailDataPassing +{ + weak var viewController: FundDetailViewController? + var dataStore: FundDetailDataStore? + + // MARK: Routing + + //func routeToSomewhere(segue: UIStoryboardSegue?) + //{ + // if let segue = segue { + // let destinationVC = segue.destination as! SomewhereViewController + // var destinationDS = destinationVC.router!.dataStore! + // passDataToSomewhere(source: dataStore!, destination: &destinationDS) + // } else { + // let storyboard = UIStoryboard(name: "Main", bundle: nil) + // let destinationVC = storyboard.instantiateViewController(withIdentifier: "SomewhereViewController") as! SomewhereViewController + // var destinationDS = destinationVC.router!.dataStore! + // passDataToSomewhere(source: dataStore!, destination: &destinationDS) + // navigateToSomewhere(source: viewController!, destination: destinationVC) + // } + //} + + // MARK: Navigation + + //func navigateToSomewhere(source: FundDetailViewController, destination: SomewhereViewController) + //{ + // source.show(destination, sender: nil) + //} + + // MARK: Passing data + + //func passDataToSomewhere(source: FundDetailDataStore, destination: inout SomewhereDataStore) + //{ + // destination.name = source.name + //} +} diff --git a/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailViewController.swift b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailViewController.swift new file mode 100644 index 00000000..2b3a1d78 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailViewController.swift @@ -0,0 +1,245 @@ +// +// FundDetailViewController.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit +import SafariServices + +protocol FundDetailDisplayLogic: class { + func displayFundDetail(viewModel: FundDetail.GetFundDetail.ViewModel) + func displayError(viewModel: FundDetail.FundDetailError.ViewModel) +} + +class FundDetailViewController: UIViewController { + + @IBOutlet weak var tableView: UITableView! + + var displayedFund: FundDetail.GetFundDetail.ViewModel? + + var interactor: FundDetailBusinessLogic? + var router: (NSObjectProtocol & FundDetailRoutingLogic & FundDetailDataPassing)? + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + private func setup() { + let viewController = self + let interactor = FundDetailInteractor() + let presenter = FundDetailPresenter() + let router = FundDetailRouter() + viewController.interactor = interactor + viewController.router = router + interactor.presenter = presenter + presenter.viewController = viewController + router.viewController = viewController + router.dataStore = interactor + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let scene = segue.identifier { + let selector = NSSelectorFromString("routeTo\(scene)WithSegue:") + if let router = router, router.responds(to: selector) { + router.perform(selector, with: segue) + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + setupTableView() + requestData() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + setupTabBar() + } + + private func setupTableView() { + registerTableViewCells() + } + + private func setupTabBar() { + tabBarController?.tabBar.backgroundImage = UIImage( + named: MainTabBar.BackgroundImageName.fundDetails.rawValue + ) + } + + private func requestData() { + let request = FundDetail.GetFundDetail.Request() + interactor?.getFundDetail(request: request) + } + + private func registerTableViewCells() { + let fundHeader = UINib(nibName: "FundHeader", bundle: nil) + tableView.register( + fundHeader, + forCellReuseIdentifier: FundHeader.reuseIdentifier + ) + + let fundRisk = UINib(nibName: "FundRisk", bundle: nil) + tableView.register( + fundRisk, + forCellReuseIdentifier: FundRisk.reuseIdentifier + ) + + let fundMoreInfoCell = UINib(nibName: "FundMoreInfoCell", bundle: nil) + tableView.register( + fundMoreInfoCell, + forCellReuseIdentifier: FundMoreInfoCell.reuseIdentifier + ) + + let fundInfoCell = UINib(nibName: "FundInfoCell", bundle: nil) + tableView.register( + fundInfoCell, + forCellReuseIdentifier: FundInfoCell.reuseIdentifier + ) + + let fundDownInfoCell = UINib(nibName: "FundDownInfoCell", bundle: nil) + tableView.register( + fundDownInfoCell, + forCellReuseIdentifier: FundDownInfoCell.reuseIdentifier + ) + + let sendCell = UINib(nibName: "SendCell", bundle: nil) + tableView.register( + sendCell, + forCellReuseIdentifier: SendCell.reuseIdentifier + ) + } + + private func showSafariVC(for url: String) { + guard let url = URL(string: url) else { return } + let safariVC = SFSafariViewController(url: url) + present(safariVC, animated: true) + } +} + +extension FundDetailViewController: UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + return 6 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case 0, 1, 2, 5: + return 1 + case 3: + guard let info = displayedFund?.info else { return 0 } + return info.count + case 4: + guard let downInfo = displayedFund?.downInfo else { return 0 } + return downInfo.count + default: + return 0 + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + switch indexPath.section { + case 0: + let cell = tableView.dequeueReusableCell( + withIdentifier: FundHeader.reuseIdentifier + ) as! FundHeader + if let displayedFund = displayedFund { + cell.viewModel = FundHeader.ViewModel( + title: displayedFund.title, + fundName: displayedFund.fundName, + whatIs: displayedFund.whatIs, + definition: displayedFund.definition + ) + } + return cell + case 1: + let cell = tableView.dequeueReusableCell( + withIdentifier: FundRisk.reuseIdentifier + ) as! FundRisk + if let displayedFund = displayedFund { + cell.viewModel = FundRisk.ViewModel( + risk: displayedFund.risk + ) + } + return cell + case 2: + let cell = tableView.dequeueReusableCell( + withIdentifier: FundMoreInfoCell.reuseIdentifier + ) as! FundMoreInfoCell + if let displayedFund = displayedFund { + cell.viewModel = FundMoreInfoCell.ViewModel( + fundMoreInfo: displayedFund.moreInfo + ) + } + return cell + case 3: + let cell = tableView.dequeueReusableCell( + withIdentifier: FundInfoCell.reuseIdentifier + ) as! FundInfoCell + if let displayedFund = displayedFund { + cell.viewModel = FundInfoCell.ViewModel( + infoName: displayedFund.info[indexPath.row].name, + infoData: displayedFund.info[indexPath.row].data + ) + } + return cell + case 4: + let cell = tableView.dequeueReusableCell( + withIdentifier: FundDownInfoCell.reuseIdentifier + ) as! FundDownInfoCell + if let displayedFund = displayedFund { + cell.viewModel = FundDownInfoCell.ViewModel( + downInfoName: displayedFund.downInfo[indexPath.row].name + ) + } + cell.delegate = self + return cell + case 5: + let cell = tableView.dequeueReusableCell( + withIdentifier: SendCell.reuseIdentifier + ) as! SendCell + if let displayedFund = displayedFund { + cell.viewModel = SendCell.ViewModel( + message: displayedFund.buttonMessage, + topSpace: displayedFund.buttonTopSpace + ) + } + return cell + default: + return UITableViewCell() + } + } +} + +extension FundDetailViewController: FundDetailDisplayLogic { + + func displayFundDetail(viewModel: FundDetail.GetFundDetail.ViewModel) { + displayedFund = viewModel + tableView.reloadData() + } + + func displayError(viewModel: FundDetail.FundDetailError.ViewModel) { + showErrorFeedback(viewModel.message) + } +} + +extension FundDetailViewController: FundDownInfoCellDelegate { + func buttonPressed() { + showSafariVC(for: "https://www.google.com") + } +} diff --git a/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailWorker.swift b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailWorker.swift new file mode 100644 index 00000000..0966158b --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/FundDetail/FundDetailWorker.swift @@ -0,0 +1,36 @@ +// +// FundDetailWorker.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +class FundDetailWorker { + + func getFund(completion: @escaping (Fund?, Error?) -> Void) { + let apiManager = FundsServiceApiManager.shared + apiManager.getFundsList { (fundsList, error) in + + guard error == nil else { + completion(nil, error) + return + } + + guard let fund = fundsList?.screen else { + let domain = "Ocorreu um erro inesperado!" + let responseError = NSError(domain: domain, code: 200, userInfo: nil) + completion(nil, responseError) + return + } + + completion(fund, nil) + } + } +} diff --git a/Santander-Test/Santander-Test/Scenes/MainTabBar/MainTabBar.storyboard b/Santander-Test/Santander-Test/Scenes/MainTabBar/MainTabBar.storyboard new file mode 100644 index 00000000..afad357e --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/MainTabBar/MainTabBar.storyboard @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/Scenes/MainTabBar/MainTabBar.swift b/Santander-Test/Santander-Test/Scenes/MainTabBar/MainTabBar.swift new file mode 100644 index 00000000..f2a6bdf3 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/MainTabBar/MainTabBar.swift @@ -0,0 +1,39 @@ +// +// MainTabBar.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import UIKit + +class MainTabBar: UITabBarController { + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + setupTabBar() + } + + private func setupTabBar() { + tabBar.barTintColor = UIColor.getColorDarkRed + tabBar.items?.forEach({ (item) in + item.setTitleTextAttributes( + [NSAttributedString.Key.foregroundColor: UIColor.white, + NSAttributedString.Key.font: UIFont(name: "DINPro-Medium", size: 19) as Any], + for: .normal + ) + }) + + tabBar.items?.first?.title = "Investimento" + tabBar.items?[1].title = "Contato" + } + +} + +extension MainTabBar { + enum BackgroundImageName: String { + case form = "tab_bar_contato" + case fundDetails = "tab_bar_investimento" + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Success/Success.storyboard b/Santander-Test/Santander-Test/Scenes/Success/Success.storyboard new file mode 100644 index 00000000..153af90f --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Success/Success.storyboard @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + DINPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Santander-Test/Santander-Test/Scenes/Success/SuccessRouter.swift b/Santander-Test/Santander-Test/Scenes/Success/SuccessRouter.swift new file mode 100644 index 00000000..5def1213 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Success/SuccessRouter.swift @@ -0,0 +1,31 @@ +// +// SuccessRouter.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +@objc protocol SuccessRoutingLogic { + func routeToForm(segue: UIStoryboardSegue?) +} + +class SuccessRouter: NSObject, SuccessRoutingLogic { + weak var viewController: SuccessViewController? + + func routeToForm(segue: UIStoryboardSegue?) { + let storyboard = UIStoryboard(name: "Form", bundle: nil) + let destinationVC = storyboard.instantiateViewController(withIdentifier: "Form") as! FormViewController + navigateToForm(source: viewController!, destination: destinationVC) + } + + func navigateToForm(source: SuccessViewController, destination: FormViewController) { + viewController?.navigationController?.popViewController(animated: true) + } +} diff --git a/Santander-Test/Santander-Test/Scenes/Success/SuccessViewController.swift b/Santander-Test/Santander-Test/Scenes/Success/SuccessViewController.swift new file mode 100644 index 00000000..efb53722 --- /dev/null +++ b/Santander-Test/Santander-Test/Scenes/Success/SuccessViewController.swift @@ -0,0 +1,61 @@ +// +// SuccessViewController.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 16/05/19. +// Copyright (c) 2019 Matheus Ribeiro. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +import UIKit + +class SuccessViewController: UIViewController { + + @IBOutlet weak var thankYouLabel: UILabel! + @IBOutlet weak var sendNewMessageButton: UIButton! + + var router: (NSObjectProtocol & SuccessRoutingLogic)? + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + private func setup() { + let viewController = self + let router = SuccessRouter() + viewController.router = router + router.viewController = viewController + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let scene = segue.identifier { + let selector = NSSelectorFromString("routeTo\(scene)WithSegue:") + if let router = router, router.responds(to: selector) { + router.perform(selector, with: segue) + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + configureLayout() + } + + private func configureLayout() { + thankYouLabel.textColor = UIColor.getColorDarkGray + sendNewMessageButton.tintColor = UIColor.getColorDarkRed + } + + @IBAction func sendNewMessageButton(_ sender: Any) { + router?.routeToForm(segue: nil) + } +} diff --git a/Santander-Test/Santander-Test/Supporting Files/API/FundsService/FundsServiceApi.swift b/Santander-Test/Santander-Test/Supporting Files/API/FundsService/FundsServiceApi.swift new file mode 100644 index 00000000..6236a8dd --- /dev/null +++ b/Santander-Test/Santander-Test/Supporting Files/API/FundsService/FundsServiceApi.swift @@ -0,0 +1,59 @@ +// +// FundsServiceApi.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Moya + +enum FundsServiceApi { + case getFormCellsList + case getFundsList +} + +extension FundsServiceApi: TargetType { + var baseURL: URL { + return URL(string: Constants.baseUrl)! + } + + var path: String { + switch self { + case .getFormCellsList: + return "/cells.json" + case .getFundsList: + return "/fund.json" + } + } + + var method: Method { + switch self { + case .getFormCellsList, + .getFundsList: + return .get + } + } + + var sampleData: Data { + return Data() + } + + var task: Task { + switch self { + case .getFormCellsList, + .getFundsList: + return .requestPlain + } + } + + var headers: [String : String]? { + switch self { + case .getFormCellsList, + .getFundsList: + return ["content-type": "application/json"] + } + } + + +} diff --git a/Santander-Test/Santander-Test/Supporting Files/API/FundsService/FundsServiceApiManager.swift b/Santander-Test/Santander-Test/Supporting Files/API/FundsService/FundsServiceApiManager.swift new file mode 100644 index 00000000..0775a7af --- /dev/null +++ b/Santander-Test/Santander-Test/Supporting Files/API/FundsService/FundsServiceApiManager.swift @@ -0,0 +1,123 @@ +// +// FundsServiceApiManager.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Moya + +class FundsServiceApiManager { + + static let shared = FundsServiceApiManager() + + private init() {} + + private let provider = MoyaProvider() + + func getFormCells(completion: @escaping ([FormCell]?, Error?) -> Void) { + provider.request(.getFormCellsList) { [unowned self] (response) in + switch response { + case .success(let value): + do { + let formCellsList: FormCellsList = try JSONDecoder().decode(FormCellsList.self, from: value.data) + let formCells = self.getFormCells(from: formCellsList) + + completion(formCells, nil) + } catch { + completion(nil, error) + } + case .failure(let error): + completion(nil, error) + } + } + } + + func getFundsList(completion: @escaping (FundsList?, Error?) -> Void) { + provider.request(.getFundsList) { (response) in + switch response { + case .success(let value): + do { + let fundsList: FundsList = try JSONDecoder().decode(FundsList.self, from: value.data) + completion(fundsList, nil) + } catch { + completion(nil, error) + } + case .failure(let error): + completion(nil, error) + } + } + } + + private func getFormCells(from formCellsList: FormCellsList) -> [FormCell] { + + var formCells: [FormCell] = [] + for formCellModel in formCellsList.cells { + let id = formCellModel.id + let type: Type? = { + switch formCellModel.type { + case 1: + return Type.field + case 2: + return Type.text + case 3: + return Type.image + case 4: + return Type.checkbox + case 5: + return Type.send + default: + return nil + } + }() + let message = formCellModel.message + let typefield: TypeField? = { + if let typeFieldString = formCellModel.typefield?.value as? String { + switch typeFieldString { + case "email": + return TypeField.email + case "telnumber": + return TypeField.telNumber + case "text": + return TypeField.text + default: + return nil + } + } else if let typeFieldInt = formCellModel.typefield?.value as? Int { + switch typeFieldInt { + case 1: + return TypeField.text + case 2: + return TypeField.telNumber + case 3: + return TypeField.email + default: + return nil + } + } else { + return nil + } + }() + let hidden = formCellModel.hidden + let topSpacing = formCellModel.topSpacing + let show = formCellModel.show + let required = formCellModel.required + + let formCell = FormCell( + id: id, + type: type, + message: message, + typefield: typefield, + hidden: hidden, + topSpacing: topSpacing, + show: show, + required: required + ) + formCells.append(formCell) + } + + return formCells + } + +} diff --git a/Santander-Test/Santander-Test/Supporting Files/Constants.swift b/Santander-Test/Santander-Test/Supporting Files/Constants.swift new file mode 100644 index 00000000..32db4da5 --- /dev/null +++ b/Santander-Test/Santander-Test/Supporting Files/Constants.swift @@ -0,0 +1,13 @@ +// +// Constants.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Foundation + +enum Constants { + static let baseUrl = "https://floating-mountain-50292.herokuapp.com" +} diff --git a/Santander-Test/Santander-Test/Supporting Files/Extensions/FloatExtensions.swift b/Santander-Test/Santander-Test/Supporting Files/Extensions/FloatExtensions.swift new file mode 100644 index 00000000..a9794867 --- /dev/null +++ b/Santander-Test/Santander-Test/Supporting Files/Extensions/FloatExtensions.swift @@ -0,0 +1,24 @@ +// +// DoubleExtensions.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 20/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Foundation + +extension Float { + + func getBrazilianPercentFormat() -> String? { + let formatter = NumberFormatter() + formatter.numberStyle = .percent + formatter.locale = NSLocale(localeIdentifier: "pt_BR") as Locale + formatter.decimalSeparator = "," + formatter.minimumFractionDigits = 1 + formatter.maximumFractionDigits = 1 + + return formatter.string(from: self / 100 as NSNumber) + } + +} diff --git a/Santander-Test/Santander-Test/Supporting Files/Extensions/StringExtensions.swift b/Santander-Test/Santander-Test/Supporting Files/Extensions/StringExtensions.swift new file mode 100644 index 00000000..9e50c035 --- /dev/null +++ b/Santander-Test/Santander-Test/Supporting Files/Extensions/StringExtensions.swift @@ -0,0 +1,43 @@ +// +// StringExtensions.swift +// Santander-Test +// +// Created by Matheus Ribeiro on 20/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import Foundation + +extension String { + func convertToPhoneNumberFormat() -> String? { + var text = self.replacingOccurrences(of: "(", with: "") + text = text.replacingOccurrences(of: ")", with: "") + text = text.replacingOccurrences(of: "-", with: "") + text = text.replacingOccurrences(of: " ", with: "") + + let numberPredicate = NSPredicate(format: "SELF MATCHES %@", "^[0-9]*$") + + guard numberPredicate.evaluate(with: text) else { return nil } + + let textLenght = text.count + + switch textLenght { + case 1, 2: + return "(\(text)" + case 3, 4, 5, 6, 7: + return "(\(text.prefix(2))) \(text.suffix(textLenght - 2))" + case 8, 9: + let start = text.index(text.startIndex, offsetBy: 2) + let end = text.index(text.endIndex, offsetBy: 7 - textLenght) + let range = start.. Bool { + return !text.isEmpty + } + + static func isValid(phoneNumber: String) -> Bool { + let phoneNumberPredicate = NSPredicate(format: "SELF MATCHES %@", "^\\([1-9]{2}\\) (?:[2-8]|9[1-9])[0-9]{3}\\-[0-9]{4}$") + let isPhoneNumber = phoneNumberPredicate.evaluate(with: phoneNumber) + return isPhoneNumber + } + + static func isValid(email: String) -> Bool { + let emailPredicate = NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}") + let isEmail = emailPredicate.evaluate(with: email) + + return isEmail + } + +} diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINEngschriftStd.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINEngschriftStd.otf new file mode 100755 index 00000000..ae85f8ee Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINEngschriftStd.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINMittelschriftStd.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINMittelschriftStd.otf new file mode 100755 index 00000000..9a6e0d4f Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINMittelschriftStd.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-BdCond.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-BdCond.otf new file mode 100755 index 00000000..1da42b06 Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-BdCond.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-Light.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-Light.otf new file mode 100755 index 00000000..0cda2e5b Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINNeuzeitGroteskStd-Light.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Black.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Black.otf new file mode 100755 index 00000000..2092a7bb Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Black.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Bold.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Bold.otf new file mode 100755 index 00000000..7c839536 Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Bold.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Light.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Light.otf new file mode 100755 index 00000000..8a7f085a Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Light.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Medium.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Medium.otf new file mode 100755 index 00000000..b4608d06 Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Medium.otf differ diff --git a/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Regular.otf b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Regular.otf new file mode 100755 index 00000000..84d57abb Binary files /dev/null and b/Santander-Test/Santander-Test/Supporting Files/Fonts/20. Din [1926 - Ludwig Goller]/DINPro-Regular.otf differ diff --git a/Santander-Test/Santander-TestTests/FieldValidationTests.swift b/Santander-Test/Santander-TestTests/FieldValidationTests.swift new file mode 100644 index 00000000..9662e24b --- /dev/null +++ b/Santander-Test/Santander-TestTests/FieldValidationTests.swift @@ -0,0 +1,92 @@ +// +// FieldValidationTests.swift +// Santander-TestTests +// +// Created by Matheus Ribeiro on 20/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import XCTest +@testable import Santander_Test + +class FieldValidationTests: XCTestCase { + + func testCorrectPhoneNumberValidation() { + let numbersList = ["(99) 99123-5232", "(92) 8485-2719", "(91) 8456-2345", "(92) 99340-9876", "(93) 8558-0950"] + + for number in numbersList { + let isValidNumber = FieldValidation.isValid(phoneNumber: number) + XCTAssertTrue(isValidNumber, number) + } + } + + func testWrongPhoneNumberValidation() { + let numbersList = [ + "(9) 99123-5232", "(92)8485-2719", "(91) 456-2345", + "(92) 9934-09876", "9385580950", "(92) 991199239", + "23) 9119-3949", "(92) 9138-2847" + ] + + for number in numbersList { + let isValidNumber = FieldValidation.isValid(phoneNumber: number) + XCTAssertFalse(isValidNumber, number) + } + } + + func testCorrectEmailValidation() { + let emailList = ["name@mail.com", "name@mail.com.br", "name@mailmail.com.br", "namename@mail.com.br.br"] + + for email in emailList { + let isValidEmail = FieldValidation.isValid(email: email) + XCTAssertTrue(isValidEmail, email) + } + } + + func testWrongEmailValidation() { + + let emailList = ["wrong@mail", "wrong@mail.", "wrong@mail.c", "@mail.com", "wrong.mail"] + + for email in emailList { + let isValidEmail = FieldValidation.isValid(email: email) + XCTAssertFalse(isValidEmail, email) + } + } + + func testConvertToPhoneNumberFormat() { + + var textFormatted = "1".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(1") + + textFormatted = "12".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12") + + textFormatted = "123".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 3") + + textFormatted = "1234".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 34") + + textFormatted = "12345".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 345") + + textFormatted = "123456".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 3456") + + textFormatted = "1234567".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 34567") + + textFormatted = "12345678".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 34567-8") + + textFormatted = "123456789".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 34567-89") + + textFormatted = "1234567890".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 3456-7890") + + textFormatted = "12345678901".convertToPhoneNumberFormat() + XCTAssertEqual(textFormatted, "(12) 34567-8901") + + } + +} diff --git a/Santander-Test/Santander-TestTests/FloatExtensionsTests.swift b/Santander-Test/Santander-TestTests/FloatExtensionsTests.swift new file mode 100644 index 00000000..dfed3917 --- /dev/null +++ b/Santander-Test/Santander-TestTests/FloatExtensionsTests.swift @@ -0,0 +1,36 @@ +// +// FloatExtensionsTests.swift +// Santander-TestTests +// +// Created by Matheus Ribeiro on 20/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import XCTest +@testable import Santander_Test + +class FloatExtensionsTests: XCTestCase { + + func testBrazilianPercentFormat() { + let numbers: [Float] = [3.0, 3.1, 3.4, 4.3, 5.59, 6.01] + + var formatted = numbers[0].getBrazilianPercentFormat() + XCTAssertEqual(formatted, "3,0%") + + formatted = numbers[1].getBrazilianPercentFormat() + XCTAssertEqual(formatted, "3,1%") + + formatted = numbers[2].getBrazilianPercentFormat() + XCTAssertEqual(formatted, "3,4%") + + formatted = numbers[3].getBrazilianPercentFormat() + XCTAssertEqual(formatted, "4,3%") + + formatted = numbers[4].getBrazilianPercentFormat() + XCTAssertEqual(formatted, "5,6%") + + formatted = numbers[5].getBrazilianPercentFormat() + XCTAssertEqual(formatted, "6,0%") + } + +} diff --git a/Santander-Test/Santander-TestTests/Info.plist b/Santander-Test/Santander-TestTests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/Santander-Test/Santander-TestTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Santander-Test/Santander-TestUITests/Info.plist b/Santander-Test/Santander-TestUITests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/Santander-Test/Santander-TestUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Santander-Test/Santander-TestUITests/Santander_TestUITests.swift b/Santander-Test/Santander-TestUITests/Santander_TestUITests.swift new file mode 100644 index 00000000..2dcabe9a --- /dev/null +++ b/Santander-Test/Santander-TestUITests/Santander_TestUITests.swift @@ -0,0 +1,34 @@ +// +// Santander_TestUITests.swift +// Santander-TestUITests +// +// Created by Matheus Ribeiro on 13/05/19. +// Copyright © 2019 Matheus Ribeiro. All rights reserved. +// + +import XCTest + +class Santander_TestUITests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + +}