Skip to content

Commit

Permalink
Merge pull request #143 from verygoodsecurity/canary
Browse files Browse the repository at this point in the history
Release 1.5.1
  • Loading branch information
dmytrokhl committed May 27, 2020
2 parents b1ebae4 + a3064bc commit cb77723
Show file tree
Hide file tree
Showing 85 changed files with 1,577 additions and 267 deletions.
5 changes: 5 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Security Policy

If you found a security issue, please do not open GitHub issues or pull requests - this makes the problem immediately visible to everyone, including malicious actors. Security issues in the SDK can be safely reported to VGS by email: [email protected]

**Before reporting security issue please read this** [guide](https://www.verygoodsecurity.com/learn/reporting-security-vulnerability).

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ internal class MaskedTextField: UITextField {
*/
@available(*, deprecated, message: "Don't use this method.")
override var text: String? {
set {}
set {
// This fixes issue when clear button don't remove content on not activ textfield form first touch
super.text = nil
}
get { return nil }
}

Expand Down
177 changes: 130 additions & 47 deletions Sources/VGSCollectSDK/UIElements/Text Field/VGSCardTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,68 +12,151 @@ import UIKit

/// An object that displays an editable text area. Can be use instead of a `VGSTextField` when need to detect and show credit card brand images.
public class VGSCardTextField: VGSTextField {
/// card brand icon width
var iconWidth: CGFloat = 45

internal let cardIconView = UIImageView()
internal lazy var stackView = self.makeStackView()
internal let stackSpacing: CGFloat = 8.0
internal lazy var defaultUnknowBrandImage: UIImage? = {
return UIImage(named: "unknown", in: AssetsBundle.main.iconBundle, compatibleWith: nil)
}()

// MARK: - Enum cases
/// Available Card brand icon positions enum.
public enum CardIconLocation {
/// Card brand icon at left side of `VGSCardTextField`.
case left

/// Card brand icon at right side of `VGSCardTextField`.
case right
}

/// callback for taking card brand icon
public var cardsIconSource: ((SwiftLuhn.CardType) -> UIImage?)?
// MARK: Attributes
/// Card brand icon position inside `VGSCardTextField`.
public var cardIconLocation = CardIconLocation.right {
didSet {
setCardIconAtLocation(cardIconLocation)
}
}

/// Card brand icon size.
public var cardIconSize: CGSize = CGSize(width: 45, height: 45) {
didSet {
updateCardIconViewSize()
}
}

internal lazy var cardIconView = self.makeCardIcon()
// MARK: Custom card brand images
/// Asks custom image for specific `SwiftLuhn.CardType`
public var cardsIconSource: ((SwiftLuhn.CardType) -> UIImage?)?

/// :nodoc:
public override func didMoveToSuperview() {
super.didMoveToSuperview()
updateCardIcon()
updateCardImage()
}
}

internal extension VGSCardTextField {

// MARK: - Initialization
override func mainInitialization() {
super.mainInitialization()

setupCardIconView()
setCardIconAtLocation(cardIconLocation)
updateCardImage()
}

override func buildTextFieldUI() {
addSubview(stackView)
textField.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(textField)
setMainPaddings()
}

override func setMainPaddings() {
NSLayoutConstraint.deactivate(verticalConstraint)
NSLayoutConstraint.deactivate(horizontalConstraints)

let views = ["view": self, "stackView": stackView]

horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-\(padding.left)-[stackView]-\(padding.right)-|",
options: .alignAllCenterY,
metrics: nil,
views: views)
NSLayoutConstraint.activate(horizontalConstraints)

verticalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(padding.top)-[stackView]-\(padding.bottom)-|",
options: .alignAllCenterX,
metrics: nil,
views: views)
NSLayoutConstraint.activate(verticalConstraint)
self.layoutIfNeeded()
}

private func makeStackView() -> UIStackView {
let stack = UIStackView()
stack.alignment = .fill
stack.axis = .horizontal
stack.distribution = .fillProportionally
stack.translatesAutoresizingMaskIntoConstraints = false
stack.spacing = 8
return stack
}

// override textFieldDidChange
override func textFieldValueChanged() {
super.textFieldValueChanged()
updateCardIcon()
updateCardImage()
}

func updateCardImage() {
if let state = state as? CardState {
cardIconView.image = (cardsIconSource == nil) ? state.cardBrand.brandIcon : cardsIconSource?(state.cardBrand)
} else {
cardIconView.image = defaultUnknowBrandImage
}
}

func setCardIconAtLocation(_ location: CardIconLocation) {
cardIconView.removeFromSuperview()
switch location {
case .left:
stackView.insertArrangedSubview(cardIconView, at: 0)
case .right:
stackView.addArrangedSubview(cardIconView)
}
}

internal func updateCardIcon() {
if let state = state as? CardState {
if cardsIconSource != nil {
let icon = cardsIconSource?(state.cardBrand)
cardIconView.image = icon

} else {
cardIconView.image = state.cardBrand.brandIcon
}
} else {
cardIconView.image = UIImage(named: "unknown", in: AssetsBundle.main.iconBundle, compatibleWith: nil)
func updateCardIconViewSize() {
if let widthConstraint = cardIconView.constraints.filter({ $0.identifier == "widthConstraint" }).first {
widthConstraint.constant = cardIconSize.width
}
if let heightConstraint = cardIconView.constraints.filter({ $0.identifier == "heightConstraint" }).first {
heightConstraint.constant = cardIconSize.height
}
}

// make image view for a card brand icon
private func makeCardIcon() -> UIImageView {
let result = UIImageView(frame: .zero)

result.contentMode = .scaleAspectFit
addSubview(result)

// make constraints
let views = ["view": result]
result.translatesAutoresizingMaskIntoConstraints = false

let width = NSLayoutConstraint.constraints(withVisualFormat: "H:[view(==\(iconWidth))]",
options: .alignAllCenterY,
metrics: nil,
views: views)
NSLayoutConstraint.activate(width)

let vertical = NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[view]-0-|",
options: .alignAllCenterX,
metrics: nil,
views: views)
NSLayoutConstraint.activate(vertical)

let horizontal = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-5-|",
options: .alignAllCenterY,
metrics: nil,
views: views)
NSLayoutConstraint.activate(horizontal)

return result
private func setupCardIconView() {
cardIconView.translatesAutoresizingMaskIntoConstraints = false
cardIconView.contentMode = .scaleAspectFit
let widthConstraint = NSLayoutConstraint(item: cardIconView,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 1,
constant: cardIconSize.width)
widthConstraint.identifier = "widthConstraint"
let heightConstraint = NSLayoutConstraint(item: cardIconView,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 1,
constant: cardIconSize.height)
heightConstraint.identifier = "heightConstraint"
cardIconView.addConstraints([widthConstraint, heightConstraint])
}
}
80 changes: 59 additions & 21 deletions Sources/VGSCollectSDK/UIElements/Text Field/VGSTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ public class VGSTextField: UIView {
internal var validationModel = VGSValidation()
internal var fieldName: String!
internal var token: String?

internal var horizontalConstraints = [NSLayoutConstraint]()
internal var verticalConstraint = [NSLayoutConstraint]()

// MARK: - UI Attributes

/// Textfield placeholder string.
Expand All @@ -39,14 +41,24 @@ public class VGSTextField: UIView {

/// `UIEdgeInsets` for text and placeholder inside `VGSTextField`.
public var padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) {
didSet { textField.padding = padding }
didSet { setMainPaddings() }
}

/// The technique to use for aligning the text.
public var textAlignment: NSTextAlignment = .natural {
didSet { textField.textAlignment = textAlignment }
}

/// Sets when the clear button shows up. Default is `UITextField.ViewMode.never`
public var clearButtonMode: UITextField.ViewMode = .never {
didSet { textField.clearButtonMode = clearButtonMode }
}

/// Indicates whether `VGSTextField ` should automatically update its font when the device’s `UIContentSizeCategory` is changed.
public var adjustsFontForContentSizeCategory: Bool = false {
didSet { textField.adjustsFontForContentSizeCategory = adjustsFontForContentSizeCategory }
}

// MARK: - Functional Attributes

/// Specifies `VGSTextField` configuration parameters to work with `VGSCollect`.
Expand Down Expand Up @@ -105,6 +117,7 @@ public class VGSTextField: UIView {

deinit {
vgsCollector?.unregisterTextFields(textField: [self])
NotificationCenter.default.removeObserver(self)
}
}

Expand Down Expand Up @@ -132,51 +145,75 @@ extension VGSTextField: UITextFieldDelegate {
public func textFieldDidBeginEditing(_ textField: UITextField) {
delegate?.vgsTextFieldDidBeginEditing?(self)
}

@objc func textFieldDidChange(_ textField: UITextField) {
delegate?.vgsTextFieldDidChange?(self)
}

public func textFieldDidEndEditing(_ textField: UITextField) {
delegate?.vgsTextFieldDidEndEditing?(self)
}

@objc public func textFieldDidEndEditingOnExit(_ textField: UITextField) {
@objc func textFieldDidEndEditingOnExit(_ textField: UITextField) {
delegate?.vgsTextFieldDidEndEditingOnReturn?(self)
}
}

// MARK: - private API
internal extension VGSTextField {

@objc
func mainInitialization() {
// set main style for view
mainStyle()
// text view
// add UI elements
buildTextFieldUI()
// add otextfield observers and delegates
addTextFieldObservers()
}

@objc
func buildTextFieldUI() {
textField.translatesAutoresizingMaskIntoConstraints = false
addSubview(textField)
setMainPaddings()
}

@objc
func addTextFieldObservers() {
//delegates
textField.addSomeTarget(self, action: #selector(textFieldDidBeginEditing), for: .editingDidBegin)
//Note: .allEditingEvents doesn't work proparly when set text programatically. Use setText instead!
textField.addSomeTarget(self, action: #selector(textFieldValueChanged), for: .allEditingEvents)
textField.addSomeTarget(self, action: #selector(textFieldDidEndEditing), for: .editingDidEnd)
textField.addSomeTarget(self, action: #selector(textFieldDidEndEditingOnExit), for: .editingDidEndOnExit)
NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidChange), name: UITextField.textDidChangeNotification, object: textField)
// tap gesture for update focus state
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(focusOn))
textField.addGestureRecognizer(tapGesture)
}

@objc
func setMainPaddings() {
NSLayoutConstraint.deactivate(verticalConstraint)
NSLayoutConstraint.deactivate(horizontalConstraints)

let views = ["view": self, "textField": textField]

let views = ["view": self, "textField": textField]

let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[textField]-0-|",
horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-\(padding.left)-[textField]-\(padding.right)-|",
options: .alignAllCenterY,
metrics: nil,
views: views)
NSLayoutConstraint.activate(horizontalConstraints)
NSLayoutConstraint.activate(horizontalConstraints)

let verticalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[textField]-0-|",
verticalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(padding.top)-[textField]-\(padding.bottom)-|",
options: .alignAllCenterX,
metrics: nil,
views: views)
NSLayoutConstraint.activate(verticalConstraint)

//delegates
//Note: .allEditingEvents doesn't work proparly when set text programatically. Use setText instead!
textField.addSomeTarget(self, action: #selector(textFieldValueChanged), for: .allEditingEvents)
textField.addSomeTarget(self, action: #selector(textFieldDidBeginEditing), for: .editingDidBegin)
textField.addSomeTarget(self, action: #selector(textFieldDidEndEditing), for: .editingDidEnd)
textField.addSomeTarget(self, action: #selector(textFieldDidEndEditingOnExit), for: .editingDidEndOnExit)
// tap gesture for update focus state
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(focusOn))
textField.addGestureRecognizer(tapGesture)
NSLayoutConstraint.activate(verticalConstraint)
self.layoutIfNeeded()
}

@objc
func textFieldValueChanged() {
// update status
Expand All @@ -189,6 +226,7 @@ internal extension VGSTextField {
textField.secureText = text
// this will update card textfield icons
textFieldValueChanged()
textFieldDidChange(textField)
}

// change focus here
Expand Down
Loading

0 comments on commit cb77723

Please sign in to comment.