From 66a2a208cdd7126bb9778aed245142e35b6b751e Mon Sep 17 00:00:00 2001 From: mohssenfathi Date: Mon, 26 Aug 2024 13:34:50 -0700 Subject: [PATCH 1/2] Remove UberButton_DEPRECATED --- .../Authorize/AuthenticationSession.swift | 15 +- Sources/UberAuth/Button/LoginButton.swift | 4 +- Sources/UberCore/Colors.swift | 14 +- .../Contents.json | 0 .../Contents.json | 38 ++ Sources/UberCore/UberButton.swift | 290 ++++--------- Sources/UberRides/RideRequestButton.swift | 401 ++++++------------ .../UberSDK/UberSDK.xcodeproj/project.pbxproj | 4 + .../UberSDK/UberSDK/AuthResponseView.swift | 46 ++ examples/UberSDK/UberSDK/ContentView.swift | 128 +++--- examples/UberSDK/UberSDK/UberButtonView.swift | 45 ++ .../UberRides/RequestButtonTests.swift | 70 +-- 12 files changed, 460 insertions(+), 595 deletions(-) rename Sources/UberCore/Resources/Media.xcassets/{UberButtonHighlightedBackground.colorset => UberButtonHighlightedDarkBackground.colorset}/Contents.json (100%) create mode 100644 Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedLightBackground.colorset/Contents.json create mode 100644 examples/UberSDK/UberSDK/AuthResponseView.swift diff --git a/Sources/UberAuth/Authorize/AuthenticationSession.swift b/Sources/UberAuth/Authorize/AuthenticationSession.swift index d51e3c2..466deb7 100644 --- a/Sources/UberAuth/Authorize/AuthenticationSession.swift +++ b/Sources/UberAuth/Authorize/AuthenticationSession.swift @@ -38,7 +38,7 @@ final class AuthenticationSession: AuthenticationSessioning { completion(.failure(UberAuthError.invalidAuthCode)) case (.some(let url), _): guard let code = Self.parse(url: url) else { - completion(.failure(UberAuthError.invalidAuthCode)) + completion(.failure(Self.parseError(url: url))) return } completion(.success(.init(authorizationCode: code))) @@ -68,6 +68,19 @@ final class AuthenticationSession: AuthenticationSessioning { } return codeParameter.value } + + private static func parseError(url: URL) -> UberAuthError { + guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), + let errorParameter = components.queryItems?.first(where: { $0.name == "error" })?.value else { + return .invalidAuthCode + } + switch OAuthError(rawValue: errorParameter) { + case .some(let error): + return UberAuthError.oAuth(error) + case .none: + return .invalidAuthCode + } + } } final class AuthPresentationContextProvider: NSObject, ASWebAuthenticationPresentationContextProviding { diff --git a/Sources/UberAuth/Button/LoginButton.swift b/Sources/UberAuth/Button/LoginButton.swift index fe8ca99..47c88df 100644 --- a/Sources/UberAuth/Button/LoginButton.swift +++ b/Sources/UberAuth/Button/LoginButton.swift @@ -87,8 +87,8 @@ public final class LoginButton: UberButton { // MARK: UberButton - override public var title: String { - buttonState.title + public override var title: NSAttributedString? { + NSAttributedString(string: buttonState.title) } override public var image: UIImage? { diff --git a/Sources/UberCore/Colors.swift b/Sources/UberCore/Colors.swift index fdfb6d3..57881d0 100644 --- a/Sources/UberCore/Colors.swift +++ b/Sources/UberCore/Colors.swift @@ -5,7 +5,7 @@ import UIKit -extension UIColor { +public extension UIColor { static let uberButtonBackground: UIColor = UIColor( named: "UberButtonBackground", @@ -13,9 +13,15 @@ extension UIColor { compatibleWith: nil ) ?? UIColor.darkText - static let uberButtonHighlightedBackground: UIColor = UIColor( - named: "UberButtonHighlightedBackground", - in: .resource(for: UberButton.self), + static let uberButtonHighlightedDarkBackground: UIColor = UIColor( + named: "UberButtonHighlightedDarkBackground", + in: .module, + compatibleWith: nil + ) ?? UIColor.darkText + + static let uberButtonHighlightedLightBackground: UIColor = UIColor( + named: "UberButtonHighlightedLightBackground", + in: .module, compatibleWith: nil ) ?? UIColor.darkText diff --git a/Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedBackground.colorset/Contents.json b/Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedDarkBackground.colorset/Contents.json similarity index 100% rename from Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedBackground.colorset/Contents.json rename to Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedDarkBackground.colorset/Contents.json diff --git a/Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedLightBackground.colorset/Contents.json b/Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedLightBackground.colorset/Contents.json new file mode 100644 index 0000000..7fcce67 --- /dev/null +++ b/Sources/UberCore/Resources/Media.xcassets/UberButtonHighlightedLightBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE4", + "green" : "0xE5", + "red" : "0xE5" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x27", + "green" : "0x27", + "red" : "0x28" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/UberCore/UberButton.swift b/Sources/UberCore/UberButton.swift index 72df13c..2f74030 100644 --- a/Sources/UberCore/UberButton.swift +++ b/Sources/UberCore/UberButton.swift @@ -22,26 +22,35 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +import Foundation import UIKit open class UberButton: UIButton { // MARK: Public Properties - open var title: String { "" } + open var title: NSAttributedString? { nil } + + open var subtitle: NSAttributedString? { nil } open var image: UIImage? { nil } + open var horizontalAlignment: UIControl.ContentHorizontalAlignment { .fill } + + open var imagePlacement: NSDirectionalRectEdge { .leading } + // MARK: Initializers public override init(frame: CGRect) { super.init(frame: frame) configure() + update() } required public init?(coder: NSCoder) { super.init(coder: coder) configure() + update() } // MARK: UIButton @@ -49,43 +58,64 @@ open class UberButton: UIButton { open override var isHighlighted: Bool { didSet { update() } } - + // MARK: Private + lazy var secondaryLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.textColor = .uberButtonForeground + label.numberOfLines = 0 + label.textAlignment = .right + return label + }() + private func configure() { - contentHorizontalAlignment = .fill - update() + addSubview(secondaryLabel) + + NSLayoutConstraint.activate([ + secondaryLabel.bottomAnchor.constraint(equalTo: bottomAnchor), + secondaryLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -12), + secondaryLabel.topAnchor.constraint(equalTo: topAnchor) + ]) + + if let titleLabel { + secondaryLabel.leftAnchor.constraint(equalTo: titleLabel.rightAnchor, constant: 24).isActive = true + } } public func update() { - DispatchQueue.main.async { [self] in - if #available(iOS 15, *) { - configuration = .uber( - title: title, - image: image, - isHighlighted: isHighlighted - ) - updateConfiguration() - } - else { - clipsToBounds = true - layer.cornerRadius = Constants.cornerRadius + if #available(iOS 15, *) { + secondaryLabel.attributedText = subtitle + + contentHorizontalAlignment = horizontalAlignment + configuration = .uber( + title: AttributedString(title ?? .init(string: "")), + image: image, + isHighlighted: isHighlighted, + imagePlacement: imagePlacement + ) + + updateConfiguration() + } + else { + clipsToBounds = true + layer.cornerRadius = Constants.cornerRadius - setImage( - image?.withRenderingMode(.alwaysTemplate), - for: .normal - ) - imageView?.tintColor = .uberButtonForeground - imageView?.contentMode = .left - - setTitle(title, for: .normal) - titleLabel?.textAlignment = .right - - setTitleColor(.uberButtonForeground, for: .normal) - backgroundColor = isHighlighted ? .uberButtonHighlightedBackground : .uberButtonBackground - - contentEdgeInsets = Constants.contentInsets - } + setImage( + image?.withRenderingMode(.alwaysTemplate), + for: .normal + ) + imageView?.tintColor = .uberButtonForeground + imageView?.contentMode = .left + + setTitle(title?.string, for: .normal) + titleLabel?.textAlignment = .right + + setTitleColor(.uberButtonForeground, for: .normal) + backgroundColor = ColorStyle.light.backgroundColor(isHighlighted: isHighlighted) + + contentEdgeInsets = Constants.contentInsets } } @@ -95,193 +125,55 @@ open class UberButton: UIButton { static let verticalPadding: CGFloat = 10 static let contentInsets: UIEdgeInsets = .init(top: 0, left: 16, bottom: 0, right: 16) } + + // MARK: ColorStyle + + public enum ColorStyle { + case light + case dark + + var foregroundColor: UIColor { + switch self { + case .light: return .uberButtonBackground + case .dark: return .uberButtonForeground + } + } + + func backgroundColor(isHighlighted: Bool) -> UIColor { + switch self { + case .light: return isHighlighted ? .uberButtonHighlightedLightBackground : .uberButtonForeground + case .dark: return isHighlighted ? .uberButtonHighlightedDarkBackground : .uberButtonBackground + } + } + } } @available(iOS 15, *) extension UIButton.Configuration { - static func uber(title: String? = nil, + static func uber(colorStyle: UberButton.ColorStyle = .dark, + title: AttributedString? = nil, image: UIImage? = nil, - isHighlighted: Bool = false) -> UIButton.Configuration { + isHighlighted: Bool = false, + imagePlacement: NSDirectionalRectEdge = .leading) -> UIButton.Configuration { var style: UIButton.Configuration = .plain() // Background Color var background = style.background - background.backgroundColor = isHighlighted ? .uberButtonHighlightedBackground : .uberButtonBackground + background.backgroundColor = colorStyle.backgroundColor(isHighlighted: isHighlighted) style.background = background // Image style.image = image style.imagePadding = 12.0 + style.imagePlacement = imagePlacement - // Text - style.title = title - style.titleAlignment = .trailing - style.baseForegroundColor = .uberButtonForeground + // Title + style.attributedTitle = title + style.baseForegroundColor = colorStyle.foregroundColor return style } } - - -/// Base class for Uber buttons that sets up colors and some constraints. -open class UberButton_DEPRECATED: UIButton { - public let cornerRadius: CGFloat = 8 - public let horizontalEdgePadding: CGFloat = 16 - public let imageLabelPadding: CGFloat = 8 - public let verticalPadding: CGFloat = 10 - - public let uberImageView: UIImageView = UIImageView() - public let uberTitleLabel: UILabel = UILabel() - - public var colorStyle: UberButtonColorStyle = .black { - didSet { - colorStyleDidUpdate(colorStyle) - } - } - - override open var isHighlighted: Bool { - didSet { - updateColors(isHighlighted) - } - } - - override public init(frame: CGRect) { - super.init(frame: frame) - setup() - colorStyleDidUpdate(.black) - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setup() - colorStyleDidUpdate(.black) - } - - /** - Function responsible for the initial setup of the button. - Calls addSubviews(), setContent(), and setConstraints() - */ - open func setup() { - addSubviews() - setContent() - setConstraints() - } - - /** - Function responsible for adding all the subviews to the button. Subclasses - should override this method and add any necessary subviews. - */ - open func addSubviews() { - addSubview(uberImageView) - addSubview(uberTitleLabel) - } - - /** - Function responsible for updating content on the button. Subclasses should - override and do any necessary view setup - */ - open func setContent() { - clipsToBounds = true - layer.cornerRadius = cornerRadius - } - - /** - Function responsible for adding autolayout constriants on the button. Subclasses - should override and add any additional autolayout constraints - */ - open func setConstraints() { - - uberTitleLabel.translatesAutoresizingMaskIntoConstraints = false - uberImageView.translatesAutoresizingMaskIntoConstraints = false - - let views = ["imageView": uberImageView, "titleLabel": uberTitleLabel] - let metrics = ["edgePadding": horizontalEdgePadding, "verticalPadding": verticalPadding, "imageLabelPadding": imageLabelPadding] - - let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-edgePadding-[imageView]-imageLabelPadding-[titleLabel]-(edgePadding)-|", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: metrics, views: views) - let verticalContraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-verticalPadding-[imageView]-verticalPadding-|", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: metrics, views: views) - - addConstraints(horizontalConstraints) - addConstraints(verticalContraints) - } - - override open func sizeThatFits(_ size: CGSize) -> CGSize { - let logoSize = uberImageView.image?.size ?? CGSize.zero - let titleSize = uberTitleLabel.intrinsicContentSize - - let width: CGFloat = 4*horizontalEdgePadding + imageLabelPadding + logoSize.width + titleSize.width - let height: CGFloat = 2*verticalPadding + max(logoSize.height, titleSize.height) - - return CGSize(width: width, height: height) - } - - open func colorStyleDidUpdate(_ style: UberButtonColorStyle) { - switch colorStyle { - case .black: - backgroundColor = ColorUtil.colorForUberButtonColor(.uberBlack) - uberTitleLabel.textColor = ColorUtil.colorForUberButtonColor(.uberWhite) - uberImageView.tintColor = ColorUtil.colorForUberButtonColor(.uberWhite) - case .white : - backgroundColor = ColorUtil.colorForUberButtonColor(.uberWhite) - uberTitleLabel.textColor = ColorUtil.colorForUberButtonColor(.uberBlack) - uberImageView.tintColor = ColorUtil.colorForUberButtonColor(.uberBlack) - } - } - - // Mark: Private Interface - - private func updateColors(_ highlighted : Bool) { - var color: UberButtonColor - switch colorStyle { - case .black: - color = highlighted ? .blackHighlighted : .uberBlack - case .white: - color = highlighted ? .whiteHighlighted : .uberWhite - } - backgroundColor = ColorUtil.colorForUberButtonColor(color) - } -} - -public enum UberButtonColor: Int { - case uberBlack - case uberWhite - case blackHighlighted - case whiteHighlighted -} - -public enum UberButtonColorStyle: Int { - case black - case white -} - -public class ColorUtil { - public static func colorForUberButtonColor(_ color: UberButtonColor) -> UIColor { - let hexCode = hexCodeFromColor(color) - let scanner = Scanner(string: hexCode) - var color: UInt32 = 0 - scanner.scanHexInt32(&color) - - let mask = 0x000000FF - - let redValue = CGFloat(Int(color >> 16)&mask)/255.0 - let greenValue = CGFloat(Int(color >> 8)&mask)/255.0 - let blueValue = CGFloat(Int(color)&mask)/255.0 - - return UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: 1.0) - } - - private static func hexCodeFromColor(_ color: UberButtonColor) -> String { - switch color { - case .uberBlack: - return "000000" - case .uberWhite: - return "FFFFFF" - case .blackHighlighted: - return "282727" - case .whiteHighlighted: - return "E5E5E4" - } - } -} diff --git a/Sources/UberRides/RideRequestButton.swift b/Sources/UberRides/RideRequestButton.swift index 09a93e6..efb7ebc 100644 --- a/Sources/UberRides/RideRequestButton.swift +++ b/Sources/UberRides/RideRequestButton.swift @@ -26,9 +26,7 @@ import UIKit import CoreLocation import UberCore -/** - * Protocol to listen to request button events, such as loading button content - */ +/// A protocol used to response to Uber RideRequestButton events public protocol RideRequestButtonDelegate { /** The button finished loading ride information successfully. @@ -46,8 +44,10 @@ public protocol RideRequestButtonDelegate { func rideRequestButton(_ button: RideRequestButton, didReceiveError error: UberError) } -/// RequestButton implements a button on the touch screen to request a ride. -public class RideRequestButton: UberButton_DEPRECATED { +public class RideRequestButton: UberButton { + + // MARK: Public Properties + /// Delegate is informed of events that occur with request button. public var delegate: RideRequestButtonDelegate? @@ -60,223 +60,44 @@ public class RideRequestButton: UberButton_DEPRECATED { /// The RidesClient used for retrieving metadata for the button. public var client: RidesClient? - static let sourceString = "button" + // MARK: Internal Properties - var metadata: ButtonMetadata = ButtonMetadata() - var uberMetadataLabel: UILabel = UILabel() - - private let opticalCorrection: CGFloat = 1.0 - - /** - Initializer to use in storyboard. Must call setRidesClient for request button to show metadata. - requestBehavior defaults to DeeplinkRequestingBehavior - rideParameters defaults to RideParameters with pickup location set to current location - */ - required public init?(coder aDecoder: NSCoder) { - requestBehavior = DeeplinkRequestingBehavior() - rideParameters = RideParametersBuilder().build() - super.init(coder: aDecoder) - } - - /** - The Request button initializer. - - - parameter client: The RidesClient to use for getting button metadata - - parameter rideParameters: The RideParameters for this button. These parameters are used to request a ride when the button is tapped. - - parameter requestingBehavior: The RideRequesting object to use for requesting a ride. - - - returns: An initialized RideRequestButton - */ - public init(client: RidesClient, rideParameters: RideParameters, requestingBehavior: RideRequesting) { - requestBehavior = requestingBehavior - self.rideParameters = rideParameters - super.init(frame: CGRect.zero) - self.client = client - } - - /** - The Request button initializer. - Uses a default RidesClient - - - parameter rideParameters: The RideParameters for this button. These parameters are used to request a ride when the button is tapped. - - parameter requestingBehavior: The RideRequesting object to use for requesting a ride. - - - returns: An initialized RideRequestButton - */ - public convenience init(rideParameters: RideParameters, requestingBehavior: RideRequesting) { - self.init(client: RidesClient(), rideParameters: rideParameters, requestingBehavior: requestingBehavior) - } - - /** - The RideRequestButton initializer. - Uses DeeplinkRequestingBehavior by default - Defaults to using the current location for pickup - - - parameter client: The RidesClient to use for getting button metadata - - - returns: An initialized RideRequestButton - */ - public convenience init(client: RidesClient) { - self.init(client: client, rideParameters: RideParametersBuilder().build(), requestingBehavior: DeeplinkRequestingBehavior()) - } - - /** - The RideRequestButton initializer. Creates a request button that uses the Deeplink - Requesting behavior & the provided RidesParameters - Uses a default RidesClient - - - parameter rideParameters: The RideParameters for this button. These parameters are used to request a ride when the button is tapped. - - - returns: An initialized RideRequestButton - */ - public convenience init(rideParameters: RideParameters) { - self.init(client: RidesClient(), rideParameters: rideParameters, requestingBehavior: DeeplinkRequestingBehavior()) - } + static let sourceString = "button" - /** - The RideRequestButton initializer. - Defaults to using the current location for pickup - Uses a default RidesClient - - - parameter requestingBehavior: The RideRequesting object to use for requesting a ride. - - - returns: An initialized RideRequestButton - */ - public convenience init(requestingBehavior: RideRequesting) { - self.init(client: RidesClient(), rideParameters: RideParametersBuilder().build(), requestingBehavior: requestingBehavior) - } + var metadata = ButtonMetadata() - //Mark: UberButton + // MARK: Private Properties - /** - The Request button initializer. - Defaults to using the current location for pickup - Defaults to DeeplinkRequestingBehavior, which links into the Uber app - Uses a default RidesClient - - - returns: An initialized RideRequestButton - */ - public convenience init() { - self.init(client: RidesClient(), rideParameters: RideParametersBuilder().build(), requestingBehavior: DeeplinkRequestingBehavior()) - } + private var _title: NSAttributedString? = .init(string: "Ride there with Uber") - /** - Setup the RideRequestButton by adding a target to the button and setting the login completion block - */ - override public func setup() { - super.setup() - addTarget(self, action: #selector(uberButtonTapped(_:)), for: .touchUpInside) - sizeToFit() - } + private var _subtitle: NSAttributedString? = nil - /** - Adds the Metadata Label to the button - */ - override public func addSubviews() { - super.addSubviews() - addSubview(uberMetadataLabel) - } + private lazy var _image: UIImage? = image(name: "Badge") - /** - Updates the content of the button. Sets the image icon and font, as well as the text - */ - override public func setContent() { - super.setContent() - - uberMetadataLabel.numberOfLines = 2 - uberMetadataLabel.textColor = colorStyle == .black ? ColorUtil.colorForUberButtonColor(.uberWhite) : ColorUtil.colorForUberButtonColor(.uberBlack) - uberMetadataLabel.textAlignment = .right - - uberTitleLabel.font = UIFont(name: "HelveticaNeue-Medium", size: 15) ?? UIFont.systemFont(ofSize: 16) - - let titleText = NSLocalizedString("Ride there with Uber", bundle: Bundle(for: type(of: self)), comment: "Request button description") - uberTitleLabel.text = titleText - - let logo = getImage(name: "Badge") - uberImageView.image = logo - uberImageView.contentMode = .center - } + private let opticalCorrection: CGFloat = 1.0 - /** - Adds the layout constraints for the ride request button. - */ - override public func setConstraints() { - - uberTitleLabel.translatesAutoresizingMaskIntoConstraints = false - uberImageView.translatesAutoresizingMaskIntoConstraints = false - uberMetadataLabel.translatesAutoresizingMaskIntoConstraints = false - - let views = ["image": uberImageView, "titleLabel": uberTitleLabel, "metadataLabel": uberMetadataLabel] - let metrics = ["edgePadding": horizontalEdgePadding, "verticalPadding": verticalPadding, "imageLabelPadding": imageLabelPadding, "middlePadding": horizontalEdgePadding*2] - - uberImageView.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .horizontal) - uberTitleLabel.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .horizontal) - uberTitleLabel.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .vertical) - uberMetadataLabel.setContentHuggingPriority(UILayoutPriority.defaultLow, for: .horizontal) - - let horizontalConstraints: [NSLayoutConstraint] = NSLayoutConstraint.constraints(withVisualFormat: "H:|-edgePadding-[image]-imageLabelPadding-[titleLabel]-middlePadding-[metadataLabel]-edgePadding-|", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: metrics, views: views) - let verticalConstraints: [NSLayoutConstraint] = NSLayoutConstraint.constraints(withVisualFormat: "V:|-verticalPadding-[image]-verticalPadding-|", options: .alignAllLeading, metrics: metrics, views: views) - - let titleLabelCenterConstraint = NSLayoutConstraint(item: self, - attribute: .centerY, - relatedBy: .equal, - toItem: uberTitleLabel, - attribute: .centerY, - multiplier: 1.0, - constant: opticalCorrection) - let metadataLabelCenterConstraint = NSLayoutConstraint(item: self, - attribute: .centerY, - relatedBy: .equal, - toItem: uberMetadataLabel, - attribute: .centerY, - multiplier: 1.0, - constant: 0) - let imageViewCenterConstraint = NSLayoutConstraint(item: self, - attribute: .centerY, - relatedBy: .equal, - toItem: uberImageView, - attribute: .centerY, - multiplier: 1.0, - constant: 0) - - addConstraints(horizontalConstraints) - addConstraints(verticalConstraints) - addConstraints([titleLabelCenterConstraint, metadataLabelCenterConstraint, imageViewCenterConstraint]) - } + // MARK: Initializers - override open func colorStyleDidUpdate(_ style: UberButtonColorStyle) { - super.colorStyleDidUpdate(style) - - switch style { - case .black: - uberMetadataLabel.textColor = ColorUtil.colorForUberButtonColor(.uberWhite) - case .white : - uberMetadataLabel.textColor = ColorUtil.colorForUberButtonColor(.uberBlack) - } + public init(client: RidesClient = RidesClient(), + rideParameters: RideParameters = RideParametersBuilder().build(), + requestBehavior: RideRequesting = DeeplinkRequestingBehavior()) { + self.client = client + self.rideParameters = rideParameters + self.requestBehavior = requestBehavior + super.init(frame: CGRect.zero) + configure() } - //Mark: UIView - - override public func sizeThatFits(_ size: CGSize) -> CGSize { - let logoSize = uberImageView.image?.size ?? CGSize.zero - let titleSize = uberTitleLabel.intrinsicContentSize - let metadataSize = uberMetadataLabel.intrinsicContentSize - var width: CGFloat = 4*horizontalEdgePadding + imageLabelPadding + logoSize.width + titleSize.width - var height: CGFloat = 2*verticalPadding + max(logoSize.height, titleSize.height) - - if let _ = metadata.productID { - width += metadataSize.width - height = max(height, metadataSize.height) - } - - return CGSize(width: width, height: height) + required public init?(coder: NSCoder) { + self.client = RidesClient() + self.rideParameters = RideParametersBuilder().build() + self.requestBehavior = DeeplinkRequestingBehavior() + super.init(coder: coder) + configure() } - //Mark: Public Interface + // MARK: Public Methods - /** - Manual refresh for the ride information on the button. The product ID must be set in order to show any metadata. - */ public func loadRideInformation() { guard client != nil else { delegate?.rideRequestButton(self, didReceiveError: createValidationFailedError()) @@ -292,90 +113,45 @@ public class RideRequestButton: UberButton_DEPRECATED { setMetadata() } - //Mark: Internal Interface + // MARK: UberButton - // Initiate deeplink when button is tapped - @objc func uberButtonTapped(_ sender: UIButton) { - rideParameters.source = RideRequestButton.sourceString - requestBehavior.requestRide(parameters: rideParameters) + public override var title: NSAttributedString? { + _title } - //Mark: Private Interface + public override var subtitle: NSAttributedString? { + _subtitle + } - /** - Helper function that sets appropriate attributes on multi-line label. - - - parameter title: The main title of the label. (ex. "3 MINS AWAY" or "Get a Ride") - - parameter subtitle: The subtitle of the label. (ex. "$6-8 for uberX") - - parameter surge: Whether the price estimate should include a surge image. Default false. - */ - private func setMultilineAttributedString(title: String, subtitle: String = "", surge: Bool = false) { - let metadataFont = UIFont(name: "HelveticaNeue-Regular", size: 12) ?? UIFont.systemFont(ofSize: 12) - - let attrString = NSMutableAttributedString(string: title) - - // If there is a price estimate to include, add a new line - if !subtitle.isEmpty { - attrString.append(NSAttributedString(string: "\n")) - - // If the price estimate is higher due to a surge, add the surge icon - if surge == true { - let attachment = getSurgeAttachment() - - // Adjust bounds to center the text attachment - attachment.bounds = CGRect(x: 0, y: metadataFont.descender-opticalCorrection, width: attachment.image?.size.width ?? 0, height: attachment.image!.size.height) - let surgeImage = NSAttributedString(attachment: attachment) - - attrString.append(surgeImage) - attrString.append(NSAttributedString(string: " ")) - - // Adding the text attachment increases the space between lines so set the max line height - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.alignment = .right - paragraphStyle.maximumLineHeight = 16 - attrString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attrString.length)) - } - - attrString.append(NSAttributedString(string: "\(subtitle)")) - } - - attrString.addAttribute(NSAttributedString.Key.font, value: metadataFont, range: (attrString.string as NSString).range(of: title)) - attrString.addAttribute(NSAttributedString.Key.font, value: metadataFont, range: (attrString.string as NSString).range(of: subtitle)) - - if attrString.string.isEmpty { - uberTitleLabel.text = NSLocalizedString("Ride there with Uber", bundle: Bundle(for: type(of: self)), comment: "Request button description") - } else { - uberTitleLabel.text = NSLocalizedString("Get a ride", bundle: Bundle(for: type(of: self)), comment: "Request button shorter description") - } - - uberMetadataLabel.attributedText = attrString + public override var image: UIImage? { + _image } - private func getSurgeAttachment() -> NSTextAttachment { - let attachment = NSTextAttachment() - - switch colorStyle { - case .black: - attachment.image = getImage(name: "Surge-WhiteOutline") - case .white: - attachment.image = getImage(name: "Surge-BlackOutline") - } + public override var horizontalAlignment: UIControl.ContentHorizontalAlignment { + .leading + } - return attachment + // MARK: Private + + private func configure() { + addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } - + + @objc func buttonTapped(_ sender: UIButton) { + rideParameters.source = RideRequestButton.sourceString + requestBehavior.requestRide(parameters: rideParameters) + } + private func createValidationFailedError() -> UberError { return UberError(status: 422, code: "validation_failed", title: "Invalid Request") } - /** - Sets metadata on button by fetching all required information. - */ private func setMetadata() { - /** - * These are all required for the following requests. - */ - guard let client = client, let pickupLatitude = metadata.pickupLatitude, let pickupLongitude = metadata.pickupLongitude, let productID = metadata.productID else { + + guard let client = client, + let pickupLatitude = metadata.pickupLatitude, + let pickupLongitude = metadata.pickupLongitude, + let productID = metadata.productID else { delegate?.rideRequestButton(self, didReceiveError: createValidationFailedError()) return } @@ -464,10 +240,69 @@ public class RideRequestButton: UberButton_DEPRECATED { client.fetchTimeEstimates(pickupLocation: pickupLocation, productID:productID, completion: timeEstimatesCompletion) } - // get image from media directory - private func getImage(name: String) -> UIImage { - let image = UIImage(named: name, in: .resource(for: RideRequestButton.self), compatibleWith: nil) - return image! + /** + Helper function that sets appropriate attributes on multi-line label. + + - parameter title: The main title of the label. (ex. "3 MINS AWAY" or "Get a Ride") + - parameter subtitle: The subtitle of the label. (ex. "$6-8 for uberX") + - parameter surge: Whether the price estimate should include a surge image. Default false. + */ + private func setMultilineAttributedString(title: String, subtitle: String = "", surge: Bool = false) { + let metadataFont = UIFont(name: "HelveticaNeue-Regular", size: 12) ?? UIFont.systemFont(ofSize: 12) + + let attrString = NSMutableAttributedString(string: title) + + // If there is a price estimate to include, add a new line + if !subtitle.isEmpty { + attrString.append(NSAttributedString(string: "\n")) + + // If the price estimate is higher due to a surge, add the surge icon + if surge == true { + let attachment = getSurgeAttachment() + + // Adjust bounds to center the text attachment + attachment.bounds = CGRect(x: 0, y: metadataFont.descender-opticalCorrection, width: attachment.image?.size.width ?? 0, height: attachment.image!.size.height) + let surgeImage = NSAttributedString(attachment: attachment) + + attrString.append(surgeImage) + attrString.append(NSAttributedString(string: " ")) + + // Adding the text attachment increases the space between lines so set the max line height + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.alignment = .right + paragraphStyle.maximumLineHeight = 16 + attrString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attrString.length)) + } + + attrString.append(NSAttributedString(string: "\(subtitle)")) + } + + attrString.addAttribute(NSAttributedString.Key.font, value: metadataFont, range: (attrString.string as NSString).range(of: title)) + attrString.addAttribute(NSAttributedString.Key.font, value: metadataFont, range: (attrString.string as NSString).range(of: subtitle)) + + if attrString.string.isEmpty { + _title = NSAttributedString( + string: NSLocalizedString("Ride there with Uber", bundle: Bundle(for: type(of: self)), comment: "Request button description") + ) + } else { + _title = NSAttributedString( + string: NSLocalizedString("Get a ride", bundle: Bundle(for: type(of: self)), comment: "Request button shorter description") + ) + } + + _subtitle = attrString + + update() + } + + private func getSurgeAttachment() -> NSTextAttachment { + let attachment = NSTextAttachment() + attachment.image = image(name: "Surge-WhiteOutline") + return attachment + } + + private func image(name: String) -> UIImage? { + UIImage(named: name, in: Bundle.module, compatibleWith: nil) } } diff --git a/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj b/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj index 2034cee..b41430d 100644 --- a/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj +++ b/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + B206A5622C6BEF6400F51AB0 /* AuthResponseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B206A5612C6BEF6400F51AB0 /* AuthResponseView.swift */; }; B21C2DD82C6574D300337E93 /* DeeplinkManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21C2DD62C6574D300337E93 /* DeeplinkManagerTests.swift */; }; B21C2DD92C6574D300337E93 /* BaseDeeplinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21C2DD72C6574D300337E93 /* BaseDeeplinkTests.swift */; }; B21C2E072C6574D900337E93 /* getHistory.json in Resources */ = {isa = PBXBuildFile; fileRef = B21C2DDA2C6574D900337E93 /* getHistory.json */; }; @@ -90,6 +91,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + B206A5612C6BEF6400F51AB0 /* AuthResponseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthResponseView.swift; sourceTree = ""; }; B21C2DCC2C656BFC00337E93 /* UberSDK.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UberSDK.xctestplan; sourceTree = ""; }; B21C2DD62C6574D300337E93 /* DeeplinkManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkManagerTests.swift; sourceTree = ""; }; B21C2DD72C6574D300337E93 /* BaseDeeplinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDeeplinkTests.swift; sourceTree = ""; }; @@ -301,6 +303,7 @@ B29303D62B891D3E00D28BAA /* SelectionView.swift */, B29303D82B89408D00D28BAA /* Helpers.swift */, B2D7AE1D2B979EDA007F03FB /* ContentView.swift */, + B206A5612C6BEF6400F51AB0 /* AuthResponseView.swift */, B25D30892BD03F50008A67CF /* UberButtonView.swift */, B2D096992B98012A0093B510 /* SelectionOptions.swift */, B2D7AE1F2B979EDB007F03FB /* Assets.xcassets */, @@ -470,6 +473,7 @@ buildActionMask = 2147483647; files = ( B2D7AE1E2B979EDA007F03FB /* ContentView.swift in Sources */, + B206A5622C6BEF6400F51AB0 /* AuthResponseView.swift in Sources */, B2D7AE1C2B979EDA007F03FB /* UberSDKApp.swift in Sources */, B2D7AE492B979F58007F03FB /* SelectionView.swift in Sources */, B2D0969A2B98012A0093B510 /* SelectionOptions.swift in Sources */, diff --git a/examples/UberSDK/UberSDK/AuthResponseView.swift b/examples/UberSDK/UberSDK/AuthResponseView.swift new file mode 100644 index 0000000..8d139e2 --- /dev/null +++ b/examples/UberSDK/UberSDK/AuthResponseView.swift @@ -0,0 +1,46 @@ +// +// Copyright © Uber Technologies, Inc. All rights reserved. +// + + +import SwiftUI + +struct AuthReponse: Identifiable { + var id: String { value } + var value: String +} + +struct AuthResponseView: View { + + @Binding + private var response: AuthReponse? + + init(response: Binding) { + self._response = response + } + + var body: some View { + VStack { + HStack { + Spacer() + Button( + action: { response = nil }, + label: { + Image(systemName: "xmark.circle.fill") + .resizable() + .frame(width: 30, height: 30) + .tint(.secondary) + } + ) + .padding() + } + + ScrollView(.horizontal) { + Text(response?.value ?? "") + .textSelection(.enabled) + .padding() + } + } + Spacer() + } +} diff --git a/examples/UberSDK/UberSDK/ContentView.swift b/examples/UberSDK/UberSDK/ContentView.swift index b091041..74d4943 100644 --- a/examples/UberSDK/UberSDK/ContentView.swift +++ b/examples/UberSDK/UberSDK/ContentView.swift @@ -32,7 +32,7 @@ final class Content { var shouldForceLogin: Bool = false var shouldForceConsent: Bool = false var isPrefillExpanded: Bool = false - var response: String? + var response: AuthReponse? var prefillBuilder = PrefillBuilder() func login() { @@ -61,11 +61,14 @@ final class Content { prefill: isPrefillExpanded ? prefillBuilder.prefill : nil ), completion: { result in - switch result { - case .success(let client): - self.response = "\(client)" - case .failure(let error): - self.response = error.localizedDescription + // Slight delay to allow for ASWebAuthenticationSession dismissal + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + switch result { + case .success(let client): + self.response = AuthReponse(value: "\(client)") + case .failure(let error): + self.response = AuthReponse(value: error.localizedDescription) + } } } ) @@ -110,12 +113,8 @@ struct ContentView: View { var body: some View { NavigationStack { - VStack(spacing: 0) { - exampleList - Divider() - responseSection - } - .navigationTitle("Uber iOS SDK") + exampleList + .navigationTitle("Uber iOS SDK") } .onOpenURL { content.openUrl($0) } .sheet(item: $content.selection, content: { item in @@ -136,25 +135,15 @@ struct ContentView: View { EmptyView() } }) + .sheet(item: $content.response) { _ in + AuthResponseView(response: $content.response) + .presentationDetents([.height(200)]) + } } // MARK: Subviews - private var responseSection: some View { - VStack { - Text("Response:") - .font(.title3) - .padding() - .frame(maxWidth: .infinity, alignment: .leading) - ScrollView(.horizontal) { - Text(content.response ?? "") - .textSelection(.enabled) - .padding() - } - } - .frame(maxWidth: .infinity, minHeight: 150) - } - + @ViewBuilder private var exampleList: some View { List { Section( @@ -165,16 +154,13 @@ struct ContentView: View { "Uber Button", content: { uberButtonSection } ) + Section( + "Request a Ride Button", + content: { rideRequestButtonSection } + ) } } - - @ViewBuilder - private var uberButtonSection: some View { - UberButtonView() - .padding() - } - @ViewBuilder private var loginSection: some View { @@ -186,42 +172,30 @@ struct ContentView: View { toggleRow(.prefill, value: $content.isPrefillExpanded) if content.isPrefillExpanded { - row( - content: { - TextField( - Content.Item.firstName.rawValue, - text: $content.prefillBuilder.firstName - ) - }, - showDisclosureIndicator: false - ) - row( - content: { - TextField( - Content.Item.lastName.rawValue, - text: $content.prefillBuilder.lastName - ) - }, - showDisclosureIndicator: false - ) - row( - content: { - TextField( - Content.Item.email.rawValue, - text: $content.prefillBuilder.email - ) - }, - showDisclosureIndicator: false - ) - row( - content: { - TextField( - Content.Item.phoneNumber.rawValue, - text: $content.prefillBuilder.phoneNumber - ) - }, - showDisclosureIndicator: false - ) + row { + TextField( + Content.Item.firstName.rawValue, + text: $content.prefillBuilder.firstName + ) + } + row { + TextField( + Content.Item.lastName.rawValue, + text: $content.prefillBuilder.lastName + ) + } + row { + TextField( + Content.Item.email.rawValue, + text: $content.prefillBuilder.email + ) + } + row { + TextField( + Content.Item.phoneNumber.rawValue, + text: $content.prefillBuilder.phoneNumber + ) + } } Button( @@ -234,9 +208,21 @@ struct ContentView: View { .padding() } + @ViewBuilder + private var uberButtonSection: some View { + UberButtonView() + .listRowInsets(EdgeInsets()) + } + + @ViewBuilder + private var rideRequestButtonSection: some View { + RideRequestButtonView() + .listRowInsets(EdgeInsets()) + } + private func row(item: Content.Item? = nil, @ViewBuilder content: () -> (some View), - showDisclosureIndicator: Bool = true, + showDisclosureIndicator: Bool = false, tapHandler: (() -> Void)? = nil) -> some View { Button( action: { tapHandler?() }, diff --git a/examples/UberSDK/UberSDK/UberButtonView.swift b/examples/UberSDK/UberSDK/UberButtonView.swift index 7fc069f..cae1cf8 100644 --- a/examples/UberSDK/UberSDK/UberButtonView.swift +++ b/examples/UberSDK/UberSDK/UberButtonView.swift @@ -3,10 +3,12 @@ // +import CoreLocation import Foundation import SwiftUI import UberAuth import UberCore +import UberRides struct UberButtonView: UIViewRepresentable { func makeUIView(context: Context) -> UberCore.UberButton { @@ -16,9 +18,52 @@ struct UberButtonView: UIViewRepresentable { func updateUIView(_ uiView: UberCore.UberButton, context: Context) {} } +struct RideRequestButtonView: UIViewRepresentable { + + private let delegate = RideRequestViewDelegate() + + private let sampleRideParameters = RideParametersBuilder( + productID: "a1111c8c-c720-46c3-8534-2fcdd730040d", + pickupLocation: CLLocation(latitude: 37.770, longitude: -122.466), + pickupNickname: "California Academy of Science", + pickupAddress: "55 Music Concourse Drive, San Francisco", + dropoffLocation: CLLocation(latitude: 37.791, longitude: -122.405), + dropoffNickname: "Pier 39", + dropoffAddress: "Beach Street & The Embarcadero, San Francisco", + paymentMethod: "paymentMethod", + surgeConfirmationID: "surgeConfirm" + ) + .build() + + + func makeUIView(context: Context) -> UberCore.UberButton { + let button = RideRequestButton(rideParameters: sampleRideParameters) + button.delegate = delegate + return button + } + + func updateUIView(_ uiView: UberCore.UberButton, context: Context) { + (uiView as? RideRequestButton)?.loadRideInformation() + } +} + +fileprivate final class RideRequestViewDelegate: RideRequestButtonDelegate { + + func rideRequestButtonDidLoadRideInformation(_ button: UberRides.RideRequestButton) { + + } + + func rideRequestButton(_ button: UberRides.RideRequestButton, didReceiveError error: UberRides.UberError) { + + } +} + #Preview { VStack { UberButtonView() .padding() + + RideRequestButtonView() + .padding() } } diff --git a/examples/UberSDK/UberSDKTests/UberRides/RequestButtonTests.swift b/examples/UberSDK/UberSDKTests/UberRides/RequestButtonTests.swift index b8f66f0..5b3cdb3 100644 --- a/examples/UberSDK/UberSDKTests/UberRides/RequestButtonTests.swift +++ b/examples/UberSDK/UberSDKTests/UberRides/RequestButtonTests.swift @@ -28,7 +28,7 @@ import OHHTTPStubsSwift import CoreLocation import WebKit import UberAuth -import UberCore +@testable import UberCore @testable import UberRides class RequestButtonTests: XCTestCase { @@ -51,7 +51,7 @@ class RequestButtonTests: XCTestCase { */ func testInitRequestButtonDefaultText() { button = RideRequestButton(client: client) - XCTAssertEqual(button.uberTitleLabel.text!, "Ride there with Uber") + XCTAssertEqual(button.titleLabel!.text!, "Ride there with Uber") } func testCorrectSource_whenRideRequestViewRequestingBehavior() { @@ -88,7 +88,7 @@ class RequestButtonTests: XCTestCase { presentingViewController: baseViewController, accessTokenIdentifier: testIdentifier ) - let button = RideRequestButton(rideParameters: RideParametersBuilder().build(), requestingBehavior: requestBehavior) + let button = RideRequestButton(rideParameters: RideParametersBuilder().build(), requestBehavior: requestBehavior) let rideRequestVC = RideRequestViewController( rideParameters: RideParametersBuilder().build(), @@ -101,7 +101,7 @@ class RequestButtonTests: XCTestCase { requestBehavior.modalRideRequestViewController.rideRequestViewController = rideRequestVC - button.uberButtonTapped(button) + button.buttonTapped(button) waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) @@ -134,9 +134,9 @@ class RequestButtonTests: XCTestCase { } let requestBehavior = DeeplinkRequestingBehaviorMock(testClosure: expectationClosure) - let button = RideRequestButton(rideParameters: RideParametersBuilder().build(), requestingBehavior: requestBehavior) + let button = RideRequestButton(rideParameters: RideParametersBuilder().build(), requestBehavior: requestBehavior) - button.uberButtonTapped(button) + button.buttonTapped(button) waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) @@ -150,7 +150,7 @@ class RequestButtonTests: XCTestCase { let builder = RideParametersBuilder() builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(button.metadata.productID, productID) } @@ -163,7 +163,7 @@ class RequestButtonTests: XCTestCase { let builder = RideParametersBuilder() builder.pickupLocation = location let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(button.metadata.pickupLatitude, pickupLat) XCTAssertEqual(button.metadata.pickupLongitude, pickupLong) @@ -177,7 +177,7 @@ class RequestButtonTests: XCTestCase { let builder = RideParametersBuilder() builder.dropoffLocation = location let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(button.metadata.dropoffLatitude, dropoffLat) XCTAssertEqual(button.metadata.dropoffLongitude, dropoffLong) @@ -198,14 +198,14 @@ class RequestButtonTests: XCTestCase { builder.pickupLocation = location builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") - XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY") + XCTAssertEqual(self.button.titleLabel!.text!, "Get a ride") + XCTAssertEqual(self.button.secondaryLabel.text!, "4 MINS AWAY") }) } @@ -232,14 +232,14 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") - XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY\n$15 for uberX") + XCTAssertEqual(self.button.titleLabel!.text!, "Get a ride") + XCTAssertEqual(self.button.secondaryLabel.text!, "4 MINS AWAY\n$15 for uberX") }) } @@ -264,14 +264,14 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") - XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY") + XCTAssertEqual(self.button.titleLabel!.text!, "Get a ride") + XCTAssertEqual(self.button.secondaryLabel.text!, "4 MINS AWAY") XCTAssertEqual(self.rideButtonError.code, "price_estimate_error") }) } @@ -297,14 +297,14 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") - XCTAssertEqual(self.button.uberMetadataLabel.text!, "$15 for uberX") + XCTAssertEqual(self.button.titleLabel!.text!, "Get a ride") + XCTAssertEqual(self.button.subtitle!.string, "$15 for uberX") XCTAssertEqual(self.rideButtonError.code, "time_estimate_error") }) } @@ -331,14 +331,14 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") - XCTAssertEqual(self.button.uberMetadataLabel.text!, "$15 for uberX") + XCTAssertEqual(self.button.titleLabel!.text!, "Get a ride") + XCTAssertEqual(self.button.secondaryLabel.text!, "$15 for uberX") }) } @@ -362,14 +362,14 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") - XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY") + XCTAssertEqual(self.button.titleLabel!.text!, "Get a ride") + XCTAssertEqual(self.button.secondaryLabel.text!, "4 MINS AWAY") }) } @@ -393,14 +393,14 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) - XCTAssertEqual(self.button.uberTitleLabel.text!, "Ride there with Uber") - XCTAssertNil(self.button.uberMetadataLabel.text) + XCTAssertEqual(self.button.titleLabel!.text!, "Ride there with Uber") + XCTAssertNil(self.button.secondaryLabel.text) }) } @@ -413,7 +413,7 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.client = nil button.loadRideInformation() @@ -436,7 +436,7 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() @@ -458,7 +458,7 @@ class RequestButtonTests: XCTestCase { builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() @@ -483,10 +483,10 @@ class RequestButtonTests: XCTestCase { builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation let rideParams = builder.build() - button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) + button = RideRequestButton(client: client, rideParameters:rideParams, requestBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() - XCTAssertEqual(self.button.uberTitleLabel.text!, "Ride there with Uber") + XCTAssertEqual(self.button.titleLabel!.text!, "Ride there with Uber") } } From 2a7699903565ea82271fccfc205adb5860f3d6ca Mon Sep 17 00:00:00 2001 From: mohssenfathi Date: Tue, 27 Aug 2024 09:55:59 -0700 Subject: [PATCH 2/2] Make RideRequestButtonDelegate weak --- Sources/UberRides/RideRequestButton.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/UberRides/RideRequestButton.swift b/Sources/UberRides/RideRequestButton.swift index efb7ebc..97728b2 100644 --- a/Sources/UberRides/RideRequestButton.swift +++ b/Sources/UberRides/RideRequestButton.swift @@ -27,7 +27,7 @@ import CoreLocation import UberCore /// A protocol used to response to Uber RideRequestButton events -public protocol RideRequestButtonDelegate { +public protocol RideRequestButtonDelegate: AnyObject { /** The button finished loading ride information successfully. @@ -49,7 +49,7 @@ public class RideRequestButton: UberButton { // MARK: Public Properties /// Delegate is informed of events that occur with request button. - public var delegate: RideRequestButtonDelegate? + public weak var delegate: RideRequestButtonDelegate? /// The RideParameters object this button will use to make a request public var rideParameters: RideParameters