qingqiu chegngong

This commit is contained in:
fengshuo
2024-02-24 13:49:51 +08:00
parent 990dfebf8a
commit d0f0f68e2b
69 changed files with 4861 additions and 3864 deletions

View File

@@ -15,6 +15,10 @@ target 'yinmeng-ios' do
pod 'Reusable'
#约束
pod 'SnapKit', '~> 5.6.0'
#获取设备信息
pod 'DeviceKit', '~> 4.0'
pod "NSObject+Rx"
# Pods for yinmeng-ios
end

View File

@@ -1,5 +1,6 @@
PODS:
- Alamofire (5.7.1)
- Alamofire (5.8.1)
- DeviceKit (4.9.0)
- HandyJSON (5.0.2)
- Kingfisher (7.10.2)
- MBProgressHUD (1.2.0)
@@ -7,6 +8,8 @@ PODS:
- Moya/Core (= 15.0.0)
- Moya/Core (15.0.0):
- Alamofire (~> 5.0)
- "NSObject+Rx (5.2.2)":
- RxSwift (~> 6.2)
- Reusable (4.1.2):
- Reusable/Storyboard (= 4.1.2)
- Reusable/View (= 4.1.2)
@@ -21,10 +24,12 @@ PODS:
- SnapKit (5.6.0)
DEPENDENCIES:
- DeviceKit (~> 4.0)
- HandyJSON
- Kingfisher
- MBProgressHUD
- Moya
- "NSObject+Rx"
- Reusable
- RxCocoa
- RxSwift
@@ -33,10 +38,12 @@ DEPENDENCIES:
SPEC REPOS:
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
- Alamofire
- DeviceKit
- HandyJSON
- Kingfisher
- MBProgressHUD
- Moya
- "NSObject+Rx"
- Reusable
- RxCocoa
- RxRelay
@@ -44,17 +51,19 @@ SPEC REPOS:
- SnapKit
SPEC CHECKSUMS:
Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88
Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7
DeviceKit: 847709bf70b78fd9ab765bd571fb9f5f815c3fc1
HandyJSON: 9e4e236f5d2dbefad5155a77417bbea438201c03
Kingfisher: 99edc495d3b7607e6425f0d6f6847b2abd6d716d
MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
"NSObject+Rx": 61cf1f7306a73dcef8b36649198af0813ec18dfd
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b
RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd
RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8
SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25
PODFILE CHECKSUM: e291658151a1fcada5c9dd297587942ae9475225
PODFILE CHECKSUM: c70d2998947e71f4b799d573fbe428797987a5b9
COCOAPODS: 1.12.1

View File

@@ -1,11 +1,10 @@
![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/Resources/AlamofireLogo.png)
[![Swift](https://img.shields.io/badge/Swift-5.5_5.6_5.7_5.8-orange?style=flat-square)](https://img.shields.io/badge/Swift-5.5_5.6_5.7_5.8-Orange?style=flat-square)
[![Platforms](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_Linux_Windows-yellowgreen?style=flat-square)](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_Linux_Windows-Green?style=flat-square)
[![Swift](https://img.shields.io/badge/Swift-5.6_5.7_5.8_5.9-orange?style=flat-square)](https://img.shields.io/badge/Swift-5.6_5.7_5.8_5.9-Orange?style=flat-square)
[![Platforms](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_visionOS_Linux_Windows_Android-yellowgreen?style=flat-square)](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_vision_OS_Linux_Windows_Android-Green?style=flat-square)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg?style=flat-square)](https://img.shields.io/cocoapods/v/Alamofire.svg)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat-square)](https://github.com/Carthage/Carthage)
[![Swift Package Manager](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)
[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat-square)](https://twitter.com/AlamofireSF)
[![Swift Forums](https://img.shields.io/badge/Swift_Forums-Alamofire-orange?style=flat-square)](https://forums.swift.org/c/related-projects/alamofire/37)
Alamofire is an HTTP networking library written in Swift.
@@ -52,6 +51,33 @@ Alamofire is an HTTP networking library written in Swift.
- [x] Comprehensive Unit and Integration Test Coverage
- [x] [Complete Documentation](https://alamofire.github.io/Alamofire)
## Write Requests Fast!
Alamofire's compact syntax and extensive feature set allow requests with powerful features like automatic retry to be written in just a few lines of code.
```swift
// Automatic String to URL conversion, Swift concurrency support, and automatic retry.
let response = await AF.request("https://httpbin.org/get", interceptor: .retryPolicy)
// Automatic HTTP Basic Auth.
.authenticate(username: "user", password: "pass")
// Caching customization.
.cacheResponse(using: .cache)
// Redirect customization.
.redirect(using: .follow)
// Validate response code and Content-Type.
.validate()
// Produce a cURL command for the request.
.cURLDescription { description in
print(description)
}
// Automatic Decodable support with background parsing.
.serializingDecodable(DecodableType.self)
// Await the full response with metrics and a parsed body.
.response
// Detailed response description for easy debugging.
debugPrint(response)
```
## Component Libraries
In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem.
@@ -62,20 +88,22 @@ In order to keep Alamofire focused specifically on core networking implementatio
## Requirements
| Platform | Minimum Swift Version | Installation | Status |
| --- | --- | --- | --- |
| iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ | 5.5 | [CocoaPods](#cocoapods), [Carthage](#carthage), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested |
| ---------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ | 5.6 | [CocoaPods](#cocoapods), [Carthage](#carthage), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested |
| Linux | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported |
| Windows | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported |
| Android | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported |
#### Known Issues on Linux and Windows
Alamofire builds on Linux and Windows but there are missing features and many issues in the underlying `swift-corelibs-foundation` that prevent full functionality and may cause crashes. These include:
Alamofire builds on Linux, Windows, and Android but there are missing features and many issues in the underlying `swift-corelibs-foundation` that prevent full functionality and may cause crashes. These include:
- `ServerTrustManager` and associated certificate functionality is unavailable, so there is no certificate pinning and no client certificate support.
- Various methods of HTTP authentication may crash, including HTTP Basic and HTTP Digest. Crashes may occur if responses contain server challenges.
- Cache control through `CachedResponseHandler` and associated APIs is unavailable, as the underlying delegate methods aren't called.
- `URLSessionTaskMetrics` are never gathered.
Due to these issues, Alamofire is unsupported on Linux and Windows. Please report any crashes to the [Swift bug reporter](https://bugs.swift.org).
Due to these issues, Alamofire is unsupported on Linux, Windows, and Android. Please report any crashes to the [Swift bug reporter](https://bugs.swift.org).
## Migration Guides
@@ -85,6 +113,7 @@ Due to these issues, Alamofire is unsupported on Linux and Windows. Please repor
- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md)
## Communication
- If you **need help with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`.
- If you need to **find or understand an API**, check [our documentation](http://alamofire.github.io/Alamofire/) or [Apple's documentation for `URLSession`](https://developer.apple.com/documentation/foundation/url_loading_system), on top of which Alamofire is built.
- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
@@ -118,7 +147,7 @@ Once you have your Swift package set up, adding Alamofire as a dependency is as
```swift
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.6.4"))
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.8.1"))
]
```

View File

@@ -24,6 +24,10 @@
import Foundation
#if canImport(Security)
import Security
#endif
/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with
/// their own associated reasons.
public enum AFError: Error {
@@ -129,7 +133,7 @@ public enum AFError: Error {
case invalidEmptyResponse(type: String)
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// Underlying reason a server trust evaluation error occurred.
public enum ServerTrustFailureReason {
/// The output of a server trust evaluation.
@@ -211,7 +215,7 @@ public enum AFError: Error {
case responseValidationFailed(reason: ResponseValidationFailureReason)
/// Response serialization failed.
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// `ServerTrustEvaluating` instance threw an error during trust evaluation.
case serverTrustEvaluationFailed(reason: ServerTrustFailureReason)
#endif
@@ -314,7 +318,7 @@ extension AFError {
return false
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will
/// contain the associated value.
public var isServerTrustEvaluationError: Bool {
@@ -393,7 +397,7 @@ extension AFError {
return reason.underlyingError
case let .responseSerializationFailed(reason):
return reason.underlyingError
#if !(os(Linux) || os(Windows))
#if canImport(Security)
case let .serverTrustEvaluationFailed(reason):
return reason.underlyingError
#endif
@@ -451,7 +455,7 @@ extension AFError {
return destination
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// The download resume data of any underlying network error. Only produced by `DownloadRequest`s.
public var downloadResumeData: Data? {
(underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data
@@ -610,7 +614,7 @@ extension AFError.ResponseSerializationFailureReason {
}
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
extension AFError.ServerTrustFailureReason {
var output: AFError.ServerTrustFailureReason.Output? {
switch self {
@@ -688,7 +692,7 @@ extension AFError: LocalizedError {
"""
case let .sessionInvalidated(error):
return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")"
#if !(os(Linux) || os(Windows))
#if canImport(Security)
case let .serverTrustEvaluationFailed(reason):
return "Server trust evaluation failed due to reason: \(reason.localizedDescription)"
#endif
@@ -822,7 +826,7 @@ extension AFError.ResponseValidationFailureReason {
}
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
extension AFError.ServerTrustFailureReason {
var localizedDescription: String {
switch self {

View File

@@ -37,4 +37,4 @@ import Foundation
public let AF = Session.default
/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate.
let version = "5.7.1"
let version = "5.8.0"

View File

@@ -217,15 +217,14 @@ public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor wh
/// The `Credential` used to authenticate requests.
public var credential: Credential? {
get { $mutableState.credential }
set { $mutableState.credential = newValue }
get { mutableState.credential }
set { mutableState.credential = newValue }
}
let authenticator: AuthenticatorType
let queue = DispatchQueue(label: "org.alamofire.authentication.inspector")
@Protected
private var mutableState: MutableState
private let mutableState: Protected<MutableState>
// MARK: Initialization
@@ -242,13 +241,13 @@ public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor wh
credential: Credential? = nil,
refreshWindow: RefreshWindow? = RefreshWindow()) {
self.authenticator = authenticator
mutableState = MutableState(credential: credential, refreshWindow: refreshWindow)
mutableState = Protected(MutableState(credential: credential, refreshWindow: refreshWindow))
}
// MARK: Adapt
public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
let adaptResult: AdaptResult = $mutableState.write { mutableState in
let adaptResult: AdaptResult = mutableState.write { mutableState in
// Queue the adapt operation if a refresh is already in place.
guard !mutableState.isRefreshing else {
let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion)
@@ -316,7 +315,7 @@ public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor wh
return
}
$mutableState.write { mutableState in
mutableState.write { mutableState in
mutableState.requestsToRetry.append(completion)
guard !mutableState.isRefreshing else { return }
@@ -340,7 +339,7 @@ public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor wh
// Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously.
queue.async {
self.authenticator.refresh(credential, for: session) { result in
self.$mutableState.write { mutableState in
self.mutableState.write { mutableState in
switch result {
case let .success(credential):
self.handleRefreshSuccess(credential, insideLock: &mutableState)

View File

@@ -22,7 +22,7 @@
// THE SOFTWARE.
//
#if !((os(iOS) && (arch(i386) || arch(arm))) || os(Windows) || os(Linux))
#if !((os(iOS) && (arch(i386) || arch(arm))) || os(Windows) || os(Linux) || os(Android))
import Combine
import Dispatch
@@ -91,23 +91,22 @@ public struct DataResponsePublisher<Value>: Publisher {
where Downstream.Input == Output {
typealias Failure = Downstream.Failure
@Protected
private var downstream: Downstream?
private let downstream: Protected<Downstream?>
private let request: DataRequest
private let responseHandler: Handler
init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) {
self.request = request
self.responseHandler = responseHandler
self.downstream = downstream
self.downstream = Protected(downstream)
}
func request(_ demand: Subscribers.Demand) {
assert(demand > 0)
guard let downstream = downstream else { return }
guard let downstream = downstream.read({ $0 }) else { return }
self.downstream = nil
self.downstream.write(nil)
responseHandler { response in
_ = downstream.receive(response)
downstream.receive(completion: .finished)
@@ -116,7 +115,7 @@ public struct DataResponsePublisher<Value>: Publisher {
func cancel() {
request.cancel()
downstream = nil
downstream.write(nil)
}
}
}
@@ -312,23 +311,22 @@ public struct DataStreamPublisher<Value>: Publisher {
where Downstream.Input == Output {
typealias Failure = Downstream.Failure
@Protected
private var downstream: Downstream?
private let downstream: Protected<Downstream?>
private let request: DataStreamRequest
private let streamHandler: Handler
init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) {
self.request = request
self.streamHandler = streamHandler
self.downstream = downstream
self.downstream = Protected(downstream)
}
func request(_ demand: Subscribers.Demand) {
assert(demand > 0)
guard let downstream = downstream else { return }
guard let downstream = downstream.read({ $0 }) else { return }
self.downstream = nil
self.downstream.write(nil)
streamHandler { stream in
_ = downstream.receive(stream)
if case .complete = stream.event {
@@ -339,7 +337,7 @@ public struct DataStreamPublisher<Value>: Publisher {
func cancel() {
request.cancel()
downstream = nil
downstream.write(nil)
}
}
}
@@ -462,23 +460,22 @@ public struct DownloadResponsePublisher<Value>: Publisher {
where Downstream.Input == Output {
typealias Failure = Downstream.Failure
@Protected
private var downstream: Downstream?
private let downstream: Protected<Downstream?>
private let request: DownloadRequest
private let responseHandler: Handler
init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) {
self.request = request
self.responseHandler = responseHandler
self.downstream = downstream
self.downstream = Protected(downstream)
}
func request(_ demand: Subscribers.Demand) {
assert(demand > 0)
guard let downstream = downstream else { return }
guard let downstream = downstream.read({ $0 }) else { return }
self.downstream = nil
self.downstream.write(nil)
responseHandler { response in
_ = downstream.receive(response)
downstream.receive(completion: .finished)
@@ -487,7 +484,7 @@ public struct DownloadResponsePublisher<Value>: Publisher {
func cancel() {
request.cancel()
downstream = nil
downstream.write(nil)
}
}
}

View File

@@ -37,7 +37,7 @@ extension Request {
/// - Returns: The `StreamOf<Progress>`.
public func uploadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
uploadProgress(queue: .singleEventQueue) { progress in
uploadProgress(queue: underlyingQueue) { progress in
continuation.yield(progress)
}
}
@@ -50,7 +50,7 @@ extension Request {
/// - Returns: The `StreamOf<Progress>`.
public func downloadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
downloadProgress(queue: .singleEventQueue) { progress in
downloadProgress(queue: underlyingQueue) { progress in
continuation.yield(progress)
}
}
@@ -63,7 +63,7 @@ extension Request {
/// - Returns: The `StreamOf<URLRequest>`.
public func urlRequests(bufferingPolicy: StreamOf<URLRequest>.BufferingPolicy = .unbounded) -> StreamOf<URLRequest> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
onURLRequestCreation(on: .singleEventQueue) { request in
onURLRequestCreation(on: underlyingQueue) { request in
continuation.yield(request)
}
}
@@ -76,7 +76,7 @@ extension Request {
/// - Returns: The `StreamOf<URLSessionTask>`.
public func urlSessionTasks(bufferingPolicy: StreamOf<URLSessionTask>.BufferingPolicy = .unbounded) -> StreamOf<URLSessionTask> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
onURLSessionTaskCreation(on: .singleEventQueue) { task in
onURLSessionTaskCreation(on: underlyingQueue) { task in
continuation.yield(task)
}
}
@@ -89,13 +89,13 @@ extension Request {
/// - Returns: The `StreamOf<String>`.
public func cURLDescriptions(bufferingPolicy: StreamOf<String>.BufferingPolicy = .unbounded) -> StreamOf<String> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
cURLDescription(on: .singleEventQueue) { description in
cURLDescription(on: underlyingQueue) { description in
continuation.yield(description)
}
}
}
private func stream<T>(of type: T.Type = T.self,
fileprivate func stream<T>(of type: T.Type = T.self,
bufferingPolicy: StreamOf<T>.BufferingPolicy = .unbounded,
yielder: @escaping (StreamOf<T>.Continuation) -> Void) -> StreamOf<T> {
StreamOf<T>(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
@@ -168,18 +168,83 @@ public struct DataTask<Value> {
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension DataRequest {
/// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
///
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
///
/// - Returns: The `StreamOf<HTTPURLResponse>`.
public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
onHTTPResponse(on: underlyingQueue) { response in
continuation.yield(response)
}
}
}
#if swift(>=5.7)
/// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataRequest` produces an
/// `HTTPURLResponse`.
///
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
/// where responses after the first will contain the part headers.
///
/// - Parameters:
/// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
/// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
/// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
/// so any synchronous calls in it will execute in that context.
///
/// - Returns: The instance.
@_disfavoredOverload
@discardableResult
public func onHTTPResponse(
perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> ResponseDisposition
) -> Self {
onHTTPResponse(on: underlyingQueue) { response, completionHandler in
Task {
let disposition = await handler(response)
completionHandler(disposition)
}
}
return self
}
/// Sets an async closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
///
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
/// where responses after the first will contain the part headers.
///
/// - Parameters:
/// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
/// arbitrary thread, so any synchronous calls in it will execute in that context.
///
/// - Returns: The instance.
@discardableResult
public func onHTTPResponse(perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> Void) -> Self {
onHTTPResponse { response in
await handler(response)
return .allow
}
return self
}
#endif
/// Creates a `DataTask` to `await` a `Data` value.
///
/// - Parameters:
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
/// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
///
/// - Returns: The `DataTask`.
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataTask<Data> {
@@ -195,7 +260,7 @@ extension DataRequest {
/// - type: `Decodable` type to decode from response data.
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
/// `PassthroughPreprocessor()` by default.
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
@@ -204,7 +269,7 @@ extension DataRequest {
///
/// - Returns: The `DataTask`.
public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
decoder: DataDecoder = JSONDecoder(),
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
@@ -221,7 +286,7 @@ extension DataRequest {
/// - Parameters:
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
/// `PassthroughPreprocessor()` by default.
/// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
@@ -231,7 +296,7 @@ extension DataRequest {
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
///
/// - Returns: The `DataTask`.
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
encoding: String.Encoding? = nil,
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
@@ -249,14 +314,14 @@ extension DataRequest {
/// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
///
/// - Returns: The `DataTask`.
public func serializingResponse<Serializer: ResponseSerializer>(using serializer: Serializer,
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
-> DataTask<Serializer.SerializedObject> {
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) {
self.response(queue: .singleEventQueue,
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
response(queue: underlyingQueue,
responseSerializer: serializer,
completionHandler: $0)
}
@@ -269,14 +334,14 @@ extension DataRequest {
/// response, and data.
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
///
/// - Returns: The `DataTask`.
public func serializingResponse<Serializer: DataResponseSerializerProtocol>(using serializer: Serializer,
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
-> DataTask<Serializer.SerializedObject> {
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) {
self.response(queue: .singleEventQueue,
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
response(queue: underlyingQueue,
responseSerializer: serializer,
completionHandler: $0)
}
@@ -366,13 +431,13 @@ extension DownloadRequest {
/// - Parameters:
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
/// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
///
/// - Returns: The `DownloadTask`.
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<Data> {
@@ -390,7 +455,7 @@ extension DownloadRequest {
/// - type: `Decodable` type to decode from response data.
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
/// `PassthroughPreprocessor()` by default.
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
@@ -399,7 +464,7 @@ extension DownloadRequest {
///
/// - Returns: The `DownloadTask`.
public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
decoder: DataDecoder = JSONDecoder(),
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
@@ -416,10 +481,10 @@ extension DownloadRequest {
/// - Parameters:
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
///
/// - Returns: The `DownloadTask`.
public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = false) -> DownloadTask<URL> {
public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = true) -> DownloadTask<URL> {
serializingDownload(using: URLResponseSerializer(),
automaticallyCancelling: shouldAutomaticallyCancel)
}
@@ -429,7 +494,7 @@ extension DownloadRequest {
/// - Parameters:
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the
/// serializer. `PassthroughPreprocessor()` by default.
/// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
@@ -439,7 +504,7 @@ extension DownloadRequest {
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
///
/// - Returns: The `DownloadTask`.
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
encoding: String.Encoding? = nil,
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
@@ -457,14 +522,14 @@ extension DownloadRequest {
/// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
///
/// - Returns: The `DownloadTask`.
public func serializingDownload<Serializer: ResponseSerializer>(using serializer: Serializer,
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
-> DownloadTask<Serializer.SerializedObject> {
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) {
self.response(queue: .singleEventQueue,
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
response(queue: underlyingQueue,
responseSerializer: serializer,
completionHandler: $0)
}
@@ -478,14 +543,14 @@ extension DownloadRequest {
/// response, and data.
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
/// properties. `false` by default.
/// properties. `true` by default.
///
/// - Returns: The `DownloadTask`.
public func serializingDownload<Serializer: DownloadResponseSerializerProtocol>(using serializer: Serializer,
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
-> DownloadTask<Serializer.SerializedObject> {
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) {
self.response(queue: .singleEventQueue,
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
response(queue: underlyingQueue,
responseSerializer: serializer,
completionHandler: $0)
}
@@ -625,6 +690,69 @@ public struct DataStreamTask {
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension DataStreamRequest {
/// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
///
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
///
/// - Returns: The `StreamOf<HTTPURLResponse>`.
public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
onHTTPResponse(on: underlyingQueue) { response in
continuation.yield(response)
}
}
}
#if swift(>=5.7)
/// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataStreamRequest`
/// produces an `HTTPURLResponse`.
///
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
/// where responses after the first will contain the part headers.
///
/// - Parameters:
/// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
/// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
/// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
/// so any synchronous calls in it will execute in that context.
///
/// - Returns: The instance.
@_disfavoredOverload
@discardableResult
public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> ResponseDisposition) -> Self {
onHTTPResponse(on: underlyingQueue) { response, completionHandler in
Task {
let disposition = await handler(response)
completionHandler(disposition)
}
}
return self
}
/// Sets an async closure called whenever the `DataStreamRequest` produces an `HTTPURLResponse`.
///
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
/// where responses after the first will contain the part headers.
///
/// - Parameters:
/// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
/// arbitrary thread, so any synchronous calls in it will execute in that context.
///
/// - Returns: The instance.
@discardableResult
public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> Void) -> Self {
onHTTPResponse { response in
await handler(response)
return .allow
}
return self
}
#endif
/// Creates a `DataStreamTask` used to `await` streams of serialized values.
///
/// - Returns: The `DataStreamTask`.

View File

@@ -69,6 +69,9 @@ public protocol EventMonitor {
// MARK: URLSessionDataDelegate Events
/// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:completionHandler:)` method.
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse)
/// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method.
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
@@ -244,6 +247,7 @@ extension EventMonitor {
didFinishCollecting metrics: URLSessionTaskMetrics) {}
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {}
public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {}
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {}
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {}
public func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
@@ -380,6 +384,10 @@ public final class CompositeEventMonitor: EventMonitor {
performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) }
}
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {
performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: response) }
}
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) }
}
@@ -593,6 +601,9 @@ open class ClosureEventMonitor: EventMonitor {
/// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event.
open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)?
/// Closure called on the `urlSession(_:dataTask:didReceive:completionHandler:)` event.
open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> Void)?
/// Closure that receives the `urlSession(_:dataTask:didReceive:)` event.
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
@@ -741,6 +752,10 @@ open class ClosureEventMonitor: EventMonitor {
taskIsWaitingForConnectivity?(session, task)
}
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {
dataTaskDidReceiveResponse?(session, dataTask, response)
}
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
dataTaskDidReceiveData?(session, dataTask, data)
}

View File

@@ -34,16 +34,12 @@ public struct HTTPHeaders {
/// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last
/// name and value encountered.
public init(_ headers: [HTTPHeader]) {
self.init()
headers.forEach { update($0) }
}
/// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name
/// and value encountered.
public init(_ dictionary: [String: String]) {
self.init()
dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) }
}
@@ -145,8 +141,6 @@ public struct HTTPHeaders {
extension HTTPHeaders: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, String)...) {
self.init()
elements.forEach { update(name: $0.0, value: $0.1) }
}
}
@@ -405,6 +399,8 @@ extension HTTPHeader {
return "Linux"
#elseif os(Windows)
return "Windows"
#elseif os(Android)
return "Android"
#else
return "Unknown"
#endif

View File

@@ -24,9 +24,9 @@
import Foundation
#if os(iOS) || os(watchOS) || os(tvOS)
#if canImport(MobileCoreServices)
import MobileCoreServices
#elseif os(macOS)
#elseif canImport(CoreServices)
import CoreServices
#endif
@@ -213,7 +213,7 @@ open class MultipartFormData {
// Check 2 - is file URL reachable?
//============================================================
#if !(os(Linux) || os(Windows))
#if !(os(Linux) || os(Windows) || os(Android))
do {
let isReachable = try fileURL.checkPromisedItemIsReachable()
guard isReachable else {
@@ -455,9 +455,11 @@ open class MultipartFormData {
inputStream.open()
defer { inputStream.close() }
while inputStream.hasBytesAvailable {
var buffer = [UInt8](repeating: 0, count: streamBufferSize)
let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
var bytesLeftToRead = bodyPart.bodyContentLength
while inputStream.hasBytesAvailable && bytesLeftToRead > 0 {
let bufferSize = min(streamBufferSize, Int(bytesLeftToRead))
var buffer = [UInt8](repeating: 0, count: bufferSize)
let bytesRead = inputStream.read(&buffer, maxLength: bufferSize)
if let streamError = inputStream.streamError {
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
@@ -469,6 +471,7 @@ open class MultipartFormData {
}
try write(&buffer, to: outputStream)
bytesLeftToRead -= UInt64(bytesRead)
} else {
break
}
@@ -549,6 +552,19 @@ extension MultipartFormData {
// MARK: - Private - Mime Type
private func mimeType(forPathExtension pathExtension: String) -> String {
#if swift(>=5.9)
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) {
return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
} else {
if
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
return contentType as String
}
return "application/octet-stream"
}
#else
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) {
return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
} else {
@@ -560,6 +576,7 @@ extension MultipartFormData {
return "application/octet-stream"
}
#endif
}
}
@@ -569,7 +586,7 @@ extension MultipartFormData {
// MARK: - Private - Mime Type
private func mimeType(forPathExtension pathExtension: String) -> String {
#if !(os(Linux) || os(Windows))
#if canImport(CoreServices) || canImport(MobileCoreServices)
if
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {

View File

@@ -28,8 +28,8 @@ import Foundation
final class MultipartUpload {
lazy var result = Result { try build() }
@Protected
private(set) var multipartFormData: MultipartFormData
private let multipartFormData: Protected<MultipartFormData>
let encodingMemoryThreshold: UInt64
let request: URLRequestConvertible
let fileManager: FileManager
@@ -40,13 +40,13 @@ final class MultipartUpload {
self.encodingMemoryThreshold = encodingMemoryThreshold
self.request = request
fileManager = multipartFormData.fileManager
self.multipartFormData = multipartFormData
self.multipartFormData = Protected(multipartFormData)
}
func build() throws -> UploadRequest.Uploadable {
let uploadable: UploadRequest.Uploadable
if $multipartFormData.contentLength < encodingMemoryThreshold {
let data = try $multipartFormData.read { try $0.encode() }
if multipartFormData.contentLength < encodingMemoryThreshold {
let data = try multipartFormData.read { try $0.encode() }
uploadable = .data(data)
} else {
@@ -58,7 +58,7 @@ final class MultipartUpload {
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
do {
try $multipartFormData.read { try $0.writeEncodedData(to: fileURL) }
try multipartFormData.read { try $0.writeEncodedData(to: fileURL) }
} catch {
// Cleanup after attempted write if it fails.
try? fileManager.removeItem(at: fileURL)
@@ -76,7 +76,7 @@ extension MultipartUpload: UploadConvertible {
func asURLRequest() throws -> URLRequest {
var urlRequest = try request.asURLRequest()
$multipartFormData.read { multipartFormData in
multipartFormData.read { multipartFormData in
urlRequest.headers.add(.contentType(multipartFormData.contentType))
}

View File

@@ -22,7 +22,7 @@
// THE SOFTWARE.
//
#if !(os(watchOS) || os(Linux) || os(Windows))
#if canImport(SystemConfiguration)
import Foundation
import SystemConfiguration
@@ -113,8 +113,7 @@ open class NetworkReachabilityManager {
private let reachability: SCNetworkReachability
/// Protected storage for mutable state.
@Protected
private var mutableState = MutableState()
private let mutableState = Protected(MutableState())
// MARK: - Initialization
@@ -168,7 +167,7 @@ open class NetworkReachabilityManager {
onUpdatePerforming listener: @escaping Listener) -> Bool {
stopListening()
$mutableState.write { state in
mutableState.write { state in
state.listenerQueue = queue
state.listener = listener
}
@@ -194,7 +193,8 @@ open class NetworkReachabilityManager {
let description = weakManager.manager?.flags?.readableDescription ?? "nil"
return Unmanaged.passRetained(description as CFString)
})
}
)
let callback: SCNetworkReachabilityCallBack = { _, flags, info in
guard let info = info else { return }
@@ -219,7 +219,7 @@ open class NetworkReachabilityManager {
open func stopListening() {
SCNetworkReachabilitySetCallback(reachability, nil, nil)
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
$mutableState.write { state in
mutableState.write { state in
state.listener = nil
state.listenerQueue = nil
state.previousStatus = nil
@@ -236,7 +236,7 @@ open class NetworkReachabilityManager {
func notifyListener(_ flags: SCNetworkReachabilityFlags) {
let newStatus = NetworkReachabilityStatus(flags)
$mutableState.write { state in
mutableState.write { state in
guard state.previousStatus != newStatus else { return }
state.previousStatus = newStatus
@@ -266,7 +266,7 @@ extension SCNetworkReachabilityFlags {
var canConnectWithoutUserInteraction: Bool { canConnectAutomatically && !contains(.interventionRequired) }
var isActuallyReachable: Bool { isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) }
var isCellular: Bool {
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))
return contains(.isWWAN)
#else
return false

View File

@@ -49,13 +49,7 @@ extension Lock {
}
}
#if os(Linux) || os(Windows)
extension NSLock: Lock {}
#endif
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
#if canImport(Darwin)
/// An `os_unfair_lock` wrapper.
final class UnfairLock: Lock {
private let unfairLock: os_unfair_lock_t
@@ -78,41 +72,35 @@ final class UnfairLock: Lock {
os_unfair_lock_unlock(unfairLock)
}
}
#elseif canImport(Foundation)
extension NSLock: Lock {}
#else
#error("This platform needs a Lock-conforming type without Foundation.")
#endif
/// A thread-safe wrapper around a value.
@propertyWrapper
@dynamicMemberLookup
final class Protected<T> {
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
final class Protected<Value> {
#if canImport(Darwin)
private let lock = UnfairLock()
#elseif os(Linux) || os(Windows)
#elseif canImport(Foundation)
private let lock = NSLock()
#else
#error("This platform needs a Lock-conforming type without Foundation.")
#endif
private var value: T
private var value: Value
init(_ value: T) {
init(_ value: Value) {
self.value = value
}
/// The contained value. Unsafe for anything more than direct read or write.
var wrappedValue: T {
get { lock.around { value } }
set { lock.around { value = newValue } }
}
var projectedValue: Protected<T> { self }
init(wrappedValue: T) {
value = wrappedValue
}
/// Synchronously read or transform the contained value.
///
/// - Parameter closure: The closure to execute.
///
/// - Returns: The return value of the closure passed.
func read<U>(_ closure: (T) throws -> U) rethrows -> U {
func read<U>(_ closure: (Value) throws -> U) rethrows -> U {
try lock.around { try closure(self.value) }
}
@@ -122,21 +110,28 @@ final class Protected<T> {
///
/// - Returns: The modified value.
@discardableResult
func write<U>(_ closure: (inout T) throws -> U) rethrows -> U {
func write<U>(_ closure: (inout Value) throws -> U) rethrows -> U {
try lock.around { try closure(&self.value) }
}
subscript<Property>(dynamicMember keyPath: WritableKeyPath<T, Property>) -> Property {
/// Synchronously update the protected value.
///
/// - Parameter value: The `Value`.
func write(_ value: Value) {
write { $0 = value }
}
subscript<Property>(dynamicMember keyPath: WritableKeyPath<Value, Property>) -> Property {
get { lock.around { value[keyPath: keyPath] } }
set { lock.around { value[keyPath: keyPath] = newValue } }
}
subscript<Property>(dynamicMember keyPath: KeyPath<T, Property>) -> Property {
subscript<Property>(dynamicMember keyPath: KeyPath<Value, Property>) -> Property {
lock.around { value[keyPath: keyPath] }
}
}
extension Protected where T == Request.MutableState {
extension Protected where Value == Request.MutableState {
/// Attempts to transition to the passed `State`.
///
/// - Parameter state: The `State` to attempt transition to.
@@ -159,3 +154,15 @@ extension Protected where T == Request.MutableState {
lock.around { perform(value.state) }
}
}
extension Protected: Equatable where Value: Equatable {
static func ==(lhs: Protected<Value>, rhs: Protected<Value>) -> Bool {
lhs.read { left in rhs.read { right in left == right }}
}
}
extension Protected: Hashable where Value: Hashable {
func hash(into hasher: inout Hasher) {
read { hasher.combine($0) }
}
}

View File

@@ -125,11 +125,10 @@ public class Request {
}
/// Protected `MutableState` value that provides thread-safe access to state values.
@Protected
fileprivate var mutableState = MutableState()
fileprivate let mutableState = Protected(MutableState())
/// `State` of the `Request`.
public var state: State { $mutableState.state }
public var state: State { mutableState.state }
/// Returns whether `state` is `.initialized`.
public var isInitialized: Bool { state == .initialized }
/// Returns whether `state is `.resumed`.
@@ -152,50 +151,49 @@ public class Request {
public let downloadProgress = Progress(totalUnitCount: 0)
/// `ProgressHandler` called when `uploadProgress` is updated, on the provided `DispatchQueue`.
private var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? {
get { $mutableState.uploadProgressHandler }
set { $mutableState.uploadProgressHandler = newValue }
get { mutableState.uploadProgressHandler }
set { mutableState.uploadProgressHandler = newValue }
}
/// `ProgressHandler` called when `downloadProgress` is updated, on the provided `DispatchQueue`.
fileprivate var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? {
get { $mutableState.downloadProgressHandler }
set { $mutableState.downloadProgressHandler = newValue }
get { mutableState.downloadProgressHandler }
set { mutableState.downloadProgressHandler = newValue }
}
// MARK: Redirect Handling
/// `RedirectHandler` set on the instance.
public private(set) var redirectHandler: RedirectHandler? {
get { $mutableState.redirectHandler }
set { $mutableState.redirectHandler = newValue }
get { mutableState.redirectHandler }
set { mutableState.redirectHandler = newValue }
}
// MARK: Cached Response Handling
/// `CachedResponseHandler` set on the instance.
public private(set) var cachedResponseHandler: CachedResponseHandler? {
get { $mutableState.cachedResponseHandler }
set { $mutableState.cachedResponseHandler = newValue }
get { mutableState.cachedResponseHandler }
set { mutableState.cachedResponseHandler = newValue }
}
// MARK: URLCredential
/// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods.
public private(set) var credential: URLCredential? {
get { $mutableState.credential }
set { $mutableState.credential = newValue }
get { mutableState.credential }
set { mutableState.credential = newValue }
}
// MARK: Validators
/// `Validator` callback closures that store the validation calls enqueued.
@Protected
fileprivate var validators: [() -> Void] = []
fileprivate let validators = Protected<[() -> Void]>([])
// MARK: URLRequests
/// All `URLRequests` created on behalf of the `Request`, including original and adapted requests.
public var requests: [URLRequest] { $mutableState.requests }
public var requests: [URLRequest] { mutableState.requests }
/// First `URLRequest` created on behalf of the `Request`. May not be the first one actually executed.
public var firstRequest: URLRequest? { requests.first }
/// Last `URLRequest` created on behalf of the `Request`.
@@ -205,7 +203,7 @@ public class Request {
/// `URLRequest`s from all of the `URLSessionTask`s executed on behalf of the `Request`. May be different from
/// `requests` due to `URLSession` manipulation.
public var performedRequests: [URLRequest] { $mutableState.read { $0.tasks.compactMap(\.currentRequest) } }
public var performedRequests: [URLRequest] { mutableState.read { $0.tasks.compactMap(\.currentRequest) } }
// MARK: HTTPURLResponse
@@ -216,7 +214,7 @@ public class Request {
// MARK: Tasks
/// All `URLSessionTask`s created on behalf of the `Request`.
public var tasks: [URLSessionTask] { $mutableState.tasks }
public var tasks: [URLSessionTask] { mutableState.tasks }
/// First `URLSessionTask` created on behalf of the `Request`.
public var firstTask: URLSessionTask? { tasks.first }
/// Last `URLSessionTask` created on behalf of the `Request`.
@@ -227,7 +225,7 @@ public class Request {
// MARK: Metrics
/// All `URLSessionTaskMetrics` gathered on behalf of the `Request`. Should correspond to the `tasks` created.
public var allMetrics: [URLSessionTaskMetrics] { $mutableState.metrics }
public var allMetrics: [URLSessionTaskMetrics] { mutableState.metrics }
/// First `URLSessionTaskMetrics` gathered on behalf of the `Request`.
public var firstMetrics: URLSessionTaskMetrics? { allMetrics.first }
/// Last `URLSessionTaskMetrics` gathered on behalf of the `Request`.
@@ -238,14 +236,14 @@ public class Request {
// MARK: Retry Count
/// Number of times the `Request` has been retried.
public var retryCount: Int { $mutableState.retryCount }
public var retryCount: Int { mutableState.retryCount }
// MARK: Error
/// `Error` returned from Alamofire internally, from the network request directly, or any validators executed.
public fileprivate(set) var error: AFError? {
get { $mutableState.error }
set { $mutableState.error = newValue }
get { mutableState.error }
set { mutableState.error = newValue }
}
/// Default initializer for the `Request` superclass.
@@ -283,7 +281,7 @@ public class Request {
func didCreateInitialURLRequest(_ request: URLRequest) {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
$mutableState.write { $0.requests.append(request) }
mutableState.write { $0.requests.append(request) }
eventMonitor?.request(self, didCreateInitialURLRequest: request)
}
@@ -313,7 +311,7 @@ public class Request {
func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
$mutableState.write { $0.requests.append(adaptedRequest) }
mutableState.write { $0.requests.append(adaptedRequest) }
eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest)
}
@@ -343,7 +341,7 @@ public class Request {
func didCreateURLRequest(_ request: URLRequest) {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
$mutableState.read { state in
mutableState.read { state in
state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) }
}
@@ -354,7 +352,7 @@ public class Request {
/// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`.
private func callCURLHandlerIfNecessary() {
$mutableState.write { mutableState in
mutableState.write { mutableState in
guard let cURLHandler = mutableState.cURLHandler else { return }
cURLHandler.queue.async { cURLHandler.handler(self.cURLDescription()) }
@@ -369,7 +367,7 @@ public class Request {
func didCreateTask(_ task: URLSessionTask) {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
$mutableState.write { state in
mutableState.write { state in
state.tasks.append(task)
guard let urlSessionTaskHandler = state.urlSessionTaskHandler else { return }
@@ -416,7 +414,9 @@ public class Request {
func didCancel() {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
error = error ?? AFError.explicitlyCancelled
mutableState.write { mutableState in
mutableState.error = mutableState.error ?? AFError.explicitlyCancelled
}
eventMonitor?.requestDidCancel(self)
}
@@ -436,7 +436,7 @@ public class Request {
func didGatherMetrics(_ metrics: URLSessionTaskMetrics) {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
$mutableState.write { $0.metrics.append(metrics) }
mutableState.write { $0.metrics.append(metrics) }
eventMonitor?.request(self, didGatherMetrics: metrics)
}
@@ -468,6 +468,7 @@ public class Request {
self.error = self.error ?? error
let validators = validators.read { $0 }
validators.forEach { $0() }
eventMonitor?.request(self, didCompleteTask: task, with: error)
@@ -479,7 +480,7 @@ public class Request {
func prepareForRetry() {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
$mutableState.write { $0.retryCount += 1 }
mutableState.write { $0.retryCount += 1 }
reset()
@@ -513,9 +514,9 @@ public class Request {
func finish(error: AFError? = nil) {
dispatchPrecondition(condition: .onQueue(underlyingQueue))
guard !$mutableState.isFinishing else { return }
guard !mutableState.isFinishing else { return }
$mutableState.isFinishing = true
mutableState.isFinishing = true
if let error = error { self.error = error }
@@ -531,7 +532,7 @@ public class Request {
///
/// - Parameter closure: The closure containing the response serialization call.
func appendResponseSerializer(_ closure: @escaping () -> Void) {
$mutableState.write { mutableState in
mutableState.write { mutableState in
mutableState.responseSerializers.append(closure)
if mutableState.state == .finished {
@@ -554,7 +555,7 @@ public class Request {
func nextResponseSerializer() -> (() -> Void)? {
var responseSerializer: (() -> Void)?
$mutableState.write { mutableState in
mutableState.write { mutableState in
let responseSerializerIndex = mutableState.responseSerializerCompletions.count
if responseSerializerIndex < mutableState.responseSerializers.count {
@@ -571,7 +572,7 @@ public class Request {
// Execute all response serializer completions and clear them
var completions: [() -> Void] = []
$mutableState.write { mutableState in
mutableState.write { mutableState in
completions = mutableState.responseSerializerCompletions
// Clear out all response serializers and response serializer completions in mutable state since the
@@ -605,7 +606,7 @@ public class Request {
/// - Parameter completion: The completion handler provided with the response serializer, called when all serializers
/// are complete.
func responseSerializerDidComplete(completion: @escaping () -> Void) {
$mutableState.write { $0.responseSerializerCompletions.append(completion) }
mutableState.write { $0.responseSerializerCompletions.append(completion) }
processNextResponseSerializer()
}
@@ -618,7 +619,7 @@ public class Request {
downloadProgress.totalUnitCount = 0
downloadProgress.completedUnitCount = 0
$mutableState.write { state in
mutableState.write { state in
state.isFinishing = false
state.responseSerializerCompletions = []
}
@@ -640,7 +641,7 @@ public class Request {
///
/// - Parameter perform: The closure to perform.
func withState(perform: (State) -> Void) {
$mutableState.withState(perform: perform)
mutableState.withState(perform: perform)
}
// MARK: Task Creation
@@ -667,7 +668,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func cancel() -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
guard mutableState.state.canTransitionTo(.cancelled) else { return }
mutableState.state = .cancelled
@@ -693,7 +694,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func suspend() -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
guard mutableState.state.canTransitionTo(.suspended) else { return }
mutableState.state = .suspended
@@ -714,7 +715,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func resume() -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
guard mutableState.state.canTransitionTo(.resumed) else { return }
mutableState.state = .resumed
@@ -754,7 +755,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func authenticate(with credential: URLCredential) -> Self {
$mutableState.credential = credential
mutableState.credential = credential
return self
}
@@ -770,7 +771,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func downloadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self {
$mutableState.downloadProgressHandler = (handler: closure, queue: queue)
mutableState.downloadProgressHandler = (handler: closure, queue: queue)
return self
}
@@ -786,7 +787,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func uploadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self {
$mutableState.uploadProgressHandler = (handler: closure, queue: queue)
mutableState.uploadProgressHandler = (handler: closure, queue: queue)
return self
}
@@ -802,7 +803,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func redirect(using handler: RedirectHandler) -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.")
mutableState.redirectHandler = handler
}
@@ -821,7 +822,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func cacheResponse(using handler: CachedResponseHandler) -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.")
mutableState.cachedResponseHandler = handler
}
@@ -842,7 +843,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func cURLDescription(on queue: DispatchQueue, calling handler: @escaping (String) -> Void) -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
if mutableState.requests.last != nil {
queue.async { handler(self.cURLDescription()) }
} else {
@@ -863,13 +864,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self {
$mutableState.write { mutableState in
if mutableState.requests.last != nil {
underlyingQueue.async { handler(self.cURLDescription()) }
} else {
mutableState.cURLHandler = (underlyingQueue, handler)
}
}
cURLDescription(on: underlyingQueue, calling: handler)
return self
}
@@ -885,7 +880,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func onURLRequestCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLRequest) -> Void) -> Self {
$mutableState.write { state in
mutableState.write { state in
if let request = state.requests.last {
queue.async { handler(request) }
}
@@ -909,7 +904,7 @@ public class Request {
/// - Returns: The instance.
@discardableResult
public func onURLSessionTaskCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLSessionTask) -> Void) -> Self {
$mutableState.write { state in
mutableState.write { state in
if let task = state.tasks.last {
queue.async { handler(task) }
}
@@ -928,19 +923,37 @@ public class Request {
func onFinish(perform finishHandler: @escaping () -> Void) {
guard !isFinished else { finishHandler(); return }
$mutableState.write { state in
mutableState.write { state in
state.finishHandlers.append(finishHandler)
}
}
/// Final cleanup step executed when the instance finishes response serialization.
func cleanup() {
delegate?.cleanup(after: self)
let handlers = $mutableState.finishHandlers
let handlers = mutableState.finishHandlers
handlers.forEach { $0() }
$mutableState.write { state in
mutableState.write { state in
state.finishHandlers.removeAll()
}
delegate?.cleanup(after: self)
}
}
extension Request {
/// Type indicating how a `DataRequest` or `DataStreamRequest` should proceed after receiving an `HTTPURLResponse`.
public enum ResponseDisposition {
/// Allow the request to continue normally.
case allow
/// Cancel the request, similar to calling `cancel()`.
case cancel
var sessionDisposition: URLSession.ResponseDisposition {
switch self {
case .allow: return .allow
case .cancel: return .cancel
}
}
}
}
@@ -1085,11 +1098,16 @@ public class DataRequest: Request {
/// `URLRequestConvertible` value used to create `URLRequest`s for this instance.
public let convertible: URLRequestConvertible
/// `Data` read from the server so far.
public var data: Data? { mutableData }
public var data: Data? { dataMutableState.data }
/// Protected storage for the `Data` read by the instance.
@Protected
private var mutableData: Data? = nil
private struct DataMutableState {
var data: Data?
var httpResponseHandler: (queue: DispatchQueue,
handler: (_ response: HTTPURLResponse,
_ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void)?
}
private let dataMutableState = Protected(DataMutableState())
/// Creates a `DataRequest` using the provided parameters.
///
@@ -1122,7 +1140,9 @@ public class DataRequest: Request {
override func reset() {
super.reset()
mutableData = nil
dataMutableState.write { mutableState in
mutableState.data = nil
}
}
/// Called when `Data` is received by this instance.
@@ -1131,15 +1151,41 @@ public class DataRequest: Request {
///
/// - Parameter data: The `Data` received.
func didReceive(data: Data) {
if self.data == nil {
mutableData = data
dataMutableState.write { mutableState in
if mutableState.data == nil {
mutableState.data = data
} else {
$mutableData.write { $0?.append(data) }
mutableState.data?.append(data)
}
}
updateDownloadProgress()
}
func didReceiveResponse(_ response: HTTPURLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
dataMutableState.read { dataMutableState in
guard let httpResponseHandler = dataMutableState.httpResponseHandler else {
underlyingQueue.async { completionHandler(.allow) }
return
}
httpResponseHandler.queue.async {
httpResponseHandler.handler(response) { disposition in
if disposition == .cancel {
self.mutableState.write { mutableState in
mutableState.state = .cancelled
mutableState.error = mutableState.error ?? AFError.explicitlyCancelled
}
}
self.underlyingQueue.async {
completionHandler(disposition.sessionDisposition)
}
}
}
}
}
override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
let copiedRequest = request
return session.dataTask(with: copiedRequest)
@@ -1179,7 +1225,48 @@ public class DataRequest: Request {
withResult: result)
}
$validators.write { $0.append(validator) }
validators.write { $0.append(validator) }
return self
}
/// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse` and providing a completion
/// handler to return a `ResponseDisposition` value.
///
/// - Parameters:
/// - queue: `DispatchQueue` on which the closure will be called. `.main` by default.
/// - handler: Closure called when the instance produces an `HTTPURLResponse`. The `completionHandler` provided
/// MUST be called, otherwise the request will never complete.
///
/// - Returns: The instance.
@_disfavoredOverload
@discardableResult
public func onHTTPResponse(
on queue: DispatchQueue = .main,
perform handler: @escaping (_ response: HTTPURLResponse,
_ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void
) -> Self {
dataMutableState.write { mutableState in
mutableState.httpResponseHandler = (queue, handler)
}
return self
}
/// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
///
/// - Parameters:
/// - queue: `DispatchQueue` on which the closure will be called. `.main` by default.
/// - handler: Closure called when the instance produces an `HTTPURLResponse`.
///
/// - Returns: The instance.
@discardableResult
public func onHTTPResponse(on queue: DispatchQueue = .main,
perform handler: @escaping (HTTPURLResponse) -> Void) -> Self {
onHTTPResponse(on: queue) { response, completionHandler in
handler(response)
completionHandler(.allow)
}
return self
}
@@ -1259,10 +1346,13 @@ public final class DataStreamRequest: Request {
var numberOfExecutingStreams = 0
/// Completion calls enqueued while streams are still executing.
var enqueuedCompletionEvents: [() -> Void] = []
/// Handler for any `HTTPURLResponse`s received.
var httpResponseHandler: (queue: DispatchQueue,
handler: (_ response: HTTPURLResponse,
_ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void)?
}
@Protected
var streamMutableState = StreamMutableState()
let streamMutableState = Protected(StreamMutableState())
/// Creates a `DataStreamRequest` using the provided parameters.
///
@@ -1306,7 +1396,7 @@ public final class DataStreamRequest: Request {
}
override func finish(error: AFError? = nil) {
$streamMutableState.write { state in
streamMutableState.write { state in
state.outputStream?.close()
}
@@ -1314,8 +1404,8 @@ public final class DataStreamRequest: Request {
}
func didReceive(data: Data) {
$streamMutableState.write { state in
#if !(os(Linux) || os(Windows))
streamMutableState.write { state in
#if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation.
if let stream = state.outputStream {
underlyingQueue.async {
var bytes = Array(data)
@@ -1329,6 +1419,30 @@ public final class DataStreamRequest: Request {
}
}
func didReceiveResponse(_ response: HTTPURLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
streamMutableState.read { dataMutableState in
guard let httpResponseHandler = dataMutableState.httpResponseHandler else {
underlyingQueue.async { completionHandler(.allow) }
return
}
httpResponseHandler.queue.async {
httpResponseHandler.handler(response) { disposition in
if disposition == .cancel {
self.mutableState.write { mutableState in
mutableState.state = .cancelled
mutableState.error = mutableState.error ?? AFError.explicitlyCancelled
}
}
self.underlyingQueue.async {
completionHandler(disposition.sessionDisposition)
}
}
}
}
}
/// Validates the `URLRequest` and `HTTPURLResponse` received for the instance using the provided `Validation` closure.
///
/// - Parameter validation: `Validation` closure used to validate the request and response.
@@ -1351,12 +1465,12 @@ public final class DataStreamRequest: Request {
withResult: result)
}
$validators.write { $0.append(validator) }
validators.write { $0.append(validator) }
return self
}
#if !(os(Linux) || os(Windows))
#if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation.
/// Produces an `InputStream` that receives the `Data` received by the instance.
///
/// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`.
@@ -1370,7 +1484,7 @@ public final class DataStreamRequest: Request {
defer { resume() }
var inputStream: InputStream?
$streamMutableState.write { state in
streamMutableState.write { state in
Foundation.Stream.getBoundStreams(withBufferSize: bufferSize,
inputStream: &inputStream,
outputStream: &state.outputStream)
@@ -1381,6 +1495,47 @@ public final class DataStreamRequest: Request {
}
#endif
/// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse` and providing a completion
/// handler to return a `ResponseDisposition` value.
///
/// - Parameters:
/// - queue: `DispatchQueue` on which the closure will be called. `.main` by default.
/// - handler: Closure called when the instance produces an `HTTPURLResponse`. The `completionHandler` provided
/// MUST be called, otherwise the request will never complete.
///
/// - Returns: The instance.
@_disfavoredOverload
@discardableResult
public func onHTTPResponse(
on queue: DispatchQueue = .main,
perform handler: @escaping (_ response: HTTPURLResponse,
_ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void
) -> Self {
streamMutableState.write { mutableState in
mutableState.httpResponseHandler = (queue, handler)
}
return self
}
/// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
///
/// - Parameters:
/// - queue: `DispatchQueue` on which the closure will be called. `.main` by default.
/// - handler: Closure called when the instance produces an `HTTPURLResponse`.
///
/// - Returns: The instance.
@discardableResult
public func onHTTPResponse(on queue: DispatchQueue = .main,
perform handler: @escaping (HTTPURLResponse) -> Void) -> Self {
onHTTPResponse(on: queue) { response, completionHandler in
handler(response)
completionHandler(.allow)
}
return self
}
func capturingError(from closure: () throws -> Void) {
do {
try closure()
@@ -1395,7 +1550,7 @@ public final class DataStreamRequest: Request {
appendResponseSerializer {
self.underlyingQueue.async {
self.responseSerializerDidComplete {
self.$streamMutableState.write { state in
self.streamMutableState.write { state in
guard state.numberOfExecutingStreams == 0 else {
state.enqueuedCompletionEvents.append {
self.enqueueCompletion(on: queue, stream: stream)
@@ -1546,23 +1701,22 @@ public class DownloadRequest: Request {
}
/// Protected mutable state specific to `DownloadRequest`.
@Protected
private var mutableDownloadState = DownloadRequestMutableState()
private let mutableDownloadState = Protected(DownloadRequestMutableState())
/// If the download is resumable and is eventually cancelled or fails, this value may be used to resume the download
/// using the `download(resumingWith data:)` API.
///
/// - Note: For more information about `resumeData`, see [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel).
public var resumeData: Data? {
#if !(os(Linux) || os(Windows))
return $mutableDownloadState.resumeData ?? error?.downloadResumeData
#if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation.
return mutableDownloadState.resumeData ?? error?.downloadResumeData
#else
return $mutableDownloadState.resumeData
return mutableDownloadState.resumeData
#endif
}
/// If the download is successful, the `URL` where the file was downloaded.
public var fileURL: URL? { $mutableDownloadState.fileURL }
public var fileURL: URL? { mutableDownloadState.fileURL }
// MARK: Initial State
@@ -1605,7 +1759,7 @@ public class DownloadRequest: Request {
override func reset() {
super.reset()
$mutableDownloadState.write {
mutableDownloadState.write {
$0.resumeData = nil
$0.fileURL = nil
}
@@ -1620,7 +1774,7 @@ public class DownloadRequest: Request {
eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result)
switch result {
case let .success(url): $mutableDownloadState.fileURL = url
case let .success(url): mutableDownloadState.fileURL = url
case let .failure(error): self.error = error
}
}
@@ -1698,7 +1852,7 @@ public class DownloadRequest: Request {
///
/// - Returns: The instance.
private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self {
$mutableState.write { mutableState in
mutableState.write { mutableState in
guard mutableState.state.canTransitionTo(.cancelled) else { return }
mutableState.state = .cancelled
@@ -1714,7 +1868,7 @@ public class DownloadRequest: Request {
// Resume to ensure metrics are gathered.
task.resume()
task.cancel { resumeData in
self.$mutableDownloadState.resumeData = resumeData
self.mutableDownloadState.resumeData = resumeData
self.underlyingQueue.async { self.didCancelTask(task) }
completionHandler(resumeData)
}
@@ -1754,7 +1908,7 @@ public class DownloadRequest: Request {
withResult: result)
}
$validators.write { $0.append(validator) }
validators.write { $0.append(validator) }
return self
}

View File

@@ -131,7 +131,7 @@ struct RequestTaskMap {
switch (events.completed, events.metricsGathered) {
case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.")
#if os(Linux) // Linux doesn't gather metrics, so unconditionally remove the reference and return true.
#if os(Linux) || os(Android) // Linux doesn't gather metrics, so unconditionally remove the reference and return true.
default: self[task] = nil; return true
#else
case (false, false):

View File

@@ -1153,7 +1153,7 @@ extension DataStreamRequest {
}
}
$streamMutableState.write { $0.streams.append(parser) }
streamMutableState.write { $0.streams.append(parser) }
appendStreamCompletion(on: queue, stream: stream)
return self
@@ -1195,7 +1195,7 @@ extension DataStreamRequest {
}
}
$streamMutableState.write { $0.streams.append(parser) }
streamMutableState.write { $0.streams.append(parser) }
appendStreamCompletion(on: queue, stream: stream)
return self
@@ -1230,14 +1230,14 @@ extension DataStreamRequest {
}
}
$streamMutableState.write { $0.streams.append(parser) }
streamMutableState.write { $0.streams.append(parser) }
appendStreamCompletion(on: queue, stream: stream)
return self
}
private func updateAndCompleteIfPossible() {
$streamMutableState.write { state in
streamMutableState.write { state in
state.numberOfExecutingStreams -= 1
guard state.numberOfExecutingStreams == 0, !state.enqueuedCompletionEvents.isEmpty else { return }

View File

@@ -1,5 +1,5 @@
//
// ServerTrustPolicy.swift
// ServerTrustEvaluation.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
//
@@ -48,7 +48,7 @@ open class ServerTrustManager {
self.evaluators = evaluators
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// Returns the `ServerTrustEvaluating` value for the given host, if one is set.
///
/// By default, this method will return the policy that perfectly matches the given host. Subclasses could override
@@ -75,8 +75,8 @@ open class ServerTrustManager {
/// A protocol describing the API used to evaluate server trusts.
public protocol ServerTrustEvaluating {
#if os(Linux) || os(Windows)
// Implement this once Linux/Windows has API for evaluating server trusts.
#if !canImport(Security)
// Implement this once other platforms have API for evaluating server trusts.
#else
/// Evaluates the given `SecTrust` value for the given `host`.
///
@@ -91,7 +91,7 @@ public protocol ServerTrustEvaluating {
// MARK: - Server Trust Evaluators
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// An evaluator which uses the default server trust evaluation while allowing you to control whether to validate the
/// host provided by the challenge. Applications are encouraged to always validate the host in production environments
/// to guarantee the validity of the server's certificate chain.
@@ -181,6 +181,15 @@ public final class RevocationTrustEvaluator: ServerTrustEvaluating {
try trust.af.performValidation(forHost: host)
}
#if swift(>=5.9)
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) {
try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options))
} else {
try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in
AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options))
}
}
#else
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options))
} else {
@@ -188,6 +197,7 @@ public final class RevocationTrustEvaluator: ServerTrustEvaluating {
AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options))
}
}
#endif
}
}
@@ -355,12 +365,10 @@ public final class PublicKeysTrustEvaluator: ServerTrustEvaluating {
let pinnedKeysInServerKeys: Bool = {
for serverPublicKey in trust.af.publicKeys {
for pinnedPublicKey in keys {
if serverPublicKey == pinnedPublicKey {
if keys.contains(serverPublicKey) {
return true
}
}
}
return false
}()
@@ -449,7 +457,7 @@ public final class DisabledTrustEvaluator: ServerTrustEvaluating {
// MARK: - Extensions
extension Array where Element == ServerTrustEvaluating {
#if os(Linux) || os(Windows)
#if os(Linux) || os(Windows) || os(Android)
// Add this same convenience method for Linux/Windows.
#else
/// Evaluates the given `SecTrust` value for the given `host`.
@@ -598,7 +606,15 @@ extension AlamofireExtension where ExtendedType == SecTrust {
/// The `SecCertificate`s contained in `self`.
public var certificates: [SecCertificate] {
#if swift(>=5.5.1) // Xcode 13.1 / 2021 SDKs.
#if swift(>=5.9)
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, visionOS 1, *) {
return (SecTrustCopyCertificateChain(type) as? [SecCertificate]) ?? []
} else {
return (0..<SecTrustGetCertificateCount(type)).compactMap { index in
SecTrustGetCertificateAtIndex(type, index)
}
}
#elseif swift(>=5.5.1) // Xcode 13.1 / 2021 SDKs.
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
return (SecTrustCopyCertificateChain(type) as? [SecCertificate]) ?? []
} else {
@@ -623,6 +639,15 @@ extension AlamofireExtension where ExtendedType == SecTrust {
/// - Parameter host: The hostname, used only in the error output if validation fails.
/// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason.
public func performDefaultValidation(forHost host: String) throws {
#if swift(>=5.9)
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) {
try evaluate(afterApplying: SecPolicy.af.default)
} else {
try validate(policy: SecPolicy.af.default) { status, result in
AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result)))
}
}
#else
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
try evaluate(afterApplying: SecPolicy.af.default)
} else {
@@ -630,6 +655,7 @@ extension AlamofireExtension where ExtendedType == SecTrust {
AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result)))
}
}
#endif
}
/// Validates `self` after applying `SecPolicy.af.hostname(host)`, which performs the default validation as well as
@@ -638,6 +664,15 @@ extension AlamofireExtension where ExtendedType == SecTrust {
/// - Parameter host: The hostname to use in the validation.
/// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason.
public func performValidation(forHost host: String) throws {
#if swift(>=5.9)
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) {
try evaluate(afterApplying: SecPolicy.af.hostname(host))
} else {
try validate(policy: SecPolicy.af.hostname(host)) { status, result in
AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result)))
}
}
#else
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
try evaluate(afterApplying: SecPolicy.af.hostname(host))
} else {
@@ -645,6 +680,7 @@ extension AlamofireExtension where ExtendedType == SecTrust {
AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result)))
}
}
#endif
}
}
@@ -704,11 +740,19 @@ extension AlamofireExtension where ExtendedType == SecCertificate {
guard let createdTrust = trust, trustCreationStatus == errSecSuccess else { return nil }
#if swift(>=5.9)
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) {
return SecTrustCopyKey(createdTrust)
} else {
return SecTrustCopyPublicKey(createdTrust)
}
#else
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) {
return SecTrustCopyKey(createdTrust)
} else {
return SecTrustCopyPublicKey(createdTrust)
}
#endif
}
}

View File

@@ -94,7 +94,7 @@ extension SessionDelegate: URLSessionTaskDelegate {
case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM,
NSURLAuthenticationMethodNegotiate:
evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task)
#if !(os(Linux) || os(Windows))
#if canImport(Security)
case NSURLAuthenticationMethodServerTrust:
evaluation = attemptServerTrustAuthentication(with: challenge)
case NSURLAuthenticationMethodClientCertificate:
@@ -111,7 +111,7 @@ extension SessionDelegate: URLSessionTaskDelegate {
completionHandler(evaluation.disposition, evaluation.credential)
}
#if !(os(Linux) || os(Windows))
#if canImport(Security)
/// Evaluates the server trust `URLAuthenticationChallenge` received.
///
/// - Parameter challenge: The `URLAuthenticationChallenge`.
@@ -230,6 +230,25 @@ extension SessionDelegate: URLSessionTaskDelegate {
// MARK: URLSessionDataDelegate
extension SessionDelegate: URLSessionDataDelegate {
open func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: response)
guard let response = response as? HTTPURLResponse else { completionHandler(.allow); return }
if let request = request(for: dataTask, as: DataRequest.self) {
request.didReceiveResponse(response, completionHandler: completionHandler)
} else if let request = request(for: dataTask, as: DataStreamRequest.self) {
request.didReceiveResponse(response, completionHandler: completionHandler)
} else {
assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive response")
completionHandler(.allow)
return
}
}
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data)
@@ -238,7 +257,7 @@ extension SessionDelegate: URLSessionDataDelegate {
} else if let request = request(for: dataTask, as: DataStreamRequest.self) {
request.didReceive(data: data)
} else {
assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive")
assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive data")
return
}
}

View File

@@ -695,6 +695,74 @@ extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol
try encode(nilValue, forKey: key)
}
func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: String?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Int?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Int8?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Int16?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Int32?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: Int64?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: UInt?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}
func encodeIfPresent<Value>(_ value: Value?, forKey key: Key) throws where Value: Encodable {
try _encodeIfPresent(value, forKey: key)
}
func _encodeIfPresent<Value>(_ value: Value?, forKey key: Key) throws where Value: Encodable {
if let value = value {
try encode(value, forKey: key)
} else {
try encodeNil(forKey: key)
}
}
func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {
var container = nestedSingleValueEncoder(for: key)
try container.encode(value)

15
Pods/Manifest.lock generated
View File

@@ -1,5 +1,6 @@
PODS:
- Alamofire (5.7.1)
- Alamofire (5.8.1)
- DeviceKit (4.9.0)
- HandyJSON (5.0.2)
- Kingfisher (7.10.2)
- MBProgressHUD (1.2.0)
@@ -7,6 +8,8 @@ PODS:
- Moya/Core (= 15.0.0)
- Moya/Core (15.0.0):
- Alamofire (~> 5.0)
- "NSObject+Rx (5.2.2)":
- RxSwift (~> 6.2)
- Reusable (4.1.2):
- Reusable/Storyboard (= 4.1.2)
- Reusable/View (= 4.1.2)
@@ -21,10 +24,12 @@ PODS:
- SnapKit (5.6.0)
DEPENDENCIES:
- DeviceKit (~> 4.0)
- HandyJSON
- Kingfisher
- MBProgressHUD
- Moya
- "NSObject+Rx"
- Reusable
- RxCocoa
- RxSwift
@@ -33,10 +38,12 @@ DEPENDENCIES:
SPEC REPOS:
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
- Alamofire
- DeviceKit
- HandyJSON
- Kingfisher
- MBProgressHUD
- Moya
- "NSObject+Rx"
- Reusable
- RxCocoa
- RxRelay
@@ -44,17 +51,19 @@ SPEC REPOS:
- SnapKit
SPEC CHECKSUMS:
Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88
Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7
DeviceKit: 847709bf70b78fd9ab765bd571fb9f5f815c3fc1
HandyJSON: 9e4e236f5d2dbefad5155a77417bbea438201c03
Kingfisher: 99edc495d3b7607e6425f0d6f6847b2abd6d716d
MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
"NSObject+Rx": 61cf1f7306a73dcef8b36649198af0813ec18dfd
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b
RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd
RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8
SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25
PODFILE CHECKSUM: e291658151a1fcada5c9dd297587942ae9475225
PODFILE CHECKSUM: c70d2998947e71f4b799d573fbe428797987a5b9
COCOAPODS: 1.12.1

File diff suppressed because it is too large Load Diff

View File

@@ -11,83 +11,97 @@
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>HandyJSON.xcscheme</key>
<key>DeviceKit.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>Kingfisher-Kingfisher.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>Kingfisher.xcscheme</key>
<key>HandyJSON.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>MBProgressHUD.xcscheme</key>
<key>Kingfisher-Kingfisher.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>Moya.xcscheme</key>
<key>Kingfisher.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>MBProgressHUD.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
</dict>
<key>Pods-yinmeng-ios.xcscheme</key>
<key>Moya.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
</dict>
<key>Reusable.xcscheme</key>
<key>NSObject+Rx.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
</dict>
<key>RxCocoa.xcscheme</key>
<key>Pods-yinmeng-ios.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
</dict>
<key>RxRelay.xcscheme</key>
<key>Reusable.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>9</integer>
</dict>
<key>RxSwift.xcscheme</key>
<key>RxCocoa.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
</dict>
<key>SnapKit.xcscheme</key>
<key>RxRelay.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
</dict>
<key>RxSwift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>12</integer>
</dict>
<key>SnapKit.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>13</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict/>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>5.7.1</string>
<string>5.8.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -24,6 +24,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## DeviceKit
Copyright (c) 2015 Dennis Weissmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## HandyJSON
Copyright 1999-2016 Alibaba Group Holding Ltd.
@@ -149,6 +171,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## NSObject+Rx
The MIT License (MIT)
Copyright (c) 2015 Ash Furrow
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## Reusable
The MIT License (MIT)

View File

@@ -41,6 +41,34 @@ THE SOFTWARE.
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2015 Dennis Weissmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>DeviceKit</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string> Copyright 1999-2016 Alibaba Group Holding Ltd.
@@ -194,6 +222,38 @@ SOFTWARE.
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2015 Ash Furrow
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>NSObject+Rx</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2016 AliSoftware
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@@ -1,9 +1,11 @@
${PODS_ROOT}/Target Support Files/Pods-yinmeng-ios/Pods-yinmeng-ios-frameworks.sh
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework
${BUILT_PRODUCTS_DIR}/HandyJSON/HandyJSON.framework
${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework
${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework
${BUILT_PRODUCTS_DIR}/Moya/Moya.framework
${BUILT_PRODUCTS_DIR}/NSObject+Rx/NSObject_Rx.framework
${BUILT_PRODUCTS_DIR}/Reusable/Reusable.framework
${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework
${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework

View File

@@ -1,8 +1,10 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DeviceKit.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HandyJSON.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NSObject_Rx.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reusable.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework

View File

@@ -1,9 +1,11 @@
${PODS_ROOT}/Target Support Files/Pods-yinmeng-ios/Pods-yinmeng-ios-frameworks.sh
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework
${BUILT_PRODUCTS_DIR}/HandyJSON/HandyJSON.framework
${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework
${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework
${BUILT_PRODUCTS_DIR}/Moya/Moya.framework
${BUILT_PRODUCTS_DIR}/NSObject+Rx/NSObject_Rx.framework
${BUILT_PRODUCTS_DIR}/Reusable/Reusable.framework
${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework
${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework

View File

@@ -1,8 +1,10 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DeviceKit.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HandyJSON.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NSObject_Rx.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reusable.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework

View File

@@ -177,10 +177,12 @@ code_sign_if_enabled() {
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
install_framework "${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework"
install_framework "${BUILT_PRODUCTS_DIR}/HandyJSON/HandyJSON.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework"
install_framework "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework"
install_framework "${BUILT_PRODUCTS_DIR}/NSObject+Rx/NSObject_Rx.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Reusable/Reusable.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework"
@@ -189,10 +191,12 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
install_framework "${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework"
install_framework "${BUILT_PRODUCTS_DIR}/HandyJSON/HandyJSON.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework"
install_framework "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework"
install_framework "${BUILT_PRODUCTS_DIR}/NSObject+Rx/NSObject_Rx.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Reusable/Reusable.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework"

View File

@@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/NSObject+Rx" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON/HandyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable/Reusable.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit/DeviceKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON/HandyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/NSObject+Rx/NSObject_Rx.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable/Reusable.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "Alamofire" -framework "CFNetwork" -framework "CoreGraphics" -framework "Foundation" -framework "HandyJSON" -framework "Kingfisher" -framework "MBProgressHUD" -framework "Moya" -framework "QuartzCore" -framework "Reusable" -framework "RxCocoa" -framework "RxRelay" -framework "RxSwift" -framework "SnapKit" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "Alamofire" -framework "CFNetwork" -framework "CoreGraphics" -framework "DeviceKit" -framework "Foundation" -framework "HandyJSON" -framework "Kingfisher" -framework "MBProgressHUD" -framework "Moya" -framework "NSObject_Rx" -framework "QuartzCore" -framework "Reusable" -framework "RxCocoa" -framework "RxRelay" -framework "RxSwift" -framework "SnapKit" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/NSObject+Rx" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON/HandyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable/Reusable.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit/DeviceKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HandyJSON/HandyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/NSObject+Rx/NSObject_Rx.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reusable/Reusable.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "Alamofire" -framework "CFNetwork" -framework "CoreGraphics" -framework "Foundation" -framework "HandyJSON" -framework "Kingfisher" -framework "MBProgressHUD" -framework "Moya" -framework "QuartzCore" -framework "Reusable" -framework "RxCocoa" -framework "RxRelay" -framework "RxSwift" -framework "SnapKit" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "Alamofire" -framework "CFNetwork" -framework "CoreGraphics" -framework "DeviceKit" -framework "Foundation" -framework "HandyJSON" -framework "Kingfisher" -framework "MBProgressHUD" -framework "Moya" -framework "NSObject_Rx" -framework "QuartzCore" -framework "Reusable" -framework "RxCocoa" -framework "RxRelay" -framework "RxSwift" -framework "SnapKit" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@@ -8,6 +8,9 @@
/* Begin PBXBuildFile section */
25C63BC8F805551E8754E409 /* Pods_yinmeng_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2F175918AD0811681497739 /* Pods_yinmeng_ios.framework */; };
E81A7BAE2B885B20009E736E /* Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = E81A7BAA2B885B20009E736E /* Base64.m */; };
E81A7BAF2B885B20009E736E /* MAIDESEncryptTool.m in Sources */ = {isa = PBXBuildFile; fileRef = E81A7BAB2B885B20009E736E /* MAIDESEncryptTool.m */; };
E81A7BB22B886299009E736E /* HUDTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81A7BB12B886299009E736E /* HUDTool.swift */; };
E86A43AA2B85DFA90084C04D /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43A92B85DFA90084C04D /* BaseViewController.swift */; };
E86A43AE2B85DFEA0084C04D /* Color+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43AD2B85DFEA0084C04D /* Color+.swift */; };
E86A43B02B85E11B0084C04D /* BaseNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43AF2B85E11B0084C04D /* BaseNavigationViewController.swift */; };
@@ -21,26 +24,32 @@
E86A43C82B8743EA0084C04D /* AuthFillDataVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43C72B8743EA0084C04D /* AuthFillDataVC.swift */; };
E86A43CB2B874C6F0084C04D /* AuthPrivacyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43CA2B874C6F0084C04D /* AuthPrivacyView.swift */; };
E86A43CD2B874C8E0084C04D /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43CC2B874C8E0084C04D /* BaseView.swift */; };
E86A43D32B8773C90084C04D /* APPUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43D22B8773C90084C04D /* APPUtils.swift */; };
E86A43D52B8774B70084C04D /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43D42B8774B70084C04D /* AuthViewModel.swift */; };
E86A43DA2B877A840084C04D /* AppConfigObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43D92B877A840084C04D /* AppConfigObject.swift */; };
E86A43E82B884C5E0084C04D /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86A43E72B884C5E0084C04D /* String+.swift */; };
E884E85F2B6900C500ADE6EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E884E85E2B6900C500ADE6EE /* AppDelegate.swift */; };
E884E8662B6900C500ADE6EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8642B6900C500ADE6EE /* Main.storyboard */; };
E884E8682B6900C600ADE6EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E884E8672B6900C600ADE6EE /* Assets.xcassets */; };
E884E86B2B6900C600ADE6EE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8692B6900C600ADE6EE /* LaunchScreen.storyboard */; };
E897B8D82B6C8D1600F884C2 /* YMNetworkAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8D72B6C8D1600F884C2 /* YMNetworkAPI.swift */; };
E897B8DA2B6C903600F884C2 /* NetworkRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8D92B6C903600F884C2 /* NetworkRequest.swift */; };
E897B8DC2B6C916A00F884C2 /* HeadstreamRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8DB2B6C916A00F884C2 /* HeadstreamRequest.swift */; };
E897B8DE2B6C91F500F884C2 /* YMRequestX.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8DD2B6C91F500F884C2 /* YMRequestX.swift */; };
E897B8E02B6C929500F884C2 /* SharedDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8DF2B6C929500F884C2 /* SharedDriver.swift */; };
E897B8E22B6C933E00F884C2 /* LevelStatusBarWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8E12B6C933E00F884C2 /* LevelStatusBarWindowController.swift */; };
E897B8E42B6C93C700F884C2 /* YMRequestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8E32B6C93C700F884C2 /* YMRequestConfig.swift */; };
E897B8E62B6C941400F884C2 /* YMPluginSubType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8E52B6C941400F884C2 /* YMPluginSubType.swift */; };
E897B8E82B6C944F00F884C2 /* YMLastNeverResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8E72B6C944F00F884C2 /* YMLastNeverResult.swift */; };
E897B8EB2B6CA1DA00F884C2 /* YMRequestLoadingPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E897B8EA2B6CA1DA00F884C2 /* YMRequestLoadingPlugin.swift */; };
E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */; };
E8D15A9F2B89AED600369467 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9E2B89AED500369467 /* AuthManager.swift */; };
E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */; };
E8D15AA32B89B03D00369467 /* Deserialized.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AA22B89B03D00369467 /* Deserialized.swift */; };
E8D15AA62B89B0C600369467 /* List+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AA52B89B0C600369467 /* List+.swift */; };
E8D15AA82B89B74700369467 /* YMRequestX.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AA72B89B74700369467 /* YMRequestX.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
A9FB906EB4D17C552C15A2B3 /* Pods-yinmeng-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-yinmeng-ios.debug.xcconfig"; path = "Target Support Files/Pods-yinmeng-ios/Pods-yinmeng-ios.debug.xcconfig"; sourceTree = "<group>"; };
B2F175918AD0811681497739 /* Pods_yinmeng_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_yinmeng_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CB0AC98C54C1D41FA4CA102B /* Pods-yinmeng-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-yinmeng-ios.release.xcconfig"; path = "Target Support Files/Pods-yinmeng-ios/Pods-yinmeng-ios.release.xcconfig"; sourceTree = "<group>"; };
E81A7BA92B885B1F009E736E /* yinmeng-ios-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "yinmeng-ios-Bridging-Header.h"; sourceTree = "<group>"; };
E81A7BAA2B885B20009E736E /* Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Base64.m; sourceTree = "<group>"; };
E81A7BAB2B885B20009E736E /* MAIDESEncryptTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MAIDESEncryptTool.m; sourceTree = "<group>"; };
E81A7BAC2B885B20009E736E /* Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base64.h; sourceTree = "<group>"; };
E81A7BAD2B885B20009E736E /* MAIDESEncryptTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MAIDESEncryptTool.h; sourceTree = "<group>"; };
E81A7BB12B886299009E736E /* HUDTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDTool.swift; sourceTree = "<group>"; };
E86A43A92B85DFA90084C04D /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
E86A43AD2B85DFEA0084C04D /* Color+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+.swift"; sourceTree = "<group>"; };
E86A43AF2B85E11B0084C04D /* BaseNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNavigationViewController.swift; sourceTree = "<group>"; };
@@ -54,22 +63,22 @@
E86A43C72B8743EA0084C04D /* AuthFillDataVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFillDataVC.swift; sourceTree = "<group>"; };
E86A43CA2B874C6F0084C04D /* AuthPrivacyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthPrivacyView.swift; sourceTree = "<group>"; };
E86A43CC2B874C8E0084C04D /* BaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseView.swift; sourceTree = "<group>"; };
E86A43D22B8773C90084C04D /* APPUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APPUtils.swift; sourceTree = "<group>"; };
E86A43D42B8774B70084C04D /* AuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewModel.swift; sourceTree = "<group>"; };
E86A43D92B877A840084C04D /* AppConfigObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigObject.swift; sourceTree = "<group>"; };
E86A43E72B884C5E0084C04D /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = "<group>"; };
E884E85B2B6900C500ADE6EE /* yinmeng-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "yinmeng-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; };
E884E85E2B6900C500ADE6EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
E884E8652B6900C500ADE6EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
E884E8672B6900C600ADE6EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E884E86A2B6900C600ADE6EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
E884E86C2B6900C600ADE6EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E897B8D72B6C8D1600F884C2 /* YMNetworkAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMNetworkAPI.swift; sourceTree = "<group>"; };
E897B8D92B6C903600F884C2 /* NetworkRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequest.swift; sourceTree = "<group>"; };
E897B8DB2B6C916A00F884C2 /* HeadstreamRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadstreamRequest.swift; sourceTree = "<group>"; };
E897B8DD2B6C91F500F884C2 /* YMRequestX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMRequestX.swift; sourceTree = "<group>"; };
E897B8DF2B6C929500F884C2 /* SharedDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDriver.swift; sourceTree = "<group>"; };
E897B8E12B6C933E00F884C2 /* LevelStatusBarWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LevelStatusBarWindowController.swift; sourceTree = "<group>"; };
E897B8E32B6C93C700F884C2 /* YMRequestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMRequestConfig.swift; sourceTree = "<group>"; };
E897B8E52B6C941400F884C2 /* YMPluginSubType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMPluginSubType.swift; sourceTree = "<group>"; };
E897B8E72B6C944F00F884C2 /* YMLastNeverResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMLastNeverResult.swift; sourceTree = "<group>"; };
E897B8EA2B6CA1DA00F884C2 /* YMRequestLoadingPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMRequestLoadingPlugin.swift; sourceTree = "<group>"; };
E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMNetworkHelper.swift; sourceTree = "<group>"; };
E8D15A9E2B89AED500369467 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; };
E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTokenObject.swift; sourceTree = "<group>"; };
E8D15AA22B89B03D00369467 /* Deserialized.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deserialized.swift; sourceTree = "<group>"; };
E8D15AA52B89B0C600369467 /* List+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "List+.swift"; sourceTree = "<group>"; };
E8D15AA72B89B74700369467 /* YMRequestX.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YMRequestX.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -101,9 +110,20 @@
name = Frameworks;
sourceTree = "<group>";
};
E81A7BB02B88628B009E736E /* Hud */ = {
isa = PBXGroup;
children = (
E81A7BB12B886299009E736E /* HUDTool.swift */,
);
path = Hud;
sourceTree = "<group>";
};
E86A43A72B85DF7A0084C04D /* Base */ = {
isa = PBXGroup;
children = (
E8D15A9B2B899DD400369467 /* Request */,
E81A7BB02B88628B009E736E /* Hud */,
E8EE60802B8858A500D02F6E /* Security */,
E86A43BD2B8620BA0084C04D /* Utils */,
E86A43A82B85DF850084C04D /* ViewController */,
);
@@ -124,6 +144,8 @@
E86A43AB2B85DFC20084C04D /* Extension */ = {
isa = PBXGroup;
children = (
E8D15AA42B89B0BA00369467 /* List */,
E86A43E62B884C520084C04D /* String */,
E86A43C42B862CB70084C04D /* UIImage */,
E86A43AC2B85DFD70084C04D /* Color */,
);
@@ -149,6 +171,7 @@
E86A43B22B85E2170084C04D /* Auth */ = {
isa = PBXGroup;
children = (
E86A43D82B877A6C0084C04D /* Model */,
E86A43C92B874C5C0084C04D /* View */,
E86A43B42B85E2340084C04D /* VM */,
E86A43B32B85E22A0084C04D /* VC */,
@@ -171,6 +194,9 @@
isa = PBXGroup;
children = (
E86A43B52B85E2520084C04D /* AuthAPI.swift */,
E86A43D42B8774B70084C04D /* AuthViewModel.swift */,
E8D15A9E2B89AED500369467 /* AuthManager.swift */,
E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */,
);
path = VM;
sourceTree = "<group>";
@@ -179,6 +205,7 @@
isa = PBXGroup;
children = (
E86A43BE2B8620C40084C04D /* Utils.swift */,
E86A43D22B8773C90084C04D /* APPUtils.swift */,
);
path = Utils;
sourceTree = "<group>";
@@ -199,6 +226,22 @@
path = View;
sourceTree = "<group>";
};
E86A43D82B877A6C0084C04D /* Model */ = {
isa = PBXGroup;
children = (
E86A43D92B877A840084C04D /* AppConfigObject.swift */,
);
path = Model;
sourceTree = "<group>";
};
E86A43E62B884C520084C04D /* String */ = {
isa = PBXGroup;
children = (
E86A43E72B884C5E0084C04D /* String+.swift */,
);
path = String;
sourceTree = "<group>";
};
E884E8522B6900C500ADE6EE = {
isa = PBXGroup;
children = (
@@ -223,7 +266,6 @@
E86A43B12B85E1FC0084C04D /* Modules */,
E86A43AB2B85DFC20084C04D /* Extension */,
E86A43A72B85DF7A0084C04D /* Base */,
E897B8D62B6C8CF600F884C2 /* HttpRequest */,
E884E85E2B6900C500ADE6EE /* AppDelegate.swift */,
E884E8642B6900C500ADE6EE /* Main.storyboard */,
E884E8672B6900C600ADE6EE /* Assets.xcassets */,
@@ -233,29 +275,34 @@
path = "yinmeng-ios";
sourceTree = "<group>";
};
E897B8D62B6C8CF600F884C2 /* HttpRequest */ = {
E8D15A9B2B899DD400369467 /* Request */ = {
isa = PBXGroup;
children = (
E897B8E92B6CA1B500F884C2 /* Plugins */,
E897B8D72B6C8D1600F884C2 /* YMNetworkAPI.swift */,
E897B8D92B6C903600F884C2 /* NetworkRequest.swift */,
E897B8DB2B6C916A00F884C2 /* HeadstreamRequest.swift */,
E897B8DD2B6C91F500F884C2 /* YMRequestX.swift */,
E897B8DF2B6C929500F884C2 /* SharedDriver.swift */,
E897B8E12B6C933E00F884C2 /* LevelStatusBarWindowController.swift */,
E897B8E32B6C93C700F884C2 /* YMRequestConfig.swift */,
E897B8E52B6C941400F884C2 /* YMPluginSubType.swift */,
E897B8E72B6C944F00F884C2 /* YMLastNeverResult.swift */,
E8D15AA72B89B74700369467 /* YMRequestX.swift */,
E8D15AA22B89B03D00369467 /* Deserialized.swift */,
E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */,
);
path = HttpRequest;
path = Request;
sourceTree = "<group>";
};
E897B8E92B6CA1B500F884C2 /* Plugins */ = {
E8D15AA42B89B0BA00369467 /* List */ = {
isa = PBXGroup;
children = (
E897B8EA2B6CA1DA00F884C2 /* YMRequestLoadingPlugin.swift */,
E8D15AA52B89B0C600369467 /* List+.swift */,
);
path = Plugins;
path = List;
sourceTree = "<group>";
};
E8EE60802B8858A500D02F6E /* Security */ = {
isa = PBXGroup;
children = (
E81A7BAC2B885B20009E736E /* Base64.h */,
E81A7BAA2B885B20009E736E /* Base64.m */,
E81A7BAD2B885B20009E736E /* MAIDESEncryptTool.h */,
E81A7BAB2B885B20009E736E /* MAIDESEncryptTool.m */,
E81A7BA92B885B1F009E736E /* yinmeng-ios-Bridging-Header.h */,
);
path = Security;
sourceTree = "<group>";
};
/* End PBXGroup section */
@@ -292,6 +339,7 @@
TargetAttributes = {
E884E85A2B6900C500ADE6EE = {
CreatedOnToolsVersion = 15.0;
LastSwiftMigration = 1500;
};
};
};
@@ -373,30 +421,33 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E897B8E02B6C929500F884C2 /* SharedDriver.swift in Sources */,
E86A43AE2B85DFEA0084C04D /* Color+.swift in Sources */,
E897B8E22B6C933E00F884C2 /* LevelStatusBarWindowController.swift in Sources */,
E8D15A9F2B89AED600369467 /* AuthManager.swift in Sources */,
E86A43B62B85E2520084C04D /* AuthAPI.swift in Sources */,
E86A43B02B85E11B0084C04D /* BaseNavigationViewController.swift in Sources */,
E897B8DE2B6C91F500F884C2 /* YMRequestX.swift in Sources */,
E86A43E82B884C5E0084C04D /* String+.swift in Sources */,
E8D15AA32B89B03D00369467 /* Deserialized.swift in Sources */,
E86A43DA2B877A840084C04D /* AppConfigObject.swift in Sources */,
E8D15AA62B89B0C600369467 /* List+.swift in Sources */,
E81A7BAE2B885B20009E736E /* Base64.m in Sources */,
E81A7BB22B886299009E736E /* HUDTool.swift in Sources */,
E86A43BC2B85FA640084C04D /* AuthForgetVC.swift in Sources */,
E86A43C32B8628AF0084C04D /* BaeTabBarViewController.swift in Sources */,
E897B8DC2B6C916A00F884C2 /* HeadstreamRequest.swift in Sources */,
E86A43AA2B85DFA90084C04D /* BaseViewController.swift in Sources */,
E884E85F2B6900C500ADE6EE /* AppDelegate.swift in Sources */,
E897B8E62B6C941400F884C2 /* YMPluginSubType.swift in Sources */,
E86A43B82B85F0B80084C04D /* AuthLaunchVC.swift in Sources */,
E86A43C82B8743EA0084C04D /* AuthFillDataVC.swift in Sources */,
E897B8DA2B6C903600F884C2 /* NetworkRequest.swift in Sources */,
E86A43CD2B874C8E0084C04D /* BaseView.swift in Sources */,
E897B8D82B6C8D1600F884C2 /* YMNetworkAPI.swift in Sources */,
E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */,
E86A43C62B862CC70084C04D /* UIImage+.swift in Sources */,
E86A43D32B8773C90084C04D /* APPUtils.swift in Sources */,
E86A43CB2B874C6F0084C04D /* AuthPrivacyView.swift in Sources */,
E897B8E42B6C93C700F884C2 /* YMRequestConfig.swift in Sources */,
E86A43BF2B8620C40084C04D /* Utils.swift in Sources */,
E86A43BA2B85F1360084C04D /* AuthLoginVC.swift in Sources */,
E897B8E82B6C944F00F884C2 /* YMLastNeverResult.swift in Sources */,
E897B8EB2B6CA1DA00F884C2 /* YMRequestLoadingPlugin.swift in Sources */,
E81A7BAF2B885B20009E736E /* MAIDESEncryptTool.m in Sources */,
E8D15AA82B89B74700369467 /* YMRequestX.swift in Sources */,
E86A43D52B8774B70084C04D /* AuthViewModel.swift in Sources */,
E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -547,8 +598,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = KQKX28SU5S;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -556,17 +609,18 @@
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "yinmeng.yinmeng-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "yinmeng-ios/Base/Security/yinmeng-ios-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -578,8 +632,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = KQKX28SU5S;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -587,17 +643,17 @@
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "yinmeng.yinmeng-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "yinmeng-ios/Base/Security/yinmeng-ios-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};

View File

@@ -7,7 +7,7 @@
<key>yinmeng-ios.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>12</integer>
<integer>14</integer>
</dict>
</dict>
</dict>

View File

@@ -3,4 +3,38 @@
uuid = "265D86DA-810F-46B5-8843-7CF61C036487"
type = "0"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "904BC081-F189-4956-A57F-7ACAED4ADA8A"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "yinmeng-ios/HttpRequest/YMLastNeverResult.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "34"
endingLineNumber = "34"
landmarkName = "mapResult(success:failure:progress:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "8C81B94B-5918-47EC-A4D7-DA45EBFC4202"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "yinmeng-ios/HttpRequest/YMLastNeverResult.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "47"
endingLineNumber = "47"
landmarkName = "mapResult(success:failure:progress:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View File

@@ -6,7 +6,7 @@
//
import UIKit
import DeviceKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
@@ -17,7 +17,11 @@ var window: UIWindow?
self.window = UIWindow.init(frame: UIScreen.main.bounds)
self.window?.backgroundColor = UIColor.white
self.window?.rootViewController = BaseNavigationViewController(rootViewController:AuthLoginVC())
return true
}
}

View File

@@ -0,0 +1,145 @@
//
// HUDTool.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/23.
//
import Foundation
import MBProgressHUD
class HUDTool: NSObject {
static var currentHud : MBProgressHUD?
private static let kDelayTime: TimeInterval = 2
/** HUD*/
class func hidden(_ time:TimeInterval = 0){
guard let HUD = currentHud else {
return
}
HUD.hide(animated: false)
currentHud = nil
}
/**
message
- Parameter message:
- Parameter view: view
- Parameter afterDelay:
- Parameter enabled: no: yes:
*/
class func showSuccess(with message: String, in view: UIView? = YMRequestX.topViewController()?.view, delay afterDelay: TimeInterval = kDelayTime, enabled: Bool = true, icon:String = "") {
if message.isEmpty { return }
DispatchQueue.main.async {
hidden(0)
var view = view
if view == nil {
view = YMRequestX.topViewController()?.view ?? YMRequestX.keyWindow()
}
if let view = view {
let hud = normalProgressHUD(in: view)
currentHud = hud
hud.isUserInteractionEnabled = enabled
hud.mode = .customView
hud.bezelView.style = .solidColor
hud.margin = 8
hud.backgroundColor = .clear
hud.removeFromSuperViewOnHide = true
let messageView = HudMessageView()
messageView.titleLb.text = message
let maxWidth = 242.0
let size = message.boundingRect(with: CGSize(width: maxWidth, height: CGFLOAT_MAX), font: UIFont.systemFont(ofSize: 14, weight: .medium), lineSpacing: 6)
var width = size.width + 5
var height = size.height
if icon.count > 0 {
messageView.logoImgView.image = UIImage(named: icon)
messageView.logoImgView.isHidden = false
width += 22.0
} else {
messageView.logoImgView.isHidden = true
}
hud.bezelView.backgroundColor = ThemeColor(hexStr: "#000000", alpha: 0.7)
hud.bezelView.layer.cornerRadius = 8
hud.bezelView.layer.masksToBounds = true
hud.bezelView.addSubview(messageView)
messageView.snp.makeConstraints { make in
make.height.equalTo(height)
make.width.equalTo(width)
make.center.equalTo(hud.bezelView)
}
hud.minSize = CGSize(width: width + 40, height: height + 24)
hud.show(animated: true)
hud.hide(animated: false, afterDelay: kDelayTime)
}
}
}
private class func normalProgressHUD(in view: UIView) -> MBProgressHUD {
let hud = MBProgressHUD.showAdded(to: view, animated: true)
hud.mode = .indeterminate
hud.bezelView.style = .solidColor
hud.margin = 8
//
hud.bezelView.color = UIColor.black.withAlphaComponent(0.8)
return hud
}
}
class HudMessageView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(stackView)
stackView.addArrangedSubview(logoImgView)
stackView.addArrangedSubview(titleLb)
stackView.snp.makeConstraints { make in
make.edges.equalTo(self)
}
logoImgView.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 20, height: 20))
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.distribution = .fill
stackView.axis = .horizontal
stackView.alignment = .top
stackView.spacing = 2
return stackView
}()
lazy var logoImgView: UIImageView = {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
lazy var titleLb: UILabel = {
let label = UILabel()
label.textColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
}

View File

@@ -0,0 +1,37 @@
//
// Deserialized.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/22.
//
import Foundation
@_exported import HandyJSON
/// Deserialized json converts to Model or Array.
public struct Deserialized<H> where H: HandyJSON {
public static func toModel(with element: Any?) -> H? {
if let string = element as? String, let model = H.deserialize(from: string) {
return model
}
if let dictionary = element as? Dictionary<String, Any>, let model = H.deserialize(from: dictionary) {
return model
}
if let dictionary = element as? [String : Any], let model = H.deserialize(from: dictionary) {
return model
}
return nil
}
public static func toArray(with element: Any?) -> [H]? {
if let string = element as? String, let array = [H].deserialize(from: string) as? [H] {
return array
}
if let array = [H].deserialize(from: element as? [Any]) as? [H] {
return array
}
return nil
}
}

View File

@@ -0,0 +1,134 @@
//
// YMNetworkAPI.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/24.
//
import Foundation
import Alamofire
import HandyJSON
import DeviceKit
typealias SessionCallSucceed = (Any) -> Void
typealias SessionCallFail = (Int,String) -> Void
class YMNetworkHelper: NSObject {
var baseParameters: [String: Any] {
var parameters: [String: Any] = Dictionary()
parameters["os"] = "iOS"
parameters["osVersion"] = Device.current.systemVersion
parameters["model"] = "iPhone14,7"
parameters["ispType"] = "1"
parameters["channel"] = APPUtils.currentChannle
parameters["netType"] = "2"
parameters["app"] = "yinmeng"
parameters["appVersion"] = APPUtils.appVersion
parameters["deviceId"] = APPUtils.currentDeveiceId
return parameters
}
var sessionNetMana :Session!
public static let share = YMNetworkHelper.init()
var headersSet: HTTPHeaders = [
"Content-Type": "application/json",
"Authorization": ""
]
func requestSend(type:HTTPMethod,path:String,params:Dictionary<String, Any>, succeed:SessionCallSucceed?,fail:SessionCallFail?) -> Void {
getHttpRequestHeaders()
requestSend(type: type,host: "http://beta.api.ymlive.fun/", path: path, params: params, encoding: URLEncoding.queryString, header: headersSet, succeed: succeed, fail: fail)
}
func requestSend(type:HTTPMethod,
host:String,
path:String,
params:[String: Any],
encoding:ParameterEncoding,
header:HTTPHeaders,
succeed:SessionCallSucceed?,
fail:SessionCallFail?) -> Void {
let encrypteChonParma = baseParameters.merging(params) {$1}
sessionNetMana.request(host+path, method: type, parameters: encrypteChonParma,encoding: encoding,headers: header)
.validate(contentType: ["application/json"])
.responseJSON { [weak self] (response) in
self?.analyzeThe(response1: response, succeed2: succeed, fail3: fail,uuid4:"")
}
}
override init() {
super.init()
let configCo = AF.sessionConfiguration
configCo.httpShouldSetCookies = false
configCo.timeoutIntervalForRequest = 30
sessionNetMana = Session(configuration: configCo)
}
private func getHttpRequestHeaders() {
headersSet["pub_uid"] = "\(AuthManager.userUid)"
headersSet["pub_ticket"] = AuthManager.ticket
}
func analyzeThe(response1:AFDataResponse<Any>,
succeed2:SessionCallSucceed?,
fail3: SessionCallFail?,uuid4:String) -> Void {
let maiUrlSss = response1.request?.url?.absoluteString ?? "unkown"
switch response1.result {
case .success:
let maiResponNk :Dictionary = response1.value as? Dictionary<String, Any> ?? Dictionary.init()
let maiResultMo = maiResponNk
if maiResultMo.keys.contains("code") {
let maicodeNum :Int = maiResultMo["code"] as? Int ?? 0
if maicodeNum == 200 {
if maiResultMo.keys.contains("data") {
let maiDDD = maiResultMo["data"] as Any
succeed2?(maiDDD)
}else{
succeed2?(Dictionary<String, Any>.init())
}
}else{
if maicodeNum == 401 && maiUrlSss.contains("auth-center/sso/logout") == false {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "MAISessionTickValid"), object: nil)
sessionNetMana.cancelAllRequests()
}
var messageIn = response1.error.debugDescription
if maiResultMo.keys.contains("message") { messageIn = maiResultMo["message"] as? String ?? "" }
fail3?(maicodeNum,messageIn)
}
} else if maiResultMo.keys.contains("errno") {
let maiCodeNum :Int = maiResultMo["errno"] as? Int ?? 0
if maiCodeNum == 0 {
if maiResultMo.keys.contains("data") {
let dataSc = maiResultMo["data"] as Any
succeed2?(dataSc)
}else{
succeed2?(Dictionary<String, Any>.init())
}
}else{
var majmessageStr = response1.error.debugDescription
if maiResultMo.keys.contains("errmsg") { majmessageStr = maiResultMo["errmsg"] as? String ?? ""}
fail3?(maiCodeNum,majmessageStr)
}
} else {
fail3?(10000,"请求失败")
}
case let .failure(error):
var maiErrorMssg = response1.error?.errorDescription ?? ""
var maicodeNum = response1.error?.responseCode ?? 0
fail3?(maicodeNum,maiErrorMssg)
}
}
}
struct ResponseModel: HandyJSON {
var code:Int? = 0
var message:String? = ""
var data:Any?
}

View File

@@ -0,0 +1,99 @@
//
// YMRequestX.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
public struct YMRequestX {
/// Maps data received from the signal into a JSON object.
public static func mapJSON<T>(_ type: T.Type, named: String, forResource: String = "RxNetworks") -> T? {
guard let data = jsonData(named, forResource: forResource) else {
return nil
}
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
return json as? T
}
/// Read json data
public static func jsonData(_ named: String, forResource: String = "RxNetworks") -> Data? {
let bundle: Bundle?
if let bundlePath = Bundle.main.path(forResource: forResource, ofType: "bundle") {
bundle = Bundle.init(path: bundlePath)
} else {
bundle = Bundle.main
}
guard let path = ["json", "JSON", "Json"].compactMap({
bundle?.path(forResource: named, ofType: $0)
}).first else {
return nil
}
let contentURL = URL(fileURLWithPath: path)
return try? Data(contentsOf: contentURL)
}
public static func toJSON(form value: Any, prettyPrint: Bool = false) -> String? {
guard JSONSerialization.isValidJSONObject(value) else {
return nil
}
var jsonData: Data? = nil
if prettyPrint {
jsonData = try? JSONSerialization.data(withJSONObject: value, options: [.prettyPrinted])
} else {
jsonData = try? JSONSerialization.data(withJSONObject: value, options: [])
}
guard let data = jsonData else { return nil }
return String(data: data ,encoding: .utf8)
}
public static func toDictionary(form json: String) -> [String : Any]? {
guard let jsonData = json.data(using: .utf8),
let object = try? JSONSerialization.jsonObject(with: jsonData, options: []),
let result = object as? [String : Any] else {
return nil
}
return result
}
public static func keyWindow() -> UIWindow? {
if #available(iOS 13.0, *) {
return UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first(where: { $0 is UIWindowScene })
.flatMap({ $0 as? UIWindowScene })?.windows
.first(where: \.isKeyWindow)
} else {
return UIApplication.shared.keyWindow
}
}
public static func topViewController() -> UIViewController? {
let window = UIApplication.shared.delegate?.window
guard window != nil, let rootViewController = window?!.rootViewController else {
return nil
}
return self.getTopViewController(controller: rootViewController)
}
public static func getTopViewController(controller: UIViewController) -> UIViewController {
if let presentedViewController = controller.presentedViewController {
return self.getTopViewController(controller: presentedViewController)
} else if let navigationController = controller as? UINavigationController {
if let topViewController = navigationController.topViewController {
return self.getTopViewController(controller: topViewController)
}
return navigationController
} else if let tabbarController = controller as? UITabBarController {
if let selectedViewController = tabbarController.selectedViewController {
return self.getTopViewController(controller: selectedViewController)
}
return tabbarController
} else {
return controller
}
}
}

View File

@@ -0,0 +1,16 @@
//
// Base64.h
// BellFramework
//
// Created by 罗兴志 on 2017/5/4.
// Copyright © 2017年 罗兴志. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Base64 : NSObject
+(NSString *)encode:(NSData *)data;
+(NSData *)decode:(NSString *)dataString;
@end

View File

@@ -0,0 +1,133 @@
//
// Base64.m
// BellFramework
//
// Created by on 2017/5/4.
// Copyright © 2017 . All rights reserved.
//
#import "Base64.h"
@interface Base64()
+(int)char2Int:(char)c;
@end
@implementation Base64
static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+(NSString *)encode:(NSData *)data
{
if (data.length == 0)
return nil;
char *characters = malloc(data.length * 3 / 2);
if (characters == NULL)
return nil;
int end = data.length - 3;
int index = 0;
int charCount = 0;
int n = 0;
while (index <= end) {
int d = (((int)(((char *)[data bytes])[index]) & 0x0ff) << 16)
| (((int)(((char *)[data bytes])[index + 1]) & 0x0ff) << 8)
| ((int)(((char *)[data bytes])[index + 2]) & 0x0ff);
characters[charCount++] = encodingTable[(d >> 18) & 63];
characters[charCount++] = encodingTable[(d >> 12) & 63];
characters[charCount++] = encodingTable[(d >> 6) & 63];
characters[charCount++] = encodingTable[d & 63];
index += 3;
if(n++ >= 14)
{
n = 0;
characters[charCount++] = ' ';
}
}
if(index == data.length - 2)
{
int d = (((int)(((char *)[data bytes])[index]) & 0x0ff) << 16)
| (((int)(((char *)[data bytes])[index + 1]) & 255) << 8);
characters[charCount++] = encodingTable[(d >> 18) & 63];
characters[charCount++] = encodingTable[(d >> 12) & 63];
characters[charCount++] = encodingTable[(d >> 6) & 63];
characters[charCount++] = '=';
}
else if(index == data.length - 1)
{
int d = ((int)(((char *)[data bytes])[index]) & 0x0ff) << 16;
characters[charCount++] = encodingTable[(d >> 18) & 63];
characters[charCount++] = encodingTable[(d >> 12) & 63];
characters[charCount++] = '=';
characters[charCount++] = '=';
}
NSString * rtnStr = [[NSString alloc] initWithBytesNoCopy:characters length:charCount encoding:NSUTF8StringEncoding freeWhenDone:YES];
return rtnStr;
}
+(NSData *)decode:(NSString *)data
{
if(data == nil || data.length <= 0) {
return nil;
}
NSMutableData *rtnData = [[NSMutableData alloc]init];
int slen = data.length;
int index = 0;
while (true) {
while (index < slen && [data characterAtIndex:index] <= ' ') {
index++;
}
if (index >= slen || index + 3 >= slen) {
break;
}
int byte = ([self char2Int:[data characterAtIndex:index]] << 18) + ([self char2Int:[data characterAtIndex:index + 1]] << 12) + ([self char2Int:[data characterAtIndex:index + 2]] << 6) + [self char2Int:[data characterAtIndex:index + 3]];
Byte temp1 = (byte >> 16) & 255;
[rtnData appendBytes:&temp1 length:1];
if([data characterAtIndex:index + 2] == '=') {
break;
}
Byte temp2 = (byte >> 8) & 255;
[rtnData appendBytes:&temp2 length:1];
if([data characterAtIndex:index + 3] == '=') {
break;
}
Byte temp3 = byte & 255;
[rtnData appendBytes:&temp3 length:1];
index += 4;
}
return rtnData;
}
+(int)char2Int:(char)c
{
if (c >= 'A' && c <= 'Z') {
return c - 65;
} else if (c >= 'a' && c <= 'z') {
return c - 97 + 26;
} else if (c >= '0' && c <= '9') {
return c - 48 + 26 + 26;
} else {
switch(c) {
case '+':
return 62;
case '/':
return 63;
case '=':
return 0;
default:
return -1;
}
}
}
@end

View File

@@ -0,0 +1,16 @@
//
// MAIDESEncryptTool.h
// BellFramework
//
// Created by 罗兴志 on 2017/5/4.
// Copyright © 2017年 罗兴志. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MAIDESEncryptTool : NSObject
//加密方法
+(NSString *)encryptUseDES:(NSString *)plainText key:(NSString *)key;
//解密方法
+(NSString *)decryptUseDES:(NSString *)cipherText key:(NSString *)key;
@end

View File

@@ -0,0 +1,63 @@
//
// MAIDESEncryptTool.m
// BellFramework
//
// Created by on 2017/5/4.
// Copyright © 2017 . All rights reserved.
//
#import "MAIDESEncryptTool.h"
#import <CommonCrypto/CommonCrypto.h>
#import "Base64.h"
@implementation MAIDESEncryptTool : NSObject
const Byte iv[] = {1,2,3,4,5,6,7,8};
#pragma mark-
+(NSString *) encryptUseDES:(NSString *)plainText key:(NSString *)key
{
NSString *ciphertext = nil;
NSData *textData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [textData length];
unsigned char buffer[20000];
memset(buffer, 0, sizeof(char));
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES,
kCCOptionPKCS7Padding|kCCOptionECBMode,
[key UTF8String], kCCKeySizeDES,
iv,
[textData bytes], dataLength,
buffer, 20000,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
ciphertext = [Base64 encode:data];
}
return ciphertext;
}
#pragma mark-
+(NSString *)decryptUseDES:(NSString *)cipherText key:(NSString *)key
{
NSString *plaintext = nil;
NSData *cipherdata = [Base64 decode:cipherText];
unsigned char buffer[20000];
memset(buffer, 0, sizeof(char));
size_t numBytesDecrypted = 0;
// kCCOptionPKCS7Padding|kCCOptionECBMode
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmDES,
kCCOptionPKCS7Padding|kCCOptionECBMode,
[key UTF8String], kCCKeySizeDES,
iv,
[cipherdata bytes], [cipherdata length],
buffer, 20000,
&numBytesDecrypted);
if(cryptStatus == kCCSuccess) {
NSData *plaindata = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
plaintext = [[NSString alloc]initWithData:plaindata encoding:NSUTF8StringEncoding];
}
return plaintext;
}
@end

View File

@@ -0,0 +1,5 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "MAIDESEncryptTool.h"

View File

@@ -0,0 +1,47 @@
//
// APPUtils.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/22.
//
import Foundation
import CoreTelephony
import DeviceKit
enum TelephonyType: String{
case Unknow = "0"
case Mobile = "1"
case Unicom = "2"
case Telecom = "3"
}
public struct APPUtils {
static var currentCarrierName: String {
getCurrentCarrierName()
}
static var currentChannle: String {
getCurrentChannle()
}
static var appVersion: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
}
static var currentDeveiceId: String {
var udid = UIDevice.current.identifierForVendor?.uuidString
udid = udid?.replacingOccurrences(of: "-", with: "")
udid = udid?.lowercased()
return udid ?? ""
}
}
private extension APPUtils {
static func getCurrentCarrierName() -> String {
return TelephonyType.Unicom.rawValue
}
static func getCurrentChannle() -> String { return "yinmeng_appstore"}
}

View File

@@ -27,3 +27,6 @@ let SafeAraeBottomHeight = isSafeScreen ? 34.0 : 0.0
let SafeAraeTopmHeight = isSafeScreen ? 24.0 : 0.0
let NavHeight = (StatusBarHeight + 44)
let TabHeight = (49)
let DesKey = "1ea53d260ecf11e7b56e00163e046a26"

View File

@@ -6,7 +6,6 @@
//
import UIKit
///
protocol HiddenNavigationBarProtocol where Self: UIViewController {}

View File

@@ -0,0 +1,49 @@
//
// List+.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/24.
//
import Foundation
extension Array where Element: Equatable {
mutating func remove(_ element: Element) {
if let index = firstIndex(of: element) {
remove(at: index)
}
}
mutating func addObjects(_ elements: [Element]) {
for value in elements {
self.append(value)
}
}
mutating func replaceObject(_ element: Element, index: Int) {
if self.count == index {
self.append(element)
}
else if self.count > index {
self.insert(element, at: index)
self.remove(at: index + 1)
}
}
}
public extension Collection {
subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}

View File

@@ -0,0 +1,68 @@
//
// String+.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/23.
//
import Foundation
import CommonCrypto
extension String {
func encrypt() -> String? {
let str = MAIDESEncryptTool.encryptUseDES(self, key: DesKey)
return str
}
func decrypt() -> String? {
let str = MAIDESEncryptTool.decryptUseDES(self, key: DesKey)
return str
}
func decrypt(key:String) -> String? {
let str = MAIDESEncryptTool.decryptUseDES(self, key: key)
return str
}
}
extension String {
///
/// - Parameters:
/// - begin:
/// - count:
/// - Returns:
func substring(start: Int, _ count: Int) -> String {
let begin = index(startIndex, offsetBy: max(0, start))
let end = index(startIndex, offsetBy: min(count, start + count))
return String(self[begin..<end])
}
///
var isBlank: Bool {
let trimmedStr = self.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmedStr.isEmpty
}
func boundingRect(with constrainedSize: CGSize, font: UIFont, lineSpacing: CGFloat? = nil) -> CGSize {
let attritube = NSMutableAttributedString(string: self)
let range = NSRange(location: 0, length: attritube.length)
attritube.addAttributes([NSAttributedString.Key.font: font], range: range)
if lineSpacing != nil {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing ?? 0
attritube.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
}
let rect = attritube.boundingRect(with: constrainedSize, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
var size = rect.size
if let currentLineSpacing = lineSpacing {
let spacing = size.height - font.lineHeight
if spacing <= currentLineSpacing && spacing > 0 {
size = CGSize(width: size.width, height: font.lineHeight)
}
}
return size
}
}

View File

@@ -1,38 +0,0 @@
//
// HeadstreamRequest.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
public final class HeadstreamRequest {
/// Empty data, convenient for subsequent plugin operations.
public var result: Result<Moya.Response, MoyaError>?
public var session: Moya.Session?
///
public var endRequest: Bool = false
public init() { }
}
extension HeadstreamRequest {
func toJSON() throws -> Any {
guard let result = result else {
let userInfo = [
NSLocalizedDescriptionKey: "The result is empty."
]
let error = NSError(domain: "com.condy.rx.network", code: 2004, userInfo: userInfo)
throw error
}
switch result {
case .success(let response):
return try YMRequestX.toJSON(with: response)
case .failure(let error):
throw error
}
}
}

View File

@@ -1,190 +0,0 @@
//
// LevelStatusBarWindowController.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
public protocol LevelStatusBarWindowShowUpable {
///
/// - Parameter superview:
func makeOpenedStatusConstraint(superview: UIView)
///
func refreshBeforeShow()
///
/// - Parameters:
/// - animated:
/// - animation:
/// - completion:
func show(animated: Bool, animation: (() -> Void)?, completion: ((Bool) -> Void)?)
///
/// - Parameters:
/// - animated:
/// - animation:
/// - completion:
func close(animated: Bool, animation: (() -> Void)?, completion: ((Bool) -> Void)?)
}
///
open class LevelStatusBarWindowController: UIViewController {
private var isCalledClose = false
private var canNotBeCanceled = false
private var loadingCount: Int = 0
private lazy var lock = NSLock()
private lazy var overlay: UIView = {
let view = UIView(frame: self.view.bounds)
view.backgroundColor = overlayBackgroundColor
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(overlayTap)))
view.isUserInteractionEnabled = true
return view
}()
public var key: String?
public var showUpView: LevelStatusBarWindowShowUpable?
///
public var canCloseWhenTapOutSize: Bool = true
/// `showUpView`
public var addedShowUpView: Bool = false
public var overlayBackgroundColor: UIColor = UIColor.black.withAlphaComponent(0.2) {
didSet {
self.overlay.backgroundColor = overlayBackgroundColor
}
}
public func addedLoadingCount() {
self.lock.lock()
self.loadingCount += 1
self.lock.unlock()
}
public func subtractLoadingCount() -> Int {
self.lock.lock()
defer { self.lock.unlock() }
self.loadingCount -= 1
return self.loadingCount
}
open override var prefersStatusBarHidden: Bool {
if let controller = YMRequestX.topViewController() {
return controller.prefersStatusBarHidden
}
return true
}
open override var preferredStatusBarStyle: UIStatusBarStyle {
if let controller = YMRequestX.topViewController() {
return controller.preferredStatusBarStyle
}
return .default
}
open override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.clear
self.view.addSubview(self.overlay)
if self.addedShowUpView {
if let alertView = self.showUpView as? UIView {
self.view.bringSubviewToFront(alertView)
}
} else if let alertView = self.showUpView as? UIView {
self.view.addSubview(alertView)
}
self.showUpView?.makeOpenedStatusConstraint(superview: self.view)
}
private var overlayTapCloseBlock: ((LevelStatusBarWindowController) -> Void)?
public func setOverlayTapCloseBlock(block: @escaping (LevelStatusBarWindowController) -> Void) {
self.overlayTapCloseBlock = block
}
public func show(completion: ((Bool) -> Void)? = nil) {
Self.controllers.removeAll { $0 == self }
if let rootViewController = Self.window.rootViewController as? Self, !rootViewController.isCalledClose {
Self.controllers.append(rootViewController)
Self.window.rootViewController = nil
}
self.showUpView?.refreshBeforeShow()
if Self.lastKeyWindow != Self.window {
Self.lastKeyWindow = YMRequestX.keyWindow()
}
Self.window.isHidden = false
Self.window.windowLevel = UIWindow.Level.statusBar
Self.window.rootViewController = self
Self.window.makeKeyAndVisible()
self.overlay.alpha = 0
self.showUpView?.show(animated: true, animation: { [weak self] in
self?.overlay.alpha = 1.0
self?.overlay.backgroundColor = self?.overlayBackgroundColor
}, completion: completion)
}
public func close(animated: Bool = true) {
self.isCalledClose = true
self.showUpView?.close(animated: animated, animation: { [weak self] in
self?.overlay.alpha = 0
}, completion: self.closeCompleted)
}
private func closeCompleted(_: Bool) {
guard Self.window.rootViewController == self else {
return
}
if let lastKeyWindow = Self.lastKeyWindow {
if lastKeyWindow.rootViewController != nil {
lastKeyWindow.makeKeyAndVisible()
}
Self.lastKeyWindow = nil
} else if let window = UIApplication.shared.delegate?.window, window != nil {
window?.makeKeyAndVisible()
}
Self.window.rootViewController = nil
Self.window.isHidden = true
if Self.controllers.count < 10 {
while let rootViewController = Self.controllers.last {
if rootViewController.isCalledClose {
Self.controllers.removeLast()
continue
}
rootViewController.show()
break
}
} else {
Self.controllers.removeAll()
}
}
@objc private func overlayTap() {
if canCloseWhenTapOutSize {
close()
overlayTapCloseBlock?(self)
}
}
}
extension LevelStatusBarWindowController {
private static let window = UIWindow(frame: UIScreen.main.bounds)
private static var lastKeyWindow: UIWindow?
private static var controllers = [LevelStatusBarWindowController]()
public static func cancelAllBackgroundControllersShow() {
Self.controllers = Self.controllers.filter({ $0.canNotBeCanceled })
}
public static func forcecancelAllControllers() {
if let controller = Self.window.rootViewController as? LevelStatusBarWindowController, !controller.canNotBeCanceled {
Self.window.rootViewController = nil
Self.window.isHidden = true
}
cancelAllBackgroundControllersShow()
}
}

View File

@@ -1,139 +0,0 @@
//
// NewworkRequest.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
import Alamofire
import Moya
public extension YMNetworkAPI {
@discardableResult func HTTPRequest(
success: @escaping APISuccess,
failure: @escaping APIFailure,
progress: ProgressBlock? = nil,
queue: DispatchQueue? = nil,
plugins: APIPlugins = []
) -> Cancellable? {
let key = self.keyPrefix
let plugins__ = YMRequestX.setupPluginsAndKey(key, plugins: self.plugins + plugins)
SharedDriver.shared.addedRequestingAPI(self, key: key, plugins: plugins__)
let request = self.setupConfiguration(plugins: plugins__)
if request.endRequest, let result = request.result {
let lastResult = LastNeverResult(result: result, plugins: plugins__)
lastResult.mapResult(success: { json in
SharedDriver.shared.removeRequestingAPI(key)
DispatchQueue.main.async { success(json) }
}, failure: { error in
SharedDriver.shared.removeRequestingAPI(key)
DispatchQueue.main.async { failure(error) }
}, progress: progress)
return nil
}
let session = request.session ?? {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = YMRequestConfig.timeoutIntervalForRequest
return Moya.Session(
configuration: configuration,
startRequestsImmediately: false,
interceptor: YMRequestConfig.interceptor
)
}()
let queue = queue ?? {
DispatchQueue(label: "condy.request.network.queue", attributes: [.concurrent])
}()
let target = MultiTarget.target(self)
let endpointTask = self.task
var endpointHeaders = YMRequestX.hasNetworkHttpHeaderPlugin(key) ?? YMRequestConfig.baseHeaders
if let dict = self.headers {
// Merge the dictionaries and take the second value.
endpointHeaders = endpointHeaders.merging(dict) { $1 }
}
let provider = MoyaProvider<MultiTarget>.init(endpointClosure: { _ in
Endpoint(url: URL(target: target).absoluteString,
sampleResponseClosure: { .networkResponse(200, self.sampleData) },
method: self.method,
task: endpointTask,
httpHeaderFields: endpointHeaders)
}, stubClosure: { _ in
stubBehavior
}, callbackQueue: queue, session: session, plugins: plugins__)
//
if let json = try? request.toJSON() {
DispatchQueue.main.async { success(json) }
}
//
return self.request(plugins__, provider: provider, success: { json in
SharedDriver.shared.removeRequestingAPI(key)
DispatchQueue.main.async { success(json) }
}, failure: { error in
SharedDriver.shared.removeRequestingAPI(key)
DispatchQueue.main.async { failure(error) }
}, progress: progress)
}
@discardableResult
func request(plugins: APIPlugins = [], complete: @escaping APIComplete) -> Cancellable? {
HTTPRequest(success: { json in
complete(.success(json))
}, failure: { error in
complete(.failure(error))
}, plugins: plugins)
}
}
extension YMNetworkAPI {
///
private func setupConfiguration(plugins: APIPlugins) -> HeadstreamRequest {
var request = HeadstreamRequest()
plugins.forEach {
request = $0.configuration(request, target: self)
}
return request
}
///
private func setupOutputResult(plugins: APIPlugins, result: APIResponseResult, onNext: @escaping LastNeverCallback) {
var lastResult = LastNeverResult(result: result, plugins: plugins)
var iterator = plugins.makeIterator()
func handleLastNever(_ plugin: PluginSubType?) {
guard let plugin = plugin else {
onNext(lastResult)
return
}
plugin.lastNever(lastResult, target: self) {
lastResult = $0
handleLastNever(iterator.next())
}
}
handleLastNever(iterator.next())
}
private func request(_ plugins: APIPlugins,
provider: MoyaProvider<MultiTarget>,
success: @escaping APISuccess,
failure: @escaping APIFailure,
progress: ProgressBlock? = nil) -> Cancellable {
let target = MultiTarget.target(self)
return provider.request(target, progress: progress, completion: { result in
setupOutputResult(plugins: plugins, result: result) { lastResult in
if lastResult.againRequest {
_ = request(plugins, provider: provider, success: success, failure: failure, progress: progress)
return
}
lastResult.mapResult(success: success, failure: failure)
}
})
}
}

View File

@@ -1,167 +0,0 @@
//
// YMRequestLoadingPlugin.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
import Moya
import MBProgressHUD
/// MBProgressHUD
/// Loading plugin, based on MBProgressHUD package
public struct YMRequestLoadingPlugin: PluginPropertiesable {
public var plugins: APIPlugins = []
public var key: String?
public var delay: Double {
options.delayHideHUD
}
public var options: Options
public init(options: Options = .init()) {
self.options = options
}
/// Hide the loading hud.
public func hideMBProgressHUD() {
let vc = YMRequestX.removeHUD(key: key)
vc?.close()
}
}
extension YMRequestLoadingPlugin {
public struct Options {
/// Loading will not be automatically hidden and display window.
public static let dontAutoHide: Options = .init(autoHide: false)
/// Do you need to display an error message, the default is empty
let displayLoadText: String
/// Delay hidden, the default is zero seconds
let delayHideHUD: Double
/// Do you need to automatically hide the loading hud.
let autoHideLoading: Bool
public init(text: String = "", delay: Double = 0.0, autoHide: Bool = true) {
self.displayLoadText = text
self.delayHideHUD = delay
self.autoHideLoading = autoHide
}
var hudCallback: ((_ hud: MBProgressHUD) -> Void)?
/// Change hud related configuration closures.
public mutating func setChangeHudParameters(block: @escaping (_ hud: MBProgressHUD) -> Void) {
self.hudCallback = block
}
}
}
extension YMRequestLoadingPlugin: PluginSubType {
public var pluginName: String {
return "Loading"
}
public func willSend(_ request: RequestType, target: TargetType) {
DispatchQueue.main.async {
self.showText(options.displayLoadText)
}
}
public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
if options.autoHideLoading == false, case .success = result {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + options.delayHideHUD) {
self.hideMBProgressHUD()
}
}
}
extension YMRequestLoadingPlugin {
/// Display the prompt text
private func showText(_ text: String) {
guard let key = self.key else {
return
}
if let vc = YMRequestX.readHUD(key: key) {
if let _ = MBProgressHUD.forView(vc.view) {
return
}
vc.show()
} else {
let vc = LevelStatusBarWindowController()
// Set Activity Indicator View to white for hud loading.
let indicatorView = UIActivityIndicatorView.appearance(whenContainedInInstancesOf: [MBProgressHUD.self])
indicatorView.color = UIColor.white
let hud = MBProgressHUD.showAdded(to: vc.view, animated: true)
hud.mode = MBProgressHUDMode.indeterminate
hud.animationType = MBProgressHUDAnimation.zoom
hud.removeFromSuperViewOnHide = true
hud.bezelView.style = MBProgressHUDBackgroundStyle.solidColor
hud.bezelView.color = UIColor.black.withAlphaComponent(0.7)
hud.bezelView.layer.cornerRadius = 14
hud.detailsLabel.text = text
hud.detailsLabel.font = UIFont.systemFont(ofSize: 16)
hud.detailsLabel.numberOfLines = 0
hud.detailsLabel.textColor = UIColor.white
// User defined the hud configuration.
self.options.hudCallback?(hud)
vc.key = key
vc.showUpView = hud
vc.addedShowUpView = true
vc.show()
YMRequestX.saveHUD(key: key, window: vc)
}
}
}
extension MBProgressHUD: LevelStatusBarWindowShowUpable {
public func makeOpenedStatusConstraint(superview: UIView) {
}
public func refreshBeforeShow() {
}
public func show(animated: Bool, animation: (() -> Void)?, completion: ((Bool) -> Void)?) {
DispatchQueue.main.async {
self.show(animated: animated)
if animated {
UIView.animate(withDuration: 0.2, animations: {
animation?()
}, completion: completion)
} else {
animation?()
completion?(true)
}
}
}
public func close(animated: Bool, animation: (() -> Void)?, completion: ((Bool) -> Void)?) {
DispatchQueue.main.async {
self.hide(animated: animated)
if animated {
UIView.animate(withDuration: 0.2, animations: {
animation?()
}, completion: completion)
} else {
animation?()
completion?(true)
}
}
}
}

View File

@@ -1,169 +0,0 @@
//
// SharedDriver.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
import Moya
///
struct SharedDriver {
typealias Key = String
static var shared = SharedDriver()
private let lock = NSLock()
private let tasklock = NSLock()
private let HUDsLock = NSLock()
private var requestingAPIs = [Key: (api: YMNetworkAPI, plugins: APIPlugins)]()
private var tasks = [Key: Moya.Cancellable]()
private var cacheBlocks = [(key: Key, success: APISuccess, failure: APIFailure)]()
private var cacheHUDs = [Key: LevelStatusBarWindowController]()
}
// MARK: - api
extension SharedDriver {
func readRequestAPI(_ key: Key) -> YMNetworkAPI? {
self.lock.lock()
defer { lock.unlock() }
return self.requestingAPIs[key]?.api
}
func readRequestPlugins(_ key: Key) -> APIPlugins {
self.lock.lock()
defer { lock.unlock() }
return self.requestingAPIs[key]?.plugins ?? []
}
mutating func removeRequestingAPI(_ key: Key) {
self.lock.lock()
let plugins = self.requestingAPIs[key]?.plugins
self.requestingAPIs.removeValue(forKey: key)
// Loading
if YMRequestConfig.lastCompleteAndCloseLoadingHUDs, self.requestingAPIs.isEmpty, let p = plugins {
let maxTime = YMRequestX.maxDelayTime(with: p)
DispatchQueue.main.asyncAfter(deadline: .now() + maxTime) {
SharedDriver.shared.removeLoadingHUDs()
}
}
self.lock.unlock()
}
mutating func addedRequestingAPI(_ api: YMNetworkAPI, key: Key, plugins: APIPlugins) {
self.lock.lock()
self.requestingAPIs[key] = (api, plugins)
self.lock.unlock()
}
}
// MARK: - task and blocks
extension SharedDriver {
func readTask(key: Key) -> Cancellable? {
self.tasklock.lock()
defer { tasklock.unlock() }
return self.tasks[key]
}
mutating func cacheBlocks(key: Key, success: @escaping APISuccess, failure: @escaping APIFailure) {
self.tasklock.lock()
defer { tasklock.unlock() }
self.cacheBlocks.append((key, success, failure))
}
mutating func cacheTask(key: Key, task: Cancellable) {
self.tasklock.lock()
defer { tasklock.unlock() }
self.tasks[key] = task
}
mutating func result(_ type: Result<APISuccessJSON, APIFailureError>, key: Key) {
self.tasklock.lock()
defer { tasklock.unlock() }
switch type {
case .success(let json):
self.cacheBlocks.forEach {
$0.key == key ? $0.success(json) : nil
}
case .failure(let error):
self.cacheBlocks.forEach {
$0.key == key ? $0.failure(error) : nil
}
}
self.tasks.removeValue(forKey: key)
self.cacheBlocks.removeAll { $0.key == key }
}
}
// MARK: - hud
extension SharedDriver {
func readHUD(key: String) -> LevelStatusBarWindowController? {
self.HUDsLock.lock()
defer { HUDsLock.unlock() }
return self.cacheHUDs[key]
}
func readHUD(prefix: String) -> [LevelStatusBarWindowController] {
self.HUDsLock.lock()
defer { HUDsLock.unlock() }
return self.cacheHUDs.compactMap {
if let prefix_ = $0.key.components(separatedBy: "_").first, prefix_ == prefix {
return $0.value
}
return nil
}
}
func readHUD(suffix: String) -> [LevelStatusBarWindowController] {
self.HUDsLock.lock()
defer { HUDsLock.unlock() }
return self.cacheHUDs.compactMap {
if let suffix_ = $0.key.components(separatedBy: "_").last, suffix_ == suffix {
return $0.value
}
return nil
}
}
mutating func saveHUD(key: Key, window: LevelStatusBarWindowController) {
self.HUDsLock.lock()
self.cacheHUDs[key] = window
self.HUDsLock.unlock()
}
@discardableResult mutating func removeHUD(key: Key?) -> LevelStatusBarWindowController? {
guard let key = key else {
return nil
}
self.HUDsLock.lock()
let window = self.cacheHUDs[key]
self.cacheHUDs.removeValue(forKey: key)
self.HUDsLock.unlock()
return window
}
mutating func removeAllAtLevelStatusBarWindow() {
self.HUDsLock.lock()
self.cacheHUDs.forEach {
$0.value.close()
}
self.cacheHUDs.removeAll()
self.HUDsLock.unlock()
}
mutating func removeLoadingHUDs() {
self.HUDsLock.lock()
for (key, hud) in self.cacheHUDs where YMRequestX.loadingSuffix(key: key) {
self.cacheHUDs.removeValue(forKey: key)
hud.close()
}
self.HUDsLock.unlock()
}
}

View File

@@ -1,69 +0,0 @@
//
// YMLastNeverResult.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
public typealias LastNeverCallback = ((_ lastResult: LastNeverResult) -> Void)
/// Containing the data source and whether auto last network request.
public final class LastNeverResult {
public var result: Result<Moya.Response, MoyaError>
/// Any
/// Solve the problem of repeated parsing, if a plugin has parsed the data into `Any`
public var mapResult: Result<Any, MoyaError>?
///
public var againRequest: Bool = false
private let plugins: APIPlugins
public init(result: Result<Moya.Response, MoyaError>, plugins: APIPlugins) {
self.result = result
self.plugins = plugins
}
}
extension LastNeverResult {
func mapResult(success: APISuccess? = nil, failure: APIFailure? = nil, progress: ProgressBlock? = nil) {
if let mapResult = mapResult {
switch mapResult {
case let .success(json):
success?(json)
case let .failure(error):
failure?(error)
}
return
}
switch result {
case let .success(response):
do {
let json = try YMRequestX.toJSON(with: response)
self.mapResult = .success(json)
success?(json)
progress?(ProgressResponse(response: response))
} catch MoyaError.statusCode(let response) {
let error = MoyaError.statusCode(response)
self.mapResult = .failure(error)
failure?(error)
} catch MoyaError.jsonMapping(let response) {
let error = MoyaError.jsonMapping(response)
self.mapResult = .failure(error)
failure?(error)
} catch {
if let error = error as? MoyaError {
self.mapResult = .failure(error)
}
failure?(error)
}
case let .failure(error):
self.mapResult = .failure(error)
failure?(error)
}
}
}

View File

@@ -1,34 +0,0 @@
//
// YMNetworkAPI.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
@_exported import Alamofire
@_exported import Moya
public typealias APIHost = String
public typealias APIPath = String
public typealias APINumber = Int
public typealias APIMethod = Moya.Method
public typealias APIParameters = Alamofire.Parameters
public typealias APIPlugins = [PluginSubType]
public typealias APIStubBehavior = Moya.StubBehavior
public typealias APISuccessJSON = Any
public typealias APIFailureError = Swift.Error
public typealias APIResponseResult = Result<Moya.Response, MoyaError>
public typealias APISuccess = (_ json: APISuccessJSON) -> Void
public typealias APIFailure = (_ error: APIFailureError) -> Void
public typealias APIComplete = (_ result: Result<APISuccessJSON, APIFailureError>) -> Void
public protocol YMNetworkAPI: Moya.TargetType {
var hostUrl:APIHost {get}
var pararms:APIParameters? {get}
var plugins:APIPlugins {get}
var stubBehavior: APIStubBehavior {get}
var retry:APINumber {get}
var keyPrefix: String { get }
func removeHUD()
func removeLoading()
}

View File

@@ -1,73 +0,0 @@
//
// YMPluginSubType.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
import Moya
public protocol PluginPropertiesable: PluginSubType {
var plugins: APIPlugins { get set }
var key: String? { get set }
/// Loading HUD delay hide time.
var delay: Double { get }
}
extension PluginPropertiesable {
public var delay: Double {
return 0
}
}
/// Moya便
/// Inherit the Moya plug-in protocol, which is convenient for subsequent expansion. All plug-in methods must implement this protocol
public protocol PluginSubType: Moya.PluginType {
///
var pluginName: String { get }
///
///
/// - Parameters:
/// - request:
/// - target:
/// - Returns:
///
/// After setting the network configuration information, before starting to prepare the request,
/// This method can be used in scenarios such as throwing data directly when the local cache exists without executing subsequent network requests.
/// - Parameters:
/// - request: Configuration information, which contains the data source and whether to end the subsequent network request.
/// - target: The protocol used to define the specifications necessary for a `MoyaProvider`.
/// - Returns: Containing the data source and whether to end the subsequent network request.
func configuration(_ request: HeadstreamRequest, target: TargetType) -> HeadstreamRequest
///
///
/// - Parameters:
/// - result:
/// - target:
/// - onNext:
///
/// The last time the last network response is returned,
/// This method can be used in scenarios such as key invalidation to obtain the key again and then automatically request the network again.
/// - Parameters:
/// - result: Containing the data source and whether auto-last network request.
/// - target: The protocol used to define the specifications necessary for a `MoyaProvider`.
/// - onNext: Provide callbacks for the plug-in to process tasks asynchronously.
func lastNever(_ result: LastNeverResult, target: TargetType, onNext: @escaping LastNeverCallback)
}
extension PluginSubType {
public func configuration(_ request: HeadstreamRequest, target: TargetType) -> HeadstreamRequest {
return request
}
public func lastNever(_ result: LastNeverResult, target: TargetType, onNext: @escaping LastNeverCallback) {
onNext(result)
}
}

View File

@@ -1,56 +0,0 @@
//
// YMRequestConfig.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
import Moya
///
/// Network configuration information, only need to be configured once when the program is started
public struct YMRequestConfig {
/// Whether to add the Debugging plugin by default
public static var addDebugging: Bool = false
/// Whether to add the Indicator plugin by default
public static var addIndicator: Bool = false
/// Set the request timeout, the default is 30 seconds
public static var timeoutIntervalForRequest: Double = 30
public static var interceptor: RequestInterceptor? = nil
/// Root path address
public static var baseURL: APIHost = ""
/// Default request type, default `post`
public static var baseMethod: APIMethod = APIMethod.post
/// Default basic parameters, similar to: userID, token, etc.
public static var baseParameters: APIParameters = [:]
/// Default Header argument, `NetworkHttpHeaderPlugin`.
public static var baseHeaders: [String: String] = [:]
/// Plugins that require default injection, generally not recommended
/// However, you can inject this kind of global unified general plugin, such as secret key plugin, certificate plugin, etc.
public static var basePlugins: [PluginSubType]?
/// Loading animation JSON, for `AnimatedLoadingPlugin` used.
public static var animatedJSON: String?
/// Loading the plugin name, to remove the loading plugin from level status bar window.
public static var loadingPluginNames: [String] = ["Loading", "AnimatedLoading"]
/// Auto close all loading after the end of the last network requesting.
public static var lastCompleteAndCloseLoadingHUDs: Bool = true
/// Update the default basic parameter data, which is generally used for what operation the user has switched.
/// - Parameters:
/// - value: Update value
/// - key: Update key
public static func updateBaseParametersWithValue(_ value: AnyObject?, key: String) {
var dict = YMRequestConfig.baseParameters
if let value = value {
dict.updateValue(value, forKey: key)
} else {
dict.removeValue(forKey: key)
}
YMRequestConfig.baseParameters = dict
}
}

View File

@@ -1,228 +0,0 @@
//
// YMRequestX.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/2.
//
import Foundation
public struct YMRequestX {
/// Maps data received from the signal into a JSON object.
public static func mapJSON<T>(_ type: T.Type, named: String, forResource: String = "RxNetworks") -> T? {
guard let data = jsonData(named, forResource: forResource) else {
return nil
}
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
return json as? T
}
/// Read json data
public static func jsonData(_ named: String, forResource: String = "RxNetworks") -> Data? {
let bundle: Bundle?
if let bundlePath = Bundle.main.path(forResource: forResource, ofType: "bundle") {
bundle = Bundle.init(path: bundlePath)
} else {
bundle = Bundle.main
}
guard let path = ["json", "JSON", "Json"].compactMap({
bundle?.path(forResource: named, ofType: $0)
}).first else {
return nil
}
let contentURL = URL(fileURLWithPath: path)
return try? Data(contentsOf: contentURL)
}
public static func toJSON(form value: Any, prettyPrint: Bool = false) -> String? {
guard JSONSerialization.isValidJSONObject(value) else {
return nil
}
var jsonData: Data? = nil
if prettyPrint {
jsonData = try? JSONSerialization.data(withJSONObject: value, options: [.prettyPrinted])
} else {
jsonData = try? JSONSerialization.data(withJSONObject: value, options: [])
}
guard let data = jsonData else { return nil }
return String(data: data ,encoding: .utf8)
}
public static func toDictionary(form json: String) -> [String : Any]? {
guard let jsonData = json.data(using: .utf8),
let object = try? JSONSerialization.jsonObject(with: jsonData, options: []),
let result = object as? [String : Any] else {
return nil
}
return result
}
public static func keyWindow() -> UIWindow? {
if #available(iOS 13.0, *) {
return UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first(where: { $0 is UIWindowScene })
.flatMap({ $0 as? UIWindowScene })?.windows
.first(where: \.isKeyWindow)
} else {
return UIApplication.shared.keyWindow
}
}
public static func topViewController() -> UIViewController? {
let window = UIApplication.shared.delegate?.window
guard window != nil, let rootViewController = window?!.rootViewController else {
return nil
}
return self.getTopViewController(controller: rootViewController)
}
public static func getTopViewController(controller: UIViewController) -> UIViewController {
if let presentedViewController = controller.presentedViewController {
return self.getTopViewController(controller: presentedViewController)
} else if let navigationController = controller as? UINavigationController {
if let topViewController = navigationController.topViewController {
return self.getTopViewController(controller: topViewController)
}
return navigationController
} else if let tabbarController = controller as? UITabBarController {
if let selectedViewController = tabbarController.selectedViewController {
return self.getTopViewController(controller: selectedViewController)
}
return tabbarController
} else {
return controller
}
}
}
// MARK: - HUD
extension YMRequestX {
/// HUD
public static func removeAllAtLevelStatusBarWindow() {
SharedDriver.shared.removeAllAtLevelStatusBarWindow()
}
/// HUD
public static func removeLoadingHUDs() {
SharedDriver.shared.removeLoadingHUDs()
}
public static func readHUD(key: String) -> LevelStatusBarWindowController? {
SharedDriver.shared.readHUD(key: key)
}
public static func saveHUD(key: String, window vc: LevelStatusBarWindowController) {
SharedDriver.shared.saveHUD(key: key, window: vc)
}
@discardableResult
public static func removeHUD(key: String?) -> LevelStatusBarWindowController? {
SharedDriver.shared.removeHUD(key: key)
}
}
// MARK: -
extension YMRequestX {
static func maxDelayTime(with plugins: APIPlugins) -> Double {
let times: [Double] = plugins.compactMap {
if let p = $0 as? PluginPropertiesable {
return p.delay
}
return 0.0
}
let maxTime = times.max() ?? 0.0
return maxTime
}
static func sortParametersToString(_ parameters: APIParameters?) -> String {
guard let params = parameters, !params.isEmpty else {
return ""
}
var paramString = "?"
let sorteds = params.sorted(by: { $0.key > $1.key })
for index in sorteds.indices {
paramString.append("\(sorteds[index].key)=\(sorteds[index].value)")
if index != sorteds.count - 1 { paramString.append("&") }
}
return paramString
}
static func requestLink(with target: TargetType, parameters: APIParameters? = nil) -> String {
let parameters: APIParameters? = parameters ?? {
if case .requestParameters(let parame, _) = target.task {
return parame
}
return nil
}()
let paramString = sortParametersToString(parameters)
return target.baseURL.absoluteString + target.path + paramString
}
static func toJSON(with response: Moya.Response) throws -> APISuccessJSON {
let response = try response.filterSuccessfulStatusCodes()
return try response.mapJSON()
}
static func loadingSuffix(key: SharedDriver.Key?) -> Bool {
guard let key = key else { return false }
if let suffix = key.components(separatedBy: "_").last, YMRequestConfig.loadingPluginNames.contains(suffix) {
return true
}
return false
}
static func setupPluginsAndKey(_ key: String, plugins: APIPlugins) -> APIPlugins {
var plugins = plugins
YMRequestX.setupBasePlugins(&plugins)
return plugins.map({
if var plugin = $0 as? PluginPropertiesable {
plugin.plugins = plugins
plugin.key = key + "_" + plugin.pluginName
return plugin
}
return $0
})
}
}
// MARK: -
extension YMRequestX {
///
static func setupBasePlugins(_ plugins: inout APIPlugins) {
var plugins_ = plugins
if let others = YMRequestConfig.basePlugins {
plugins_ += others
}
#if RXNETWORKS_PLUGINGS_INDICATOR
if NetworkConfig.addIndicator, !plugins_.contains(where: { $0 is NetworkIndicatorPlugin}) {
let Indicator = NetworkIndicatorPlugin.shared
plugins_.insert(Indicator, at: 0)
}
#endif
#if DEBUG && RXNETWORKS_PLUGINGS_DEBUGGING
if NetworkConfig.addDebugging, !plugins_.contains(where: { $0 is NetworkDebuggingPlugin}) {
let Debugging = NetworkDebuggingPlugin.init()
plugins_.append(Debugging)
}
#endif
plugins = plugins_
}
///
static func hasNetworkHttpHeaderPlugin(_ key: String) -> [String: String]? {
#if RXNETWORKS_PLUGINGS_HTTPHEADER
let plugins = SharedDriver.shared.readRequestPlugins(key)
if let p = plugins.first(where: { $0 is NetworkHttpHeaderPlugin }) {
return (p as? NetworkHttpHeaderPlugin)?.dictionary
}
#endif
return nil
}
}

View File

@@ -2,6 +2,11 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>

View File

@@ -0,0 +1,15 @@
//
// AppConfigObject.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/22.
//
import Foundation
struct AppConfigObject : HandyJSON {
var certificationType:Bool? = false
var reportSwitch:Bool? = false
var teenagerMode:Bool? = false
var homeTabList:[String]? = []
}

View File

@@ -7,12 +7,23 @@
import UIKit
import SnapKit
import NSObject_Rx
class AuthLoginVC: BaseViewController, HiddenNavigationBarProtocol {
var countdownSeconds = 60
var timer: Timer?
var viewModel:AuthViewModel = AuthViewModel()
var phone:String = ""
var code:String = ""
override func viewDidLoad() {
super.viewDidLoad()
loadSubViews()
viewModel.data.subscribe(onNext: { [weak self] success in
if success {
self?.startCountdown()
}
}).disposed(by: rx.disposeBag)
}
func loadSubViews() {
@@ -180,6 +191,7 @@ class AuthLoginVC: BaseViewController, HiddenNavigationBarProtocol {
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
button.addTarget(self, action: #selector(phoneLoginBtnAction), for: .touchUpInside)
button.isSelected = true
return button
}()
@@ -374,11 +386,19 @@ extension AuthLoginVC {
}
@objc func getCodeBtnAction() {
if phone.count > 0 {
viewModel.getSmsCode(phone: phone, type: 1)
} else {
HUDTool.showSuccess(with: "请输入正确的手机号码")
}
}
@objc func confirmBtnAction() {
if self.phoneLoginBtn.isSelected == true {
viewModel.authPhoneCode(phone: phone, code: code)
} else {
//TODO: id
}
}
@objc func forgetBtnAction() {
@@ -386,11 +406,21 @@ extension AuthLoginVC {
}
@objc func phoneTextFiledDidChange(_ textField: UITextField) {
if let text = textField.text {
if text.count > 11 {
textField.text = text.substring(start: 0, 11)
}
}
phone = textField.text ?? ""
}
@objc func codeTextFiledDidChange(_ textField: UITextField) {
if let text = textField.text {
if text.count > 11 {
textField.text = text.substring(start: 0, 11)
}
}
code = textField.text ?? ""
}
@objc func idTextFiledDidChange(_ textField: UITextField) {
@@ -401,3 +431,38 @@ extension AuthLoginVC {
}
}
extension AuthLoginVC {
func startCountdown() {
if timer != nil {
timer?.invalidate()
timer = nil
}
countdownSeconds = 60
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true)
timer!.fire()
}
@objc func updateCountdown() {
countdownSeconds -= 1
if countdownSeconds <= 0 {
getCodeBtn.setTitle("重新获取验证码", for: .normal)
getCodeBtn.isEnabled = true
stopCountdown()
return
}
getCodeBtn.isHidden = false
let seconds = countdownSeconds % 60
getCodeBtn.setTitle("重新获取验证码(\(seconds)s", for: .disabled)
getCodeBtn.isEnabled = false
}
func stopCountdown() {
timer?.invalidate()
timer = nil
}
}

View File

@@ -5,8 +5,50 @@
// Created by MaiMang on 2024/2/21.
//
import Foundation
//import Foundation
//import Moya
//enum AuthAPI {
//
// case appConfig
// case getCode(String, String)
// case authToken(String, String)
//}
//
//extension AuthAPI: YMNetworkAPI {
// var hostUrl: APIHost {
// return YMRequestConfig.baseURL
// }
//
// var pararms: APIParameters? {
// switch self {
// case .getCode(let phone, let type):
// return ["mobile":phone, "type":type]
// case .authToken(let phone, let code):
// return ["phone":phone, "code":code, "client_secret":"uyzjdhds", "version":"1", "client_id":"erban-client", "grant_type":"password"]
// default:
// return [:]
// }
// }
//
// var path: String {
// switch self {
// case .appConfig:
// return "/client/init"
// case .getCode:
// return "/sms/getCode"
// case .authToken:
// return "/oauth/token"
// }
//
// }
//
// var method: Moya.Method {
// switch self {
// case .appConfig:
// return .get
// case .getCode:
// return .post
// case .authToken:
// return .post
// }
// }
//}

View File

@@ -0,0 +1,67 @@
//
// AuthManager.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/24.
//
import Foundation
private let UserTokenKey = "UserTokenKey"
private let UserTicketKey = "UserTicketKey"
class AuthManager: NSObject {
var tokenInfo:UserTokenObject?
static var userUid:Int {
return LoginTokenConfig.config.getAccountInfo()?.uid ?? 0
}
static var ticket:String{
return LoginTokenConfig.config.getTicket()
}
}
class LoginTokenConfig: NSObject {
public static let config = LoginTokenConfig.init()
var tokenInfo: UserTokenObject?
var ticket:String?
func saveTokenToLocaltion(token: UserTokenObject) {
self.tokenInfo = token
if let dic = token.dictionary {
UserDefaults.standard.setValue(dic, forKey: UserTokenKey)
UserDefaults.standard.synchronize()
}
}
func saveTicketToLoaction(ticket:String) {
self.ticket = ticket
UserDefaults.standard.setValue(ticket, forKey: UserTicketKey)
UserDefaults.standard.synchronize()
}
func getTicket() -> String {
if let ticket = self.ticket {
return ticket
}
if let ticket = UserDefaults.standard.value(forKey: UserTicketKey) as? String{
self.ticket = ticket
}
return self.ticket ?? ""
}
func getAccountInfo() -> UserTokenObject? {
if let accountModel = self.tokenInfo {
return accountModel
}
if let dic = UserDefaults.standard.value(forKey: UserTokenKey), let model = Deserialized<UserTokenObject>.toModel(with: dic){
self.tokenInfo = model
}
return self.tokenInfo
}
}

View File

@@ -0,0 +1,28 @@
//
// AuthViewModel.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/22.
//
import Foundation
import RxSwift
class AuthViewModel: NSObject {
let data = PublishSubject<Bool>()
func getSmsCode(phone:String, type:Int) {
if let phoneDes = phone.encrypt() {
let params:[String: Any] = ["mobile":phoneDes, "type":type]
YMNetworkHelper.share.requestSend(type: .post, path: "sms/getCode", params: params, succeed: { data in
self.data.onNext(true)
}, fail: { code, msg in
self.data.onNext(false)
})
}
}
func authPhoneCode(phone:String, code:String) {
}
}

View File

@@ -0,0 +1,15 @@
//
// UserTokenObject.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/2/24.
//
import Foundation
import HandyJSON
struct UserTokenObject: HandyJSON, Codable {
var uid:Int? = 0
var netEaseToken:String? = ""
var access_token:String? = ""
}