From 66d16aaa046efba416b9d7201f3780f3dc881009 Mon Sep 17 00:00:00 2001 From: Edward Jiang Date: Fri, 8 Dec 2017 14:05:09 -0800 Subject: [PATCH] Make models optional (#213) * Make models optional. Update sample apps and CHANGELOG * add more color to changelog * Add empty string for when no detail text --- CHANGELOG.md | 7 + ...zationCodeGrantExampleViewController.swift | 5 +- .../ImplicitGrantExampleViewController.swift | 17 +- source/UberCore/UBSDKConstants.h | 4 - source/UberCore/UBSDKConstants.m | 4 - source/UberRides/Model/DistanceEstimate.swift | 34 +++- source/UberRides/Model/Driver.swift | 25 ++- source/UberRides/Model/PaymentMethod.swift | 8 +- source/UberRides/Model/Place.swift | 4 +- source/UberRides/Model/PriceEstimate.swift | 71 ++++--- source/UberRides/Model/Product.swift | 180 +++++++++++++----- source/UberRides/Model/Ride.swift | 34 +++- source/UberRides/Model/RideCharge.swift | 24 ++- source/UberRides/Model/RideEstimate.swift | 8 +- source/UberRides/Model/RideMap.swift | 9 +- source/UberRides/Model/RideParameters.swift | 2 +- source/UberRides/Model/RideReceipt.swift | 53 ++++-- .../UberRides/Model/RideRequestLocation.swift | 52 +++-- source/UberRides/Model/TimeEstimate.swift | 21 +- source/UberRides/Model/UpfrontFare.swift | 52 +++-- source/UberRides/Model/UserActivity.swift | 93 ++++++--- source/UberRides/Model/UserProfile.swift | 10 +- source/UberRides/Model/Vehicle.swift | 12 +- source/UberRides/RideRequestButton.swift | 3 +- .../UberRidesTests/ObjectMappingTests.swift | 44 ++--- 25 files changed, 532 insertions(+), 244 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57976543..28667151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## [0.9.0] TBD + +### Changes + +* All model properties are now Optionals. + * In Objective-C, `Double`, `Int`, and `Bool` are represented by NSNumber `boolValue`, `intValue`, and `doubleValue`. The `UBSDKDistanceUnavailable`, `UBSDKEstimateUnavailable`, and `UBSDKBearingUnavailable` constants are now removed. + ## [0.8.1] 2018-01-31 ### Fixes diff --git a/examples/Swift SDK/Swift SDK/AuthorizationCodeGrantExampleViewController.swift b/examples/Swift SDK/Swift SDK/AuthorizationCodeGrantExampleViewController.swift index 45c3da5a..369acde6 100644 --- a/examples/Swift SDK/Swift SDK/AuthorizationCodeGrantExampleViewController.swift +++ b/examples/Swift SDK/Swift SDK/AuthorizationCodeGrantExampleViewController.swift @@ -130,10 +130,11 @@ class AuthorizationCodeGrantExampleViewController: AuthorizationBaseViewControll ridesClient.requestRide(parameters: builder.build(), completion: { ride, response in DispatchQueue.main.async(execute: { self.checkError(response) - if let ride = ride { + if let ride = ride, + let requestID = ride.requestID { self.statusLabel.text = "Processing" - self.updateRideStatus(ride.requestID, index: 0) + self.updateRideStatus(requestID, index: 0) } else { self.requestButton.isEnabled = true } diff --git a/examples/Swift SDK/Swift SDK/ImplicitGrantExampleViewController.swift b/examples/Swift SDK/Swift SDK/ImplicitGrantExampleViewController.swift index 2c907636..e4ffca92 100644 --- a/examples/Swift SDK/Swift SDK/ImplicitGrantExampleViewController.swift +++ b/examples/Swift SDK/Swift SDK/ImplicitGrantExampleViewController.swift @@ -210,18 +210,21 @@ extension ImplicitGrantExampleViewController: UITableViewDataSource { return cell case HistorySection: let trip = history[indexPath.row] - let startCity = trip.startCity.name - let startTime = trip.startTime - let endTime = trip.endTime + let startCity = trip.startCity?.name ?? "" let cell = tableView.dequeueReusableCell(withIdentifier: HistoryCell) ?? UITableViewCell(style: .default, reuseIdentifier: HistoryCell) cell.textLabel?.text = startCity - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .short - dateFormatter.timeStyle = .short - cell.detailTextLabel?.text = "\(dateFormatter.string(from: startTime)) to \(dateFormatter.string(from: endTime))" + if let startTime = trip.startTime, + let endTime = trip.endTime { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .short + dateFormatter.timeStyle = .short + cell.detailTextLabel?.text = "\(dateFormatter.string(from: startTime)) to \(dateFormatter.string(from: endTime))" + } else { + cell.detailTextLabel?.text = "" + } return cell default: diff --git a/source/UberCore/UBSDKConstants.h b/source/UberCore/UBSDKConstants.h index 5b937ffc..6cd10e3a 100644 --- a/source/UberCore/UBSDKConstants.h +++ b/source/UberCore/UBSDKConstants.h @@ -22,8 +22,4 @@ #ifndef UBSDKConstants_h #define UBSDKConstants_h -FOUNDATION_EXPORT double UBSDKDistanceUnavailable; -FOUNDATION_EXPORT NSInteger UBSDKEstimateUnavailable; -FOUNDATION_EXPORT NSInteger UBSDKBearingUnavailable; - #endif /* UBSDKConstants_h */ diff --git a/source/UberCore/UBSDKConstants.m b/source/UberCore/UBSDKConstants.m index f29af4ed..e044a14e 100644 --- a/source/UberCore/UBSDKConstants.m +++ b/source/UberCore/UBSDKConstants.m @@ -20,7 +20,3 @@ // THE SOFTWARE. #import - -double UBSDKDistanceUnavailable = -INFINITY; -NSInteger UBSDKEstimateUnavailable = NSIntegerMin; -NSInteger UBSDKBearingUnavailable = NSIntegerMin; diff --git a/source/UberRides/Model/DistanceEstimate.swift b/source/UberRides/Model/DistanceEstimate.swift index 1c1d1695..ded486e4 100644 --- a/source/UberRides/Model/DistanceEstimate.swift +++ b/source/UberRides/Model/DistanceEstimate.swift @@ -24,19 +24,39 @@ // MARK: DistanceEstimate +import UberCore + /** * Estimate information on an Uber trip. */ @objc(UBSDKDistanceEstimate) public class DistanceEstimate: NSObject, Codable { /// Expected activity distance. - @objc public private(set) var distance: Double + @nonobjc public private(set) var distance: Double? + + /// Expected activity distance. + @objc(distance) public var objc_distance: NSNumber? { + if let distance = distance { + return NSNumber(value: distance) + } else { + return nil + } + } /// The unit of distance (mile or km). - @objc public private(set) var distanceUnit: String - + @objc public private(set) var distanceUnit: String? + + /// Expected activity duration (in seconds). + @nonobjc public private(set) var duration: Int? + /// Expected activity duration (in seconds). - @objc public private(set) var duration: Int + @objc(duration) public var objc_duration: NSNumber? { + if let duration = duration { + return NSNumber(value: duration) + } else { + return nil + } + } enum CodingKeys: String, CodingKey { case distance = "distance_estimate" @@ -46,8 +66,8 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - distance = try container.decode(Double.self, forKey: .distance) - distanceUnit = try container.decode(String.self, forKey: .distanceUnit) - duration = try container.decode(Int.self, forKey: .duration) + distance = try container.decodeIfPresent(Double.self, forKey: .distance) + distanceUnit = try container.decodeIfPresent(String.self, forKey: .distanceUnit) + duration = try container.decodeIfPresent(Int.self, forKey: .duration) } } diff --git a/source/UberRides/Model/Driver.swift b/source/UberRides/Model/Driver.swift index 83f8808d..e1f90046 100644 --- a/source/UberRides/Model/Driver.swift +++ b/source/UberRides/Model/Driver.swift @@ -30,19 +30,28 @@ @objc(UBSDKDriver) public class Driver: NSObject, Codable { /// The first name of the driver. - @objc public private(set) var name: String + @objc public private(set) var name: String? /// The URL to the photo of the driver. - @objc public private(set) var pictureURL: URL + @objc public private(set) var pictureURL: URL? /// The formatted phone number for calling the driver. - @objc public private(set) var phoneNumber: String + @objc public private(set) var phoneNumber: String? /// The formatted phone number for sending a SMS to the driver. @objc public private(set) var smsNumber: String? /// The driver's star rating out of 5 stars. - @objc public private(set) var rating: Double + @nonobjc public private(set) var rating: Double? + + /// The driver's star rating out of 5 stars. + @objc(rating) public var objc_rating: NSNumber? { + if let rating = rating { + return NSNumber(value: rating) + } else { + return nil + } + } enum CodingKeys: String, CodingKey { case name = "name" @@ -54,10 +63,10 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - name = try container.decode(String.self, forKey: .name) - pictureURL = try container.decode(URL.self, forKey: .pictureURL) - phoneNumber = try container.decode(String.self, forKey: .phoneNumber) + name = try container.decodeIfPresent(String.self, forKey: .name) + pictureURL = try container.decodeIfPresent(URL.self, forKey: .pictureURL) + phoneNumber = try container.decodeIfPresent(String.self, forKey: .phoneNumber) smsNumber = try container.decodeIfPresent(String.self, forKey: .smsNumber) - rating = try container.decode(Double.self, forKey: .rating) + rating = try container.decodeIfPresent(Double.self, forKey: .rating) } } diff --git a/source/UberRides/Model/PaymentMethod.swift b/source/UberRides/Model/PaymentMethod.swift index de3cb431..2f35837d 100644 --- a/source/UberRides/Model/PaymentMethod.swift +++ b/source/UberRides/Model/PaymentMethod.swift @@ -44,10 +44,10 @@ struct PaymentMethods: Codable { @objc public private(set) var paymentDescription: String? /// Unique identifier of the payment method. - @objc public private(set) var methodID: String + @objc public private(set) var methodID: String? /// The type of the payment method. See https://developer.uber.com/docs/v1-payment-methods. - @objc public private(set) var type: String + @objc public private(set) var type: String? enum CodingKeys: String, CodingKey { case paymentDescription = "description" @@ -58,7 +58,7 @@ struct PaymentMethods: Codable { public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) paymentDescription = try container.decodeIfPresent(String.self, forKey: .paymentDescription) - methodID = try container.decode(String.self, forKey: .methodID) - type = try container.decode(String.self, forKey: .type) + methodID = try container.decodeIfPresent(String.self, forKey: .methodID) + type = try container.decodeIfPresent(String.self, forKey: .type) } } diff --git a/source/UberRides/Model/Place.swift b/source/UberRides/Model/Place.swift index fdf37ae2..47884292 100644 --- a/source/UberRides/Model/Place.swift +++ b/source/UberRides/Model/Place.swift @@ -34,10 +34,10 @@ @objc public static let work = "work" /// Fully qualified address of the location. - @objc public private(set) var address: String + @objc public private(set) var address: String? public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - address = try container.decode(String.self, forKey: .address) + address = try container.decodeIfPresent(String.self, forKey: .address) } } diff --git a/source/UberRides/Model/PriceEstimate.swift b/source/UberRides/Model/PriceEstimate.swift index a2283ec9..2eec3c85 100644 --- a/source/UberRides/Model/PriceEstimate.swift +++ b/source/UberRides/Model/PriceEstimate.swift @@ -50,52 +50,77 @@ struct PriceEstimates: Codable { /// Expected activity distance (in miles). @nonobjc public private(set) var distance: Double? - /// Expected activity distance (in miles). -1 if not present. - @objc(distance) public var objc_distance: Double { - return distance ?? UBSDKDistanceUnavailable + /// Expected activity distance (in miles). + @objc(distance) public var objc_distance: NSNumber? { + if let distance = distance { + return NSNumber(value: distance) + } else { + return nil + } } - + /// Expected activity duration (in seconds). @nonobjc public private(set) var duration: Int? - /// Expected activity duration (in seconds). UBSDKEstimateUnavailable if not present. - @objc(duration) public var objc_duration: Int { - return duration ?? UBSDKEstimateUnavailable + /// Expected activity duration (in seconds). + @objc(duration) public var objc_duration: NSNumber? { + if let duration = duration { + return NSNumber(value: duration) + } else { + return nil + } } - + /// A formatted string representing the estimate in local currency. Could be range, single number, or "Metered" for TAXI. @objc public private(set) var estimate: String? - + /// Upper bound of the estimated price. @nonobjc public private(set) var highEstimate: Int? - /// Upper bound of the estimated price. UBSDKEstimateUnavailable if not present. - @objc(highEstimate) public var objc_highEstimate: Int { - return highEstimate ?? UBSDKEstimateUnavailable + /// Upper bound of the estimated price. + @objc(highEstimate) public var objc_highEstimate: NSNumber? { + if let highEstimate = highEstimate { + return NSNumber(value: highEstimate) + } else { + return nil + } } - + /// Lower bound of the estimated price. @nonobjc public private(set) var lowEstimate: Int? - /// Lower bound of the estimated price. UBSDKEstimateUnavailable if not present. - @objc(lowEstimate) public var objc_lowEstimate: Int { - return lowEstimate ?? UBSDKEstimateUnavailable + /// Lower bound of the estimated price. + @objc(lowEstimate) public var objc_lowEstimate: NSNumber? { + if let lowEstimate = lowEstimate { + return NSNumber(value: lowEstimate) + } else { + return nil + } } - + /// Display name of product. Ex: "UberBLACK". @objc public private(set) var name: String? - + /// Unique identifier representing a specific product for a given latitude & longitude. @objc public private(set) var productID: String? - + /// The unique identifier of the surge session for a user. Nil for no surge. @objc public private(set) var surgeConfirmationID: String? - + /// The URL a user must visit to accept surge pricing. @objc public private(set) var surgeConfirmationURL: URL? - + /// Expected surge multiplier (active if surge is greater than 1). - @objc public private(set) var surgeMultiplier: Double + @nonobjc public private(set) var surgeMultiplier: Double? + + /// Expected surge multiplier (active if surge is greater than 1). + @objc(surgeMultiplier) public var objc_surgeMultiplier: NSNumber? { + if let surgeMultiplier = surgeMultiplier { + return NSNumber(value: surgeMultiplier) + } else { + return nil + } + } enum CodingKeys: String, CodingKey { case currencyCode = "currency_code" @@ -123,6 +148,6 @@ struct PriceEstimates: Codable { productID = try container.decodeIfPresent(String.self, forKey: .productID) surgeConfirmationID = try container.decodeIfPresent(String.self, forKey: .surgeConfirmationID) surgeConfirmationURL = try container.decodeIfPresent(URL.self, forKey: .surgeConfirmationURL) - surgeMultiplier = try container.decodeIfPresent(Double.self, forKey: .surgeMultiplier) ?? 1.0 + surgeMultiplier = try container.decodeIfPresent(Double.self, forKey: .surgeMultiplier) } } diff --git a/source/UberRides/Model/Product.swift b/source/UberRides/Model/Product.swift index a139951a..2cbc28a7 100644 --- a/source/UberRides/Model/Product.swift +++ b/source/UberRides/Model/Product.swift @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import UIKit +import UberCore // MARK: Products @@ -44,31 +44,67 @@ struct UberProducts: Codable { */ @objc(UBSDKProduct) public class Product: NSObject, Codable { /// Unique identifier representing a specific product for a given latitude & longitude. - @objc public private(set) var productID: String + @objc public private(set) var productID: String? /// Display name of product. Ex: "UberBLACK". - @objc public private(set) var name: String + @objc public private(set) var name: String? /// Description of product. Ex: "The original Uber". - @objc public private(set) var productDescription: String - + @objc public private(set) var productDescription: String? + + /// Capacity of product. Ex: 4, for a product that fits 4. + @nonobjc public private(set) var capacity: Int? + /// Capacity of product. Ex: 4, for a product that fits 4. - @objc public private(set) var capacity: Int + @objc public var objc_capacity: NSNumber? { + if let capacity = capacity { + return NSNumber(value: capacity) + } else { + return nil + } + } /// Image URL representing the product. - @objc public private(set) var imageURL: URL + @objc public private(set) var imageURL: URL? /// The basic price details. See `PriceDetails` for structure. @objc public private(set) var priceDetails: PriceDetails? - /// Allows users to get upfront fares, instead of time + distance. - @objc public private(set) var upfrontFareEnabled: Bool + /// Specifies whether this product allows users to get upfront fares, instead of time + distance. + @nonobjc public private(set) var upfrontFareEnabled: Bool? + + /// Specifies whether this product allows users to get upfront fares, instead of time + distance. Boolean value. + @objc public var objc_upfrontFareEnabled: NSNumber? { + if let upfrontFareEnabled = upfrontFareEnabled { + return NSNumber(value: upfrontFareEnabled) + } else { + return nil + } + } /// Specifies whether this product allows cash payments - @objc public private(set) var cashEnabled: Bool + @nonobjc public private(set) var cashEnabled: Bool? + + /// Specifies whether this product allows cash payments. Boolean value. + @objc public var objc_cashEnabled: NSNumber? { + if let cashEnabled = cashEnabled { + return NSNumber(value: cashEnabled) + } else { + return nil + } + } /// Specifies whether this product allows for the pickup and drop off of other riders during the trip - @objc public private(set) var isShared: Bool + @nonobjc public private(set) var isShared: Bool? + + /// Specifies whether this product allows for the pickup and drop off of other riders during the trip. Boolean value. + @objc public var objc_isShared: NSNumber? { + if let isShared = isShared { + return NSNumber(value: isShared) + } else { + return nil + } + } /// The product group that this product belongs to @objc public private(set) var productGroup: ProductGroup @@ -88,16 +124,16 @@ struct UberProducts: Codable { public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - productID = try container.decode(String.self, forKey: .productID) - name = try container.decode(String.self, forKey: .name) - productDescription = try container.decode(String.self, forKey: .productDescription) - capacity = try container.decode(Int.self, forKey: .capacity) - imageURL = try container.decode(URL.self, forKey: .imageURL) + productID = try container.decodeIfPresent(String.self, forKey: .productID) + name = try container.decodeIfPresent(String.self, forKey: .name) + productDescription = try container.decodeIfPresent(String.self, forKey: .productDescription) + capacity = try container.decodeIfPresent(Int.self, forKey: .capacity) + imageURL = try container.decodeIfPresent(URL.self, forKey: .imageURL) priceDetails = try container.decodeIfPresent(PriceDetails.self, forKey: .priceDetails) - upfrontFareEnabled = try container.decode(Bool.self, forKey: .upfrontFareEnabled) - cashEnabled = try container.decode(Bool.self, forKey: .cashEnabled) - isShared = try container.decode(Bool.self, forKey: .isShared) - productGroup = try container.decode(ProductGroup.self, forKey: .productGroup) + upfrontFareEnabled = try container.decodeIfPresent(Bool.self, forKey: .upfrontFareEnabled) + cashEnabled = try container.decodeIfPresent(Bool.self, forKey: .cashEnabled) + isShared = try container.decodeIfPresent(Bool.self, forKey: .isShared) + productGroup = try container.decodeIfPresent(ProductGroup.self, forKey: .productGroup) ?? .unknown } } @@ -108,28 +144,73 @@ struct UberProducts: Codable { */ @objc(UBSDKPriceDetails) public class PriceDetails: NSObject, Codable { /// Unit of distance used to calculate fare (mile or km). - @objc public private(set) var distanceUnit: String + @objc public private(set) var distanceUnit: String? /// ISO 4217 currency code. - @objc public private(set) var currencyCode: String - + @objc public private(set) var currencyCode: String? + /// The charge per minute (if applicable). - @objc public private(set) var costPerMinute: Double - + @nonobjc public private(set) var costPerMinute: Double? + + /// The charge per minute (if applicable). + @objc(costPerMinute) public var objc_costPerMinute: NSNumber? { + if let costPerMinute = costPerMinute { + return NSNumber(value: costPerMinute) + } else { + return nil + } + } + /// The charge per distance unit (if applicable). - @objc public private(set) var costPerDistance: Double - + @nonobjc public private(set) var costPerDistance: Double? + + /// The charge per distance unit (if applicable). + @objc(costPerDistance) public var objc_costPerDistance: NSNumber? { + if let costPerDistance = costPerDistance { + return NSNumber(value: costPerDistance) + } else { + return nil + } + } + /// The base price. - @objc public private(set) var baseFee: Double - + @nonobjc public private(set) var baseFee: Double? + + /// The base price. + @objc(baseFee) public var objc_baseFee: NSNumber? { + if let baseFee = baseFee { + return NSNumber(value: baseFee) + } else { + return nil + } + } + /// The minimum price of a trip. - @objc public private(set) var minimumFee: Double - + @nonobjc public private(set) var minimumFee: Double? + + /// The minimum price of a trip. + @objc(minimumFee) public var objc_minimumFee: NSNumber? { + if let minimumFee = minimumFee { + return NSNumber(value: minimumFee) + } else { + return nil + } + } + + /// The fee if a rider cancels the trip after a grace period. + @nonobjc public private(set) var cancellationFee: Double? + /// The fee if a rider cancels the trip after a grace period. - @objc public private(set) var cancellationFee: Double + @objc(cancellationFee) public var objc_cancellationFee: NSNumber? { + if let cancellationFee = cancellationFee { + return NSNumber(value: cancellationFee) + } else { + return nil + } + } /// Array containing additional fees added to the price. See `ServiceFee`. - @objc public private(set) var serviceFees: [ServiceFee] + @objc public private(set) var serviceFees: [ServiceFee]? enum CodingKeys: String, CodingKey { case distanceUnit = "distance_unit" @@ -144,14 +225,14 @@ struct UberProducts: Codable { public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - distanceUnit = try container.decode(String.self, forKey: .distanceUnit) - currencyCode = try container.decode(String.self, forKey: .currencyCode) - costPerMinute = try container.decode(Double.self, forKey: .costPerMinute) - costPerDistance = try container.decode(Double.self, forKey: .costPerDistance) - baseFee = try container.decode(Double.self, forKey: .baseFee) - minimumFee = try container.decode(Double.self, forKey: .minimumFee) - cancellationFee = try container.decode(Double.self, forKey: .cancellationFee) - serviceFees = try container.decode([ServiceFee].self, forKey: .serviceFees) + distanceUnit = try container.decodeIfPresent(String.self, forKey: .distanceUnit) + currencyCode = try container.decodeIfPresent(String.self, forKey: .currencyCode) + costPerMinute = try container.decodeIfPresent(Double.self, forKey: .costPerMinute) + costPerDistance = try container.decodeIfPresent(Double.self, forKey: .costPerDistance) + baseFee = try container.decodeIfPresent(Double.self, forKey: .baseFee) + minimumFee = try container.decodeIfPresent(Double.self, forKey: .minimumFee) + cancellationFee = try container.decodeIfPresent(Double.self, forKey: .cancellationFee) + serviceFees = try container.decodeIfPresent([ServiceFee].self, forKey: .serviceFees) } } @@ -162,10 +243,19 @@ struct UberProducts: Codable { */ @objc(UBSDKServiceFee) public class ServiceFee: NSObject, Codable { /// The name of the service fee. - @objc public private(set) var name: String + @objc public private(set) var name: String? /// The amount of the service fee. - @objc public private(set) var fee: Double + @nonobjc public private(set) var fee: Double? + + /// The amount of the service fee. + @objc(fee) public var objc_fee: NSNumber? { + if let fee = fee { + return NSNumber(value: fee) + } else { + return nil + } + } enum CodingKeys: String, CodingKey { case name = "name" @@ -174,8 +264,8 @@ struct UberProducts: Codable { public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - name = try container.decode(String.self, forKey: .name) - fee = try container.decode(Double.self, forKey: .fee) + name = try container.decodeIfPresent(String.self, forKey: .name) + fee = try container.decodeIfPresent(Double.self, forKey: .fee) } } diff --git a/source/UberRides/Model/Ride.swift b/source/UberRides/Model/Ride.swift index 84efa721..0bb9ca44 100644 --- a/source/UberRides/Model/Ride.swift +++ b/source/UberRides/Model/Ride.swift @@ -44,22 +44,40 @@ import CoreLocation @objc public private(set) var pickup: RideRequestLocation? /// The unique ID of the Request. - @objc public private(set) var requestID: String + @objc public private(set) var requestID: String? /// The ID of the product - @objc public private(set) var productID: String + @objc public private(set) var productID: String? /// The status of the Request indicating state. @objc public private(set) var status: RideStatus /// The surge pricing multiplier used to calculate the increased price of a Request. - @objc public private(set) var surgeMultiplier: Double - + @nonobjc public private(set) var surgeMultiplier: Double? + + /// The surge pricing multiplier used to calculate the increased price of a Request. + @objc(surgeMultiplier) public var objc_surgeMultiplier: NSNumber? { + if let surgeMultiplier = surgeMultiplier { + return NSNumber(value: surgeMultiplier) + } else { + return nil + } + } + /// The object that contains vehicle details. Only non-null during an ongoing trip. @objc public private(set) var vehicle: Vehicle? /// True if the ride is an UberPOOL ride. False otherwise. - @objc public private(set) var isShared: Bool + @nonobjc public private(set) var isShared: Bool? + + /// True if the ride is an UberPOOL ride. False otherwise. + @objc(isShared) public var objc_isShared: NSNumber? { + if let isShared = isShared { + return NSNumber(value: isShared) + } else { + return nil + } + } enum CodingKeys: String, CodingKey { case destination = "destination" @@ -80,11 +98,11 @@ import CoreLocation driver = try container.decodeIfPresent(Driver.self, forKey: .driver) driverLocation = try container.decodeIfPresent(RideRequestLocation.self, forKey: .driverLocation) pickup = try container.decodeIfPresent(RideRequestLocation.self, forKey: .pickup) - requestID = try container.decode(String.self, forKey: .requestID) - productID = try container.decode(String.self, forKey: .productID) + requestID = try container.decodeIfPresent(String.self, forKey: .requestID) + productID = try container.decodeIfPresent(String.self, forKey: .productID) surgeMultiplier = try container.decodeIfPresent(Double.self, forKey: .surgeMultiplier) ?? 1.0 vehicle = try container.decodeIfPresent(Vehicle.self, forKey: .vehicle) status = try container.decodeIfPresent(RideStatus.self, forKey: .status) ?? .unknown - isShared = try container.decode(Bool.self, forKey: .isShared) + isShared = try container.decodeIfPresent(Bool.self, forKey: .isShared) } } diff --git a/source/UberRides/Model/RideCharge.swift b/source/UberRides/Model/RideCharge.swift index 31200487..18be0cae 100644 --- a/source/UberRides/Model/RideCharge.swift +++ b/source/UberRides/Model/RideCharge.swift @@ -28,20 +28,30 @@ * Describes the charges made against the rider in a ride receipt. */ @objc(UBSDKRideCharge) public class RideCharge: NSObject, Codable { - /// The amount of the charge. - @objc public private(set) var amount: Double + @nonobjc public private(set) var amount: Double? + + /// The amount of the charge. + @objc(amount) public var objc_amount: NSNumber? { + if let amount = amount { + return NSNumber(value: amount) + } else { + return nil + } + } /// The name of the charge. - @objc public private(set) var name: String + @objc public private(set) var name: String? /// The type of the charge. - @objc public private(set) var type: String + @objc public private(set) var type: String? public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - amount = try Double(container.decode(String.self, forKey: .amount)) ?? 0.0 - name = try container.decode(String.self, forKey: .name) - type = try container.decode(String.self, forKey: .type) + if let amountString = try container.decodeIfPresent(String.self, forKey: .amount) { + amount = Double(amountString) + } + name = try container.decodeIfPresent(String.self, forKey: .name) + type = try container.decodeIfPresent(String.self, forKey: .type) } } diff --git a/source/UberRides/Model/RideEstimate.swift b/source/UberRides/Model/RideEstimate.swift index b1f274a6..e247688e 100644 --- a/source/UberRides/Model/RideEstimate.swift +++ b/source/UberRides/Model/RideEstimate.swift @@ -41,8 +41,12 @@ import UberCore @nonobjc public private(set) var pickupEstimate: Int? /// The estimated time of vehicle arrival in minutes. UBSDKEstimateUnavailable if there are no cars available. - @objc(pickupEstimate) public var objc_pickupEstimate: Int { - return pickupEstimate ?? UBSDKEstimateUnavailable + @objc(pickupEstimate) public var objc_pickupEstimate: NSNumber? { + if let pickupEstimate = pickupEstimate { + return NSNumber(value: pickupEstimate) + } else { + return nil + } } /// Upfront Fare for the Ride Estimate. diff --git a/source/UberRides/Model/RideMap.swift b/source/UberRides/Model/RideMap.swift index d96ca776..677ddcf9 100644 --- a/source/UberRides/Model/RideMap.swift +++ b/source/UberRides/Model/RideMap.swift @@ -28,12 +28,11 @@ * Visual representation of a ride request, only available after a request is accepted. */ @objc(UBSDKRideMap) public class RideMap: NSObject, Codable { - /// URL to a map representing the requested trip. - @objc public private(set) var path: URL + @objc public private(set) var path: URL? /// Unique identifier representing a ride request. - @objc public private(set) var requestID: String + @objc public private(set) var requestID: String? enum CodingKeys: String, CodingKey { case path = "href" @@ -42,7 +41,7 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - path = try container.decode(URL.self, forKey: .path) - requestID = try container.decode(String.self, forKey: .requestID) + path = try container.decodeIfPresent(URL.self, forKey: .path) + requestID = try container.decodeIfPresent(String.self, forKey: .requestID) } } diff --git a/source/UberRides/Model/RideParameters.swift b/source/UberRides/Model/RideParameters.swift index 2d45cbce..b71e3ebc 100644 --- a/source/UberRides/Model/RideParameters.swift +++ b/source/UberRides/Model/RideParameters.swift @@ -111,7 +111,7 @@ import MapKit self.upfrontFare = upfrontFare } - var userAgent: String { + var userAgent: String? { var userAgentString: String = "" if let versionNumber: String = Bundle(for: type(of: self)).object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String { userAgentString = "rides-ios-v\(versionNumber)" diff --git a/source/UberRides/Model/RideReceipt.swift b/source/UberRides/Model/RideReceipt.swift index 4aab8817..5a2c9c24 100644 --- a/source/UberRides/Model/RideReceipt.swift +++ b/source/UberRides/Model/RideReceipt.swift @@ -30,34 +30,43 @@ @objc(UBSDKRideReceipt) public class RideReceipt: NSObject, Codable { /// Adjustments made to the charges such as promotions, and fees. - @objc public private(set) var chargeAdjustments: [RideCharge] + @objc public private(set) var chargeAdjustments: [RideCharge]? /// ISO 4217 - @objc public private(set) var currencyCode: String + @objc public private(set) var currencyCode: String? /// Distance of the trip charged. - @objc public private(set) var distance: String + @objc public private(set) var distance: String? /// The localized unit of distance. - @objc public private(set) var distanceLabel: String + @objc public private(set) var distanceLabel: String? /// Time duration of the trip. Use only the hour, minute, and second components. - @objc public private(set) var duration: DateComponents + @objc public private(set) var duration: DateComponents? /// Unique identifier representing a Request. - @objc public private(set) var requestID: String + @objc public private(set) var requestID: String? /// The summation of the normal fare and surge charge amount. - @objc public private(set) var subtotal: String + @objc public private(set) var subtotal: String? /// The total amount charged to the users payment method. This is the the subtotal (split if applicable) with taxes included. - @objc public private(set) var totalCharged: String + @objc public private(set) var totalCharged: String? /// The total amount still owed after attempting to charge the user. May be 0 if amount was paid in full. - @objc public private(set) var totalOwed: Double + @nonobjc public private(set) var totalOwed: Double? + + /// The total amount still owed after attempting to charge the user. May be 0 if amount was paid in full. + @objc(totalOwed) public var objc_totalOwed: NSNumber? { + if let totalOwed = totalOwed { + return NSNumber(value: totalOwed) + } else { + return nil + } + } /// The fare after credits and refunds have been applied. - @objc public private(set) var totalFare: String + @objc public private(set) var totalFare: String? enum CodingKeys: String, CodingKey { case chargeAdjustments = "charge_adjustments" @@ -74,21 +83,25 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - chargeAdjustments = try container.decode([RideCharge].self, forKey: .chargeAdjustments) - currencyCode = try container.decode(String.self, forKey: .currencyCode) - distance = try container.decode(String.self, forKey: .distance) - distanceLabel = try container.decode(String.self, forKey: .distanceLabel) - requestID = try container.decode(String.self, forKey: .requestID) - subtotal = try container.decode(String.self, forKey: .subtotal) - totalCharged = try container.decode(String.self, forKey: .totalCharged) + chargeAdjustments = try container.decodeIfPresent([RideCharge].self, forKey: .chargeAdjustments) + currencyCode = try container.decodeIfPresent(String.self, forKey: .currencyCode) + distance = try container.decodeIfPresent(String.self, forKey: .distance) + distanceLabel = try container.decodeIfPresent(String.self, forKey: .distanceLabel) + requestID = try container.decodeIfPresent(String.self, forKey: .requestID) + subtotal = try container.decodeIfPresent(String.self, forKey: .subtotal) + totalCharged = try container.decodeIfPresent(String.self, forKey: .totalCharged) totalOwed = try container.decodeIfPresent(Double.self, forKey: .totalOwed) ?? 0.0 - totalFare = try container.decode(String.self, forKey: .totalFare) + totalFare = try container.decodeIfPresent(String.self, forKey: .totalFare) - let durationString = try container.decode(String.self, forKey: .duration) + let durationString = try container.decodeIfPresent(String.self, forKey: .duration) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "HH:mm:ss" dateFormatter.calendar = Calendar.current - let date = dateFormatter.date(from: durationString) ?? Date(timeIntervalSince1970: 0) + var date = Date(timeIntervalSince1970: 0) + if let durationString = durationString, + let dateFromDuration = dateFormatter.date(from: durationString) { + date = dateFromDuration + } duration = Calendar.current.dateComponents(in: TimeZone.current, from: date) } } diff --git a/source/UberRides/Model/RideRequestLocation.swift b/source/UberRides/Model/RideRequestLocation.swift index d371ba4b..61ee4387 100644 --- a/source/UberRides/Model/RideRequestLocation.swift +++ b/source/UberRides/Model/RideRequestLocation.swift @@ -42,24 +42,50 @@ import UberCore /// The current bearing in degrees for a moving location. @nonobjc public private(set) var bearing: Int? - /// The current bearing in degrees for a moving location. UBSDKBearingUnavailable if not present. - @objc public var objc_bearing: Int { - return bearing ?? UBSDKBearingUnavailable + /// The current bearing in degrees for a moving location. + @objc(bearing) public var objc_bearing: NSNumber? { + if let bearing = bearing { + return NSNumber(value: bearing) + } else { + return nil + } } - + /// ETA is only available when the trips is accepted or arriving. @nonobjc public private(set) var eta: Int? - /// ETA is only available when the trips is accepted or arriving. -1 if not present. - @objc public var objc_eta: Int { - return eta ?? UBSDKEstimateUnavailable + /// ETA is only available when the trips is accepted or arriving. + @objc(eta) public var objc_eta: NSNumber? { + if let eta = eta { + return NSNumber(value: eta) + } else { + return nil + } } - + /// The latitude of the location. - @objc public private(set) var latitude: Double - + @nonobjc public private(set) var latitude: Double? + + /// The latitude of the location. + @objc(latitude) public var objc_latitude: NSNumber? { + if let latitude = latitude { + return NSNumber(value: latitude) + } else { + return nil + } + } + /// The longitude of the location. - @objc public private(set) var longitude: Double + @nonobjc public private(set) var longitude: Double? + + /// The longitude of the location. + @objc(longitude) public var objc_longitude: NSNumber? { + if let longitude = longitude { + return NSNumber(value: longitude) + } else { + return nil + } + } public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) @@ -68,7 +94,7 @@ import UberCore bearing = try container.decodeIfPresent(Int.self, forKey: .bearing) eta = try container.decodeIfPresent(Int.self, forKey: .eta) eta = eta != -1 ? eta : nil // Since the API returns -1, converting to an optional. - latitude = try container.decode(Double.self, forKey: .latitude) - longitude = try container.decode(Double.self, forKey: .longitude) + latitude = try container.decodeIfPresent(Double.self, forKey: .latitude) + longitude = try container.decodeIfPresent(Double.self, forKey: .longitude) } } diff --git a/source/UberRides/Model/TimeEstimate.swift b/source/UberRides/Model/TimeEstimate.swift index 060344d3..439fa4ae 100644 --- a/source/UberRides/Model/TimeEstimate.swift +++ b/source/UberRides/Model/TimeEstimate.swift @@ -42,13 +42,22 @@ struct TimeEstimates: Codable { */ @objc(UBSDKTimeEstimate) public class TimeEstimate: NSObject, Codable { /// Unique identifier representing a specific product for a given latitude & longitude. - @objc public private(set) var productID: String + @objc public private(set) var productID: String? /// Display name of product. Ex: "UberBLACK". - @objc public private(set) var name: String + @objc public private(set) var name: String? /// ETA for the product (in seconds). - @objc public private(set) var estimate: Int + @nonobjc public private(set) var estimate: Int? + + /// ETA for the product (in seconds). + @objc(estimate) public var objc_estimate: NSNumber? { + if let estimate = estimate { + return NSNumber(value: estimate) + } else { + return nil + } + } enum CodingKeys: String, CodingKey { case productID = "product_id" @@ -58,8 +67,8 @@ struct TimeEstimates: Codable { public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - productID = try container.decode(String.self, forKey: .productID) - name = try container.decode(String.self, forKey: .name) - estimate = try container.decode(Int.self, forKey: .estimate) + productID = try container.decodeIfPresent(String.self, forKey: .productID) + name = try container.decodeIfPresent(String.self, forKey: .name) + estimate = try container.decodeIfPresent(Int.self, forKey: .estimate) } } diff --git a/source/UberRides/Model/UpfrontFare.swift b/source/UberRides/Model/UpfrontFare.swift index ed905951..41d6d9c4 100644 --- a/source/UberRides/Model/UpfrontFare.swift +++ b/source/UberRides/Model/UpfrontFare.swift @@ -21,22 +21,31 @@ @objc(UBSDKUpfrontFare) public class UpfrontFare: NSObject, Codable { /// A unique upfront fare identifier. - @objc public private(set) var fareID: String + @objc public private(set) var fareID: String? /// The total upfront fare value. - @objc public private(set) var value: Double + @nonobjc public private(set) var value: Double? + + /// The total upfront fare value. + @objc(value) public var objc_value: NSNumber? { + if let value = value { + return NSNumber(value: value) + } else { + return nil + } + } /// ISO 4217 currency code. - @objc public private(set) var currencyCode: String + @objc public private(set) var currencyCode: String? /// Formatted string of estimate in local currency. - @objc public private(set) var display: String + @objc public private(set) var display: String? /// The upfront fare expiration time - @objc public private(set) var expiresAt: Date + @objc public private(set) var expiresAt: Date? /// The components that make up the upfront fare - @objc public private(set) var breakdown: [UpfrontFareComponent] + @objc public private(set) var breakdown: [UpfrontFareComponent]? enum CodingKeys: String, CodingKey { case fareID = "fare_id" @@ -49,12 +58,12 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - fareID = try container.decode(String.self, forKey: .fareID) - value = try container.decode(Double.self, forKey: .value) - currencyCode = try container.decode(String.self, forKey: .currencyCode) - display = try container.decode(String.self, forKey: .display) - expiresAt = try container.decode(Date.self, forKey: .expiresAt) - breakdown = try container.decode([UpfrontFareComponent].self, forKey: .breakdown) + fareID = try container.decodeIfPresent(String.self, forKey: .fareID) + value = try container.decodeIfPresent(Double.self, forKey: .value) + currencyCode = try container.decodeIfPresent(String.self, forKey: .currencyCode) + display = try container.decodeIfPresent(String.self, forKey: .display) + expiresAt = try container.decodeIfPresent(Date.self, forKey: .expiresAt) + breakdown = try container.decodeIfPresent([UpfrontFareComponent].self, forKey: .breakdown) } } @@ -63,16 +72,25 @@ @objc public private(set) var type: UpfrontFareComponentType /// Value of the upfront fare component - @objc public private(set) var value: Double + @nonobjc public private(set) var value: Double? + + /// Value of the upfront fare component + @objc(value) public var objc_value: NSNumber? { + if let value = value { + return NSNumber(value: value) + } else { + return nil + } + } /// A string that can be displayed to the user representing this portion of the fare - @objc public private(set) var name: String + @objc public private(set) var name: String? public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - type = try container.decode(UpfrontFareComponentType.self, forKey: .type) - value = try container.decode(Double.self, forKey: .value) - name = try container.decode(String.self, forKey: .name) + type = try container.decodeIfPresent(UpfrontFareComponentType.self, forKey: .type) ?? .unknown + value = try container.decodeIfPresent(Double.self, forKey: .value) + name = try container.decodeIfPresent(String.self, forKey: .name) } } diff --git a/source/UberRides/Model/UserActivity.swift b/source/UberRides/Model/UserActivity.swift index 5ad71eb5..5bc62859 100644 --- a/source/UberRides/Model/UserActivity.swift +++ b/source/UberRides/Model/UserActivity.swift @@ -22,12 +22,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +import CoreLocation +import UberCore + // MARK: TripHistory /** * User's lifetime trip activity with Uber. */ -@objc(UBSDKTripHistory) public class TripHistory: NSObject, Codable { +@objc(UBSDKTripHistory) public class TripHistory: NSObject, Decodable { /// Position in pagination. @objc public private(set) var offset: Int @@ -61,30 +64,39 @@ /** * Information regarding an Uber trip in a user's activity history. */ -@objc(UBSDKUserActivity) public class UserActivity: NSObject, Codable { +@objc(UBSDKUserActivity) public class UserActivity: NSObject, Decodable { /// Status of the activity. Only returns completed for now. - public private(set) var status: RideStatus + @objc public private(set) var status: RideStatus /// Length of activity in miles. - @objc public private(set) var distance: Double + @nonobjc public private(set) var distance: Double? + + /// Length of activity in miles. + @objc(distance) public var objc_distance: NSNumber? { + if let distance = distance { + return NSNumber(value: distance) + } else { + return nil + } + } /// Represents timestamp of activity request time in current locale. - @objc public private(set) var requestTime: Date + @objc public private(set) var requestTime: Date? /// Represents timestamp of activity start time in current locale. - @objc public private(set) var startTime: Date + @objc public private(set) var startTime: Date? /// Represents timestamp of activity end time in current locale. - @objc public private(set) var endTime: Date + @objc public private(set) var endTime: Date? /// City that activity started in. - @objc public private(set) var startCity: TripCity + @objc public private(set) var startCity: TripCity? /// Unique activity identifier. - @objc public private(set) var requestID: String + @objc public private(set) var requestID: String? /// Unique identifier representing a specific product for a given latitude & longitude. - @objc public private(set) var productID: String + @objc public private(set) var productID: String? enum CodingKeys: String, CodingKey { case distance = "distance" @@ -98,13 +110,13 @@ } public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - distance = try container.decode(Double.self, forKey: .distance) - requestTime = try container.decode(Date.self, forKey: .requestTime) - startTime = try container.decode(Date.self, forKey: .startTime) - endTime = try container.decode(Date.self, forKey: .endTime) - startCity = try container.decode(TripCity.self, forKey: .startCity) - requestID = try container.decode(String.self, forKey: .requestID) - productID = try container.decode(String.self, forKey: .productID) + distance = try container.decodeIfPresent(Double.self, forKey: .distance) + requestTime = try container.decodeIfPresent(Date.self, forKey: .requestTime) + startTime = try container.decodeIfPresent(Date.self, forKey: .startTime) + endTime = try container.decodeIfPresent(Date.self, forKey: .endTime) + startCity = try container.decodeIfPresent(TripCity.self, forKey: .startCity) + requestID = try container.decodeIfPresent(String.self, forKey: .requestID) + productID = try container.decodeIfPresent(String.self, forKey: .productID) status = try container.decodeIfPresent(RideStatus.self, forKey: .status) ?? .unknown } } @@ -114,15 +126,44 @@ /** * Information relating to a city in a trip activity. */ -@objc(UBSDKTripCity) public class TripCity: NSObject, Codable { +@objc(UBSDKTripCity) public class TripCity: NSObject, Decodable { /// Latitude of city location. - @objc public private(set) var latitude: Double - + /// - Warning: Deprecated. Use `location` instead. + @nonobjc public var latitude: Double? { + return location?.coordinate.latitude + } + + /// Latitude of city location. + /// - Warning: Deprecated. Use `location` instead. + @objc(latitude) public var objc_latitude: NSNumber? { + if let latitude = latitude { + return NSNumber(value: latitude) + } else { + return nil + } + } + /// Longitude of city location. - @objc public private(set) var longitude: Double + /// - Warning: Deprecated. Use `location` instead. + @nonobjc public var longitude: Double? { + return location?.coordinate.longitude + } + + /// Longitude of city location. + /// - Warning: Deprecated. Use `location` instead. + @objc(longitude) public var objc_longitude: NSNumber? { + if let longitude = longitude { + return NSNumber(value: longitude) + } else { + return nil + } + } + + /// City location + @objc public private(set) var location: CLLocation? /// Display name of city. - @objc public private(set) var name: String + @objc public private(set) var name: String? enum CodingKeys: String, CodingKey { case latitude = "latitude" @@ -132,8 +173,10 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - latitude = try container.decode(Double.self, forKey: .latitude) - longitude = try container.decode(Double.self, forKey: .longitude) - name = try container.decode(String.self, forKey: .name) + if let latitude = try container.decodeIfPresent(Double.self, forKey: .latitude), + let longitude = try container.decodeIfPresent(Double.self, forKey: .longitude) { + location = CLLocation(latitude: latitude, longitude: longitude) + } + name = try container.decodeIfPresent(String.self, forKey: .name) } } diff --git a/source/UberRides/Model/UserProfile.swift b/source/UberRides/Model/UserProfile.swift index 50f51964..ec958d3a 100644 --- a/source/UberRides/Model/UserProfile.swift +++ b/source/UberRides/Model/UserProfile.swift @@ -45,14 +45,14 @@ /// Unique identifier of the Uber user. Deprecated, use riderID instead. @available(*, deprecated, message:"use riderID instead") - @objc public var UUID: String { + @objc public var UUID: String? { // This implementation gets rid of the deprecated warning while compiling this SDK. return _UUID } - private let _UUID: String + private let _UUID: String? /// Unique identifier of the Uber user. - @objc public private(set) var riderID: String + @objc public private(set) var riderID: String? enum CodingKeys: String, CodingKey { case firstName = "first_name" @@ -71,7 +71,7 @@ email = try container.decodeIfPresent(String.self, forKey: .email) picturePath = try container.decodeIfPresent(String.self, forKey: .picturePath) promoCode = try container.decodeIfPresent(String.self, forKey: .promoCode) - _UUID = try container.decode(String.self, forKey: ._UUID) - riderID = try container.decode(String.self, forKey: .riderID) + _UUID = try container.decodeIfPresent(String.self, forKey: ._UUID) + riderID = try container.decodeIfPresent(String.self, forKey: .riderID) } } diff --git a/source/UberRides/Model/Vehicle.swift b/source/UberRides/Model/Vehicle.swift index 99e1f28e..aa4ab8a7 100644 --- a/source/UberRides/Model/Vehicle.swift +++ b/source/UberRides/Model/Vehicle.swift @@ -30,13 +30,13 @@ @objc(UBSDKVehicle) public class Vehicle: NSObject, Codable { /// The license plate number of the vehicle. - @objc public private(set) var licensePlate: String + @objc public private(set) var licensePlate: String? /// The vehicle make or brand. - @objc public private(set) var make: String + @objc public private(set) var make: String? /// The vehicle model or type. - @objc public private(set) var model: String + @objc public private(set) var model: String? /// The URL to a stock photo of the vehicle @objc public private(set) var pictureURL: URL? @@ -50,9 +50,9 @@ public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - licensePlate = try container.decode(String.self, forKey: .licensePlate) - make = try container.decode(String.self, forKey: .make) - model = try container.decode(String.self, forKey: .model) + licensePlate = try container.decodeIfPresent(String.self, forKey: .licensePlate) + make = try container.decodeIfPresent(String.self, forKey: .make) + model = try container.decodeIfPresent(String.self, forKey: .model) pictureURL = try container.decodeIfPresent(URL.self, forKey: .pictureURL) } } diff --git a/source/UberRides/RideRequestButton.swift b/source/UberRides/RideRequestButton.swift index 7a645783..de285d70 100644 --- a/source/UberRides/RideRequestButton.swift +++ b/source/UberRides/RideRequestButton.swift @@ -405,7 +405,8 @@ import UberCore if let price = estimate.estimate, let productName = estimate.name, estimate.productID == productID { - if estimate.surgeMultiplier > 1.0 { + if let surgeMultiplier = estimate.surgeMultiplier, + surgeMultiplier > 1.0 { surge = true } let priceEstimateString = String(format: NSLocalizedString("%1$@ for %2$@", bundle: Bundle(for: type(of: self)), comment: "Price estimate string for an Uber product"), price, productName) diff --git a/source/UberRidesTests/ObjectMappingTests.swift b/source/UberRidesTests/ObjectMappingTests.swift index 42b9ada9..3785831f 100644 --- a/source/UberRidesTests/ObjectMappingTests.swift +++ b/source/UberRidesTests/ObjectMappingTests.swift @@ -66,9 +66,9 @@ class ObjectMappingTests: XCTestCase { let serviceFees = priceDetails!.serviceFees XCTAssertNotNil(serviceFees) - XCTAssertEqual(serviceFees.count, 1) - XCTAssertEqual(serviceFees.first!.name, "Booking fee") - XCTAssertEqual(serviceFees.first!.fee, 2.0) + XCTAssertEqual(serviceFees?.count, 1) + XCTAssertEqual(serviceFees?.first?.name, "Booking fee") + XCTAssertEqual(serviceFees?.first?.fee, 2.0) } } } @@ -137,8 +137,8 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(uberX?.productGroup, ProductGroup.uberX) XCTAssertEqual(uberX?.productDescription, "Everyday rides that are always smarter than a taxi") - XCTAssertEqual(uberX?.priceDetails?.serviceFees.first?.fee, 0.55) - XCTAssertEqual(uberX?.priceDetails?.serviceFees.first?.name, "Booking fee") + XCTAssertEqual(uberX?.priceDetails?.serviceFees?.first?.fee, 0.55) + XCTAssertEqual(uberX?.priceDetails?.serviceFees?.first?.name, "Booking fee") XCTAssertEqual(uberX?.priceDetails?.costPerMinute, 0.4) XCTAssertEqual(uberX?.priceDetails?.distanceUnit, "km") XCTAssertEqual(uberX?.priceDetails?.minimumFee, 9) @@ -298,9 +298,9 @@ class ObjectMappingTests: XCTestCase { XCTAssertNotNil(history[0].startCity) let city = history[0].startCity - XCTAssertEqual(city.name, "San Francisco") - XCTAssertEqual(city.latitude, 37.7749295) - XCTAssertEqual(city.longitude, -122.4194155) + XCTAssertEqual(city?.name, "San Francisco") + XCTAssertEqual(city?.latitude, 37.7749295) + XCTAssertEqual(city?.longitude, -122.4194155) } } } @@ -397,7 +397,7 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(trip.requestID, "43faeac4-1634-4a0c-9826-783e3a3d1668") XCTAssertEqual(trip.productID, "a1111c8c-c720-46c3-8534-2fcdd730040d") XCTAssertEqual(trip.status, RideStatus.processing) - XCTAssertFalse(trip.isShared) + XCTAssertEqual(trip.isShared, false) XCTAssertNil(trip.driverLocation) XCTAssertNil(trip.vehicle) @@ -431,7 +431,7 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(trip.requestID, "17cb78a7-b672-4d34-a288-a6c6e44d5315") XCTAssertEqual(trip.productID, "a1111c8c-c720-46c3-8534-2fcdd730040d") XCTAssertEqual(trip.status, RideStatus.accepted) - XCTAssertFalse(trip.isShared) + XCTAssertEqual(trip.isShared, false) XCTAssertEqual(trip.surgeMultiplier, 1.0) XCTAssertNotNil(trip.driverLocation) @@ -480,7 +480,7 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(trip.requestID, "a274f565-cdb7-4a64-947d-042dfd185eed") XCTAssertEqual(trip.productID, "a1111c8c-c720-46c3-8534-2fcdd730040d") XCTAssertEqual(trip.status, RideStatus.arriving) - XCTAssertFalse(trip.isShared) + XCTAssertEqual(trip.isShared, false) XCTAssertNotNil(trip.driverLocation) XCTAssertEqual(trip.driverLocation?.latitude, 37.7751956968) @@ -528,7 +528,7 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(trip.requestID, "a274f565-cdb7-4a64-947d-042dfd185eed") XCTAssertEqual(trip.productID, "a1111c8c-c720-46c3-8534-2fcdd730040d") XCTAssertEqual(trip.status, RideStatus.inProgress) - XCTAssertFalse(trip.isShared) + XCTAssertEqual(trip.isShared, false) XCTAssertNotNil(trip.driverLocation) XCTAssertEqual(trip.driverLocation?.latitude, 37.7751956968) @@ -576,7 +576,7 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(trip.requestID, "a274f565-cdb7-4a64-947d-042dfd185eed") XCTAssertEqual(trip.productID, "a1111c8c-c720-46c3-8534-2fcdd730040d") XCTAssertEqual(trip.status, RideStatus.completed) - XCTAssertFalse(trip.isShared) + XCTAssertEqual(trip.isShared, false) XCTAssertNil(trip.driverLocation) XCTAssertNil(trip.vehicle) @@ -605,9 +605,9 @@ class ObjectMappingTests: XCTestCase { XCTAssertEqual(estimate!.pickupEstimate, 2) XCTAssertNotNil(estimate?.fare) - XCTAssertEqual(estimate?.fare?.breakdown.first?.name, "Base Fare") - XCTAssertEqual(estimate?.fare?.breakdown.first?.type, UpfrontFareComponentType.baseFare) - XCTAssertEqual(estimate?.fare?.breakdown.first?.value, 11.95) + XCTAssertEqual(estimate?.fare?.breakdown?.first?.name, "Base Fare") + XCTAssertEqual(estimate?.fare?.breakdown?.first?.type, UpfrontFareComponentType.baseFare) + XCTAssertEqual(estimate?.fare?.breakdown?.first?.value, 11.95) XCTAssertEqual(estimate?.fare?.value, 11.95) XCTAssertEqual(estimate?.fare?.fareID, "3d957d6ab84e88209b6778d91bd4df3c12d17b60796d89793d6ed01650cbabfe") XCTAssertEqual(estimate?.fare?.expiresAt, Date(timeIntervalSince1970: 1503702982)) @@ -743,18 +743,18 @@ class ObjectMappingTests: XCTestCase { let chargeAdjustments = receipt.chargeAdjustments - XCTAssertEqual(chargeAdjustments.count, 1) - XCTAssertEqual(chargeAdjustments.first?.name, "Booking Fee") - XCTAssertEqual(chargeAdjustments.first?.type, "booking_fee") + XCTAssertEqual(chargeAdjustments?.count, 1) + XCTAssertEqual(chargeAdjustments?.first?.name, "Booking Fee") + XCTAssertEqual(chargeAdjustments?.first?.type, "booking_fee") XCTAssertEqual(receipt.subtotal, "$12.78") XCTAssertEqual(receipt.totalCharged, "$5.92") XCTAssertEqual(receipt.totalFare, "$12.79") XCTAssertEqual(receipt.totalOwed, 0.0) XCTAssertEqual(receipt.currencyCode, "USD") - XCTAssertEqual(receipt.duration.hour, 0) - XCTAssertEqual(receipt.duration.minute, 11) - XCTAssertEqual(receipt.duration.second, 32) + XCTAssertEqual(receipt.duration?.hour, 0) + XCTAssertEqual(receipt.duration?.minute, 11) + XCTAssertEqual(receipt.duration?.second, 32) XCTAssertEqual(receipt.distance, "1.87") XCTAssertEqual(receipt.distanceLabel, "miles")