Remove Alamofire
This commit is contained in:
parent
608963567f
commit
f95ecb0216
|
@ -16,13 +16,11 @@ let package = Package(
|
||||||
name: "Stubbing",
|
name: "Stubbing",
|
||||||
targets: ["Stubbing"])
|
targets: ["Stubbing"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [],
|
||||||
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.2.2"))
|
|
||||||
],
|
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "HTTP",
|
name: "HTTP",
|
||||||
dependencies: ["Alamofire"]),
|
dependencies: []),
|
||||||
.target(
|
.target(
|
||||||
name: "Stubbing",
|
name: "Stubbing",
|
||||||
dependencies: ["HTTP"]),
|
dependencies: ["HTTP"]),
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Alamofire
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias Session = Alamofire.Session
|
public enum HTTPError: Error {
|
||||||
|
case invalidStatusCode(HTTPURLResponse)
|
||||||
|
}
|
||||||
|
|
||||||
open class HTTPClient {
|
open class HTTPClient {
|
||||||
private let session: Session
|
private let session: URLSession
|
||||||
private let decoder: DataDecoder
|
private let decoder: JSONDecoder
|
||||||
|
|
||||||
public init(session: Session, decoder: DataDecoder) {
|
public init(session: URLSession, decoder: JSONDecoder) {
|
||||||
self.session = session
|
self.session = session
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
open func request<T: DecodableTarget>(_ target: T) -> AnyPublisher<T.ResultType, Error> {
|
open func request<T: DecodableTarget>(_ target: T) -> AnyPublisher<T.ResultType, Error> {
|
||||||
requestPublisher(target).value().mapError { $0.underlyingOrTypeErased }.eraseToAnyPublisher()
|
dataTaskPublisher(target)
|
||||||
|
.map(\.data)
|
||||||
|
.decode(type: T.ResultType.self, decoder: decoder)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func request<T: DecodableTarget, E: Error & Decodable>(
|
public func request<T: DecodableTarget, E: Error & Decodable>(
|
||||||
|
@ -24,40 +28,39 @@ open class HTTPClient {
|
||||||
decodeErrorsAs errorType: E.Type) -> AnyPublisher<T.ResultType, Error> {
|
decodeErrorsAs errorType: E.Type) -> AnyPublisher<T.ResultType, Error> {
|
||||||
let decoder = self.decoder
|
let decoder = self.decoder
|
||||||
|
|
||||||
return requestPublisher(target)
|
return dataTaskPublisher(target)
|
||||||
.tryMap { response -> T.ResultType in
|
.tryMap { result -> Data in
|
||||||
switch response.result {
|
if
|
||||||
case let .success(decoded): return decoded
|
let response = result.response as? HTTPURLResponse,
|
||||||
case let .failure(error):
|
!Self.validStatusCodes.contains(response.statusCode) {
|
||||||
if
|
|
||||||
let data = response.data,
|
|
||||||
let decodedError = try? decoder.decode(E.self, from: data) {
|
|
||||||
throw decodedError
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error.underlyingOrTypeErased
|
if let decodedError = try? decoder.decode(E.self, from: result.data) {
|
||||||
|
throw decodedError
|
||||||
|
} else {
|
||||||
|
throw HTTPError.invalidStatusCode(response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result.data
|
||||||
}
|
}
|
||||||
|
.decode(type: T.ResultType.self, decoder: decoder)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension HTTPClient {
|
private extension HTTPClient {
|
||||||
func requestPublisher<T: DecodableTarget>(_ target: T) -> DataResponsePublisher<T.ResultType> {
|
static let validStatusCodes = 200..<300
|
||||||
if let protocolClasses = session.sessionConfiguration.protocolClasses {
|
func dataTaskPublisher<T: DecodableTarget>(_ target: T) -> URLSession.DataTaskPublisher {
|
||||||
|
if let protocolClasses = session.configuration.protocolClasses {
|
||||||
for protocolClass in protocolClasses {
|
for protocolClass in protocolClasses {
|
||||||
(protocolClass as? TargetProcessing.Type)?.process(target: target)
|
(protocolClass as? TargetProcessing.Type)?.process(target: target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return session.request(target)
|
return session.dataTaskPublisher(for: target.urlRequest())
|
||||||
.validate()
|
|
||||||
.publishDecodable(type: T.ResultType.self, queue: session.rootQueue, decoder: decoder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AFError {
|
// return session.request(target.urlRequest())
|
||||||
var underlyingOrTypeErased: Error {
|
// .validate()
|
||||||
underlyingError ?? self
|
// .publishDecodable(type: T.ResultType.self, queue: session.rootQueue, decoder: decoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum HTTPMethod: String {
|
||||||
|
case delete = "DELETE"
|
||||||
|
case get = "GET"
|
||||||
|
case post = "POST"
|
||||||
|
case put = "PUT"
|
||||||
|
}
|
|
@ -1,32 +1,43 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Alamofire
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias HTTPMethod = Alamofire.HTTPMethod
|
public protocol Target {
|
||||||
public typealias HTTPHeaders = Alamofire.HTTPHeaders
|
|
||||||
public typealias ParameterEncoding = Alamofire.ParameterEncoding
|
|
||||||
public typealias URLEncoding = Alamofire.URLEncoding
|
|
||||||
public typealias JSONEncoding = Alamofire.JSONEncoding
|
|
||||||
|
|
||||||
public protocol Target: URLRequestConvertible {
|
|
||||||
var baseURL: URL { get }
|
var baseURL: URL { get }
|
||||||
var pathComponents: [String] { get }
|
var pathComponents: [String] { get }
|
||||||
var method: HTTPMethod { get }
|
var method: HTTPMethod { get }
|
||||||
var encoding: ParameterEncoding { get }
|
var queryParameters: [String: String]? { get }
|
||||||
var parameters: [String: Any]? { get }
|
var jsonBody: [String: Any]? { get }
|
||||||
var headers: HTTPHeaders? { get }
|
var headers: [String: String]? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Target {
|
public extension Target {
|
||||||
func asURLRequest() throws -> URLRequest {
|
func urlRequest() -> URLRequest {
|
||||||
var url = baseURL
|
var url = baseURL
|
||||||
|
|
||||||
for pathComponent in pathComponents {
|
for pathComponent in pathComponents {
|
||||||
url.appendPathComponent(pathComponent)
|
url.appendPathComponent(pathComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return try encoding.encode(try URLRequest(url: url, method: method, headers: headers), with: parameters)
|
if var components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||||
|
let queryItems = queryParameters?.map(URLQueryItem.init(name:value:)) {
|
||||||
|
components.queryItems = queryItems
|
||||||
|
|
||||||
|
if let queryComponentURL = components.url {
|
||||||
|
url = queryComponentURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlRequest = URLRequest(url: url)
|
||||||
|
|
||||||
|
urlRequest.httpMethod = method.rawValue
|
||||||
|
urlRequest.allHTTPHeaderFields = headers
|
||||||
|
|
||||||
|
if let jsonBody = jsonBody {
|
||||||
|
urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: jsonBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRequest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ private extension StubbingURLProtocol {
|
||||||
|
|
||||||
extension StubbingURLProtocol: TargetProcessing {
|
extension StubbingURLProtocol: TargetProcessing {
|
||||||
public static func process(target: Target) {
|
public static func process(target: Target) {
|
||||||
if let url = try? target.asURLRequest().url {
|
if let url = target.urlRequest().url {
|
||||||
targetsForURLs[url] = target
|
targetsForURLs[url] = target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ public protocol Endpoint {
|
||||||
var context: [String] { get }
|
var context: [String] { get }
|
||||||
var pathComponentsInContext: [String] { get }
|
var pathComponentsInContext: [String] { get }
|
||||||
var method: HTTPMethod { get }
|
var method: HTTPMethod { get }
|
||||||
var encoding: ParameterEncoding { get }
|
var queryParameters: [String: String]? { get }
|
||||||
var parameters: [String: Any]? { get }
|
var jsonBody: [String: Any]? { get }
|
||||||
var headers: HTTPHeaders? { get }
|
var headers: [String: String]? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Endpoint {
|
public extension Endpoint {
|
||||||
|
@ -29,14 +29,9 @@ public extension Endpoint {
|
||||||
context + pathComponentsInContext
|
context + pathComponentsInContext
|
||||||
}
|
}
|
||||||
|
|
||||||
var encoding: ParameterEncoding {
|
var queryParameters: [String: String]? { nil }
|
||||||
switch method {
|
|
||||||
case .get: return URLEncoding.default
|
|
||||||
default: return JSONEncoding.default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters: [String: Any]? { nil }
|
var jsonBody: [String: Any]? { nil }
|
||||||
|
|
||||||
var headers: HTTPHeaders? { nil }
|
var headers: [String: String]? { nil }
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ extension AccessTokenEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .oauthToken(clientID, clientSecret, grantType, scopes, code, redirectURI):
|
case let .oauthToken(clientID, clientSecret, grantType, scopes, code, redirectURI):
|
||||||
var params = [
|
var params = [
|
||||||
|
|
|
@ -23,7 +23,7 @@ extension AppAuthorizationEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .apps(clientName, redirectURI, scopes, website):
|
case let .apps(clientName, redirectURI, scopes, website):
|
||||||
var params = [
|
var params = [
|
||||||
|
|
|
@ -42,7 +42,7 @@ extension DeletionEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .oauthRevoke(token, clientID, clientSecret):
|
case let .oauthRevoke(token, clientID, clientSecret):
|
||||||
return ["token": token, "client_id": clientID, "client_secret": clientSecret]
|
return ["token": token, "client_id": clientID, "client_secret": clientSecret]
|
||||||
|
|
|
@ -36,7 +36,7 @@ extension FilterEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .create(phrase, context, irreversible, wholeWord, expiresIn):
|
case let .create(phrase, context, irreversible, wholeWord, expiresIn):
|
||||||
return params(phrase: phrase,
|
return params(phrase: phrase,
|
||||||
|
|
|
@ -22,7 +22,7 @@ extension ListEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .create(title):
|
case let .create(title):
|
||||||
return ["title": title]
|
return ["title": title]
|
||||||
|
|
|
@ -22,6 +22,7 @@ public struct Paged<T: Endpoint> {
|
||||||
|
|
||||||
extension Paged: Endpoint {
|
extension Paged: Endpoint {
|
||||||
public typealias ResultType = T.ResultType
|
public typealias ResultType = T.ResultType
|
||||||
|
// public typealias ResultType = PagedResult<T.ResultType>
|
||||||
|
|
||||||
public var APIVersion: String { endpoint.APIVersion }
|
public var APIVersion: String { endpoint.APIVersion }
|
||||||
|
|
||||||
|
@ -31,18 +32,25 @@ extension Paged: Endpoint {
|
||||||
|
|
||||||
public var method: HTTPMethod { endpoint.method }
|
public var method: HTTPMethod { endpoint.method }
|
||||||
|
|
||||||
public var encoding: ParameterEncoding { endpoint.encoding }
|
public var queryParameters: [String: String]? {
|
||||||
|
var queryParameters = endpoint.queryParameters ?? [String: String]()
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
queryParameters["max_id"] = maxID
|
||||||
var parameters = endpoint.parameters ?? [String: Any]()
|
queryParameters["min_id"] = minID
|
||||||
|
queryParameters["since_id"] = sinceID
|
||||||
|
|
||||||
parameters["max_id"] = maxID
|
if let limit = limit {
|
||||||
parameters["min_id"] = minID
|
queryParameters["limit"] = String(limit)
|
||||||
parameters["since_id"] = sinceID
|
}
|
||||||
parameters["limit"] = limit
|
|
||||||
|
|
||||||
return parameters
|
return queryParameters
|
||||||
}
|
}
|
||||||
|
|
||||||
public var headers: HTTPHeaders? { endpoint.headers }
|
public var headers: [String: String]? { endpoint.headers }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//public struct PagedResult<T: Decodable>: Decodable {
|
||||||
|
// public let result: T
|
||||||
|
// public let maxID: String?
|
||||||
|
// public let sinceID: String?
|
||||||
|
//}
|
||||||
|
|
|
@ -33,7 +33,7 @@ extension PushSubscriptionEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .create(endpoint, publicKey, auth, alerts):
|
case let .create(endpoint, publicKey, auth, alerts):
|
||||||
return ["subscription":
|
return ["subscription":
|
||||||
|
|
|
@ -39,12 +39,14 @@ extension StatusesEndpoint: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var parameters: [String: Any]? {
|
public var queryParameters: [String: String]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .timelinesPublic(local):
|
case let .timelinesPublic(local):
|
||||||
return ["local": local]
|
return ["local": String(local)]
|
||||||
case let .accountsStatuses(_, excludeReplies, onlyMedia, pinned):
|
case let .accountsStatuses(_, excludeReplies, onlyMedia, pinned):
|
||||||
return ["exclude_replies": excludeReplies, "only_media": onlyMedia, "pinned": pinned]
|
return ["exclude_replies": String(excludeReplies),
|
||||||
|
"only_media": String(onlyMedia),
|
||||||
|
"pinned": String(pinned)]
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ public final class MastodonAPIClient: HTTPClient {
|
||||||
public var instanceURL: URL
|
public var instanceURL: URL
|
||||||
public var accessToken: String?
|
public var accessToken: String?
|
||||||
|
|
||||||
public required init(session: Session, instanceURL: URL) {
|
public required init(session: URLSession, instanceURL: URL) {
|
||||||
self.instanceURL = instanceURL
|
self.instanceURL = instanceURL
|
||||||
super.init(session: session, decoder: MastodonDecoder())
|
super.init(session: session, decoder: MastodonDecoder())
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,19 +22,19 @@ extension MastodonAPITarget: DecodableTarget {
|
||||||
|
|
||||||
public var method: HTTPMethod { endpoint.method }
|
public var method: HTTPMethod { endpoint.method }
|
||||||
|
|
||||||
public var encoding: ParameterEncoding { endpoint.encoding }
|
public var queryParameters: [String: String]? { endpoint.queryParameters }
|
||||||
|
|
||||||
public var parameters: [String: Any]? { endpoint.parameters }
|
public var jsonBody: [String: Any]? { endpoint.jsonBody }
|
||||||
|
|
||||||
public var headers: HTTPHeaders? {
|
public var headers: [String: String]? {
|
||||||
var headers = endpoint.headers
|
var headers = endpoint.headers
|
||||||
|
|
||||||
if let accessToken = accessToken {
|
if let accessToken = accessToken {
|
||||||
if headers == nil {
|
if headers == nil {
|
||||||
headers = HTTPHeaders()
|
headers = [String: String]()
|
||||||
}
|
}
|
||||||
|
|
||||||
headers?.add(.authorization(bearerToken: accessToken))
|
headers?["Authorization"] = "Bearer ".appending(accessToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
{
|
{
|
||||||
"object": {
|
"object": {
|
||||||
"pins": [
|
"pins": [
|
||||||
{
|
|
||||||
"package": "Alamofire",
|
|
||||||
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
|
||||||
"state": {
|
|
||||||
"branch": null,
|
|
||||||
"revision": "becd9a729a37bdbef5bc39dc3c702b99f9e3d046",
|
|
||||||
"version": "5.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"package": "CombineExpectations",
|
"package": "CombineExpectations",
|
||||||
"repositoryURL": "https://github.com/groue/CombineExpectations.git",
|
"repositoryURL": "https://github.com/groue/CombineExpectations.git",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Mastodon
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
public struct AppEnvironment {
|
public struct AppEnvironment {
|
||||||
let session: Session
|
let session: URLSession
|
||||||
let webAuthSessionType: WebAuthSession.Type
|
let webAuthSessionType: WebAuthSession.Type
|
||||||
let keychain: Keychain.Type
|
let keychain: Keychain.Type
|
||||||
let userDefaults: UserDefaults
|
let userDefaults: UserDefaults
|
||||||
|
@ -17,7 +17,7 @@ public struct AppEnvironment {
|
||||||
let inMemoryContent: Bool
|
let inMemoryContent: Bool
|
||||||
let fixtureDatabase: IdentityDatabase?
|
let fixtureDatabase: IdentityDatabase?
|
||||||
|
|
||||||
public init(session: Session,
|
public init(session: URLSession,
|
||||||
webAuthSessionType: WebAuthSession.Type,
|
webAuthSessionType: WebAuthSession.Type,
|
||||||
keychain: Keychain.Type,
|
keychain: Keychain.Type,
|
||||||
userDefaults: UserDefaults,
|
userDefaults: UserDefaults,
|
||||||
|
@ -39,7 +39,7 @@ public struct AppEnvironment {
|
||||||
public extension AppEnvironment {
|
public extension AppEnvironment {
|
||||||
static func live(userNotificationCenter: UNUserNotificationCenter) -> Self {
|
static func live(userNotificationCenter: UNUserNotificationCenter) -> Self {
|
||||||
Self(
|
Self(
|
||||||
session: Session(configuration: .default),
|
session: URLSession.shared,
|
||||||
webAuthSessionType: LiveWebAuthSession.self,
|
webAuthSessionType: LiveWebAuthSession.self,
|
||||||
keychain: LiveKeychain.self,
|
keychain: LiveKeychain.self,
|
||||||
userDefaults: .standard,
|
userDefaults: .standard,
|
||||||
|
|
|
@ -73,9 +73,9 @@ private struct UpdatedFilterTarget: DecodableTarget {
|
||||||
let baseURL = URL(string: "https://filter.metabolist.com")!
|
let baseURL = URL(string: "https://filter.metabolist.com")!
|
||||||
let pathComponents = ["filter"]
|
let pathComponents = ["filter"]
|
||||||
let method = HTTPMethod.get
|
let method = HTTPMethod.get
|
||||||
let encoding: ParameterEncoding = JSONEncoding.default
|
let queryParameters: [String: String]? = nil
|
||||||
let parameters: [String: Any]? = nil
|
let jsonBody: [String: Any]? = nil
|
||||||
let headers: HTTPHeaders? = nil
|
let headers: [String: String]? = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension InstanceURLService {
|
private extension InstanceURLService {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import ServiceLayer
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
public extension AppEnvironment {
|
public extension AppEnvironment {
|
||||||
static func mock(session: Session = Session(configuration: .stubbing),
|
static func mock(session: URLSession = URLSession(configuration: .stubbing),
|
||||||
webAuthSessionType: WebAuthSession.Type = SuccessfulMockWebAuthSession.self,
|
webAuthSessionType: WebAuthSession.Type = SuccessfulMockWebAuthSession.self,
|
||||||
keychain: Keychain.Type = MockKeychain.self,
|
keychain: Keychain.Type = MockKeychain.self,
|
||||||
userDefaults: UserDefaults = MockUserDefaults(),
|
userDefaults: UserDefaults = MockUserDefaults(),
|
||||||
|
@ -18,7 +18,7 @@ public extension AppEnvironment {
|
||||||
inMemoryContent: Bool = true,
|
inMemoryContent: Bool = true,
|
||||||
fixtureDatabase: IdentityDatabase? = nil) -> Self {
|
fixtureDatabase: IdentityDatabase? = nil) -> Self {
|
||||||
AppEnvironment(
|
AppEnvironment(
|
||||||
session: Session(configuration: .stubbing),
|
session: session,
|
||||||
webAuthSessionType: webAuthSessionType,
|
webAuthSessionType: webAuthSessionType,
|
||||||
keychain: keychain,
|
keychain: keychain,
|
||||||
userDefaults: userDefaults,
|
userDefaults: userDefaults,
|
||||||
|
|
Loading…
Reference in New Issue