ios-swift-developers/swift Learning Repository

repository·master·Indexed 23 days ago

https://github.com/ios-swift-developers/swift

A comprehensive learning repository for Swift developers covering basic syntax and advanced applications. The documentation includes detailed guides on using Alamofire for HTTP networking, featuring request/response methods, parameter encoding, SessionManager configuration, and the implementation of the Router pattern via URLRequestConvertible. It also covers advanced networking topics such as RequestAdapter and RequestRetrier for OAuth2 refresh flows, custom response serialization, and the use of AlamofireImage and AlamofireNetworkActivityIndicator.

Tokens
35.5K
Snippets
71
Records
142
Agent score
80%

What's inside ios-swift-developers/swift

  1. What is RxSwift?

    master
    RxSwift is a Swift implementation of Reactive Extensions (Rx). It provides a generic abstraction of computation expressed through the Observable<Element> interface. It is designed to enable easy composition of asynchronous operations and event/data streams by unifying KVO observing, async operations, and streams under the abstraction of a sequence.
  2. Overview of Alamofire features

    master

    Alamofire is an HTTP networking library for Swift that provides:

    • Chainable Request/Response methods
    • Parameter encoding (URL, JSON, plist)
    • File, Data, Stream, and MultipartFormData uploading
    • File downloading with support for resuming data
    • Authentication via URLCredential
    • HTTP response validation
    • Progress closures for uploads and downloads
    • Request adaptation and retrying
    • TLS Certificate and Public Key Pinning
    • Network Reachability monitoring
  3. Overview of Kingfisher features

    master

    Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. Key features include:

    • Asynchronous downloading and caching: Uses URLSession-based networking.
    • Multi-layer cache: Supports both memory and disk caching.
    • Task Management: Cancelable downloading and processing tasks to improve performance.
    • Component Independence: You can use the downloader or caching system separately.
    • Prefetching: Ability to prefetch images for later use.
    • UI Extensions: Direct support for UIImageView, NSImage, and UIButton.
    • Animations: Built-in transition animations when setting images.
    • Extensibility: Support for custom image processing and image formats.
  4. Manage multiple ServerTrustPolicies with ServerTrustPolicyManager

    master

    Use ServerTrustPolicyManager to map different ServerTrustPolicy configurations to specific hosts. This allows you to apply strict pinning to sensitive domains while using default evaluation or disabling evaluation for others.

    Important: You must keep a strong reference to the SessionManager instance containing the ServerTrustPolicyManager, otherwise requests will be cancelled when the manager is deallocated.

    To implement custom matching logic (like wildcard domains), subclass ServerTrustPolicyManager and override serverTrustPolicy(forHost:).

    let serverTrustPolicies: [String: ServerTrustPolicy] = [
        "test.example.com": .pinCertificates(
            certificates: ServerTrustPolicy.certificates(),
            validateCertificateChain: true,
            validateHost: true
        ),
        "insecure.expired-apis.com": .disableEvaluation
    ]
    
    let sessionManager = SessionManager(
        serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
    )
    
    class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
        override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
            var policy: ServerTrustPolicy?
            // Implement custom domain matching behavior...
            return policy
        }
    }
  5. Customize SessionDelegate behavior

    master

    The SessionDelegate handles URLSession delegate callbacks. You can customize this behavior in two ways:

    1. Override Closures: The preferred method for customizing specific behaviors (like authentication challenges or redirects) without replacing the entire delegate. This allows you to keep the default implementation for all other events.
    2. Subclassing: Use this as a last resort if you need to completely replace the delegate logic or create a proxy (e.g., for logging). Be cautious of memory leaks as subdelegates are managed by the default implementation.

    Commonly used override closures include:

    • sessionDidReceiveChallenge: For authentication challenges.
    • sessionDidFinishEventsForBackgroundURLSession: For background session completion.
    • taskWillPerformHTTPRedirection: For handling HTTP redirects.
    • dataTaskWillCacheResponse: For managing response caching.
    // Example: Using an override closure to prevent redirects to apple.com
    let sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default)
    let delegate: Alamofire.SessionDelegate = sessionManager.delegate
    
    delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
        var finalRequest = request
    
        if let originalRequest = task.originalRequest, 
           let urlString = originalRequest.url?.urlString, 
           urlString.contains("apple.com") {
            finalRequest = originalRequest
        }
    
        return finalRequest
    }
    
    // Example: Subclassing SessionDelegate for logging
    class LoggingSessionDelegate: SessionDelegate {
        override func urlSession(
            _ session: URLSession,
            task: URLSessionTask,
            willPerformHTTPRedirection response: HTTPURLResponse,
            newRequest request: URLRequest,
            completionHandler: @escaping (URLRequest?) -> Void)
        {
            print("URLSession will perform HTTP redirection to request: \(request)")
            super.urlSession(
                session,
                task,
                willPerformHTTPRedirection: response,
                newRequest: request,
                completionHandler: completionHandler
            )
        }
    }
  6. RxSwift Core Concepts

    master

    RxSwift is a generic abstraction of computation expressed through the Observable<Element> interface. It unifies KVO observing, asynchronous operations, and data streams under the single abstraction of a sequence.

    Key concepts include:

    • Observables: Sequences of data or events.
    • Traits: Specialized observable types like Single, Completable, Maybe, Driver, ControlProperty, and Variable that provide specific semantic guarantees.
    • Operators: Functions used to transform, filter, or combine sequences (e.g., throttle, flatMapLatest, distinctUntilChanged).
  7. Implement the Router pattern with URLRequestConvertible

    master

    For complex applications, use the URLRequestConvertible protocol to implement the Router pattern. This allows you to centralize endpoint definitions, manage HTTP methods (GET, POST, etc.), handle parameter encoding, and manage authentication in a single, type-safe enum.

    This approach abstracts away server-side inconsistencies and provides a clean API for the rest of your application.

    // Example: A Router for search functionality
    enum Router: URLRequestConvertible {
        case search(query: String, page: Int)
    
        static let baseURLString = "https://example.com"
        static let perPage = 50
    
        func asURLRequest() throws -> URLRequest {
            let result: (path: String, parameters: Parameters) = {
                switch self {
                case let .search(query, page) where page > 0:
                    return ("/search", ["q": query, "offset": Router.perPage * page])
                case let .search(query, _):
                    return ("/search", ["q": query])
                }
            }()
    
            let url = try Router.baseURLString.asURL()
                .appendingPathComponent(result.path)
            let urlRequest = URLRequest(url: url)
    
            return try URLEncoding.default.encode(urlRequest, with: result.parameters)
        }
    }
    
    // Usage
    Alamofire.request(Router.search(query: "foo bar", page: 1)) 
    // Resulting URL: https://example.com/search?q=foo%20bar&offset=50
  8. Alamofire ecosystem component libraries

    master

    The Alamofire Software Foundation provides additional libraries to extend functionality:

    • AlamofireImage: Includes image response serializers, UIImage/UIImageView extensions, custom filters, an in-memory cache, and a priority-based downloading system.
    • AlamofireNetworkActivityIndicator: Controls the visibility of the iOS network activity indicator with configurable delay timers to prevent flicker.
  9. Implement generic response object serialization

    master

    To achieve automatic, type-safe serialization of JSON responses into Swift objects, implement the ResponseObjectSerializable protocol. This allows you to add a .responseObject() method to DataRequest.

    1. Define a protocol ResponseObjectSerializable with an initializer: init?(response: HTTPURLResponse, representation: Any).
    2. Extend DataRequest to include a responseObject<T: ResponseObjectSerializable> method that uses a DataResponseSerializer to parse JSON and then initialize the object.
    3. Implement the protocol in your model structs.
    protocol ResponseObjectSerializable {
        init?(response: HTTPURLResponse, representation: Any)
    }
    
    struct User: ResponseObjectSerializable {
        let username: String
        let name: String
    
        init?(response: HTTPURLResponse, representation: Any) {
            guard
                let username = response.url?.lastPathComponent,
                let representation = representation as? [String: Any],
                let name = representation["name"] as? String
            else { return nil }
    
            self.username = username
            self.name = name
        }
    }
    
    Alamofire.request("https://example.com/users/mattt").responseObject { (response: DataResponse<User>) in
        if let user = response.result.value {
            print("User: { username: \(user.username), name: \(user.name) }")
        }
    }
  10. Adapt and retry requests with RequestAdapter and RequestRetrier

    master

    Alamofire provides the RequestAdapter and RequestRetrier protocols to handle complex authentication flows (like OAuth2) in a thread-safe manner.

    • RequestAdapter: Allows you to inspect and modify a URLRequest before it is sent. A common use case is appending an Authorization header (e.g., a Bearer token) to requests.
    • RequestRetrier: Allows you to intercept a request that encountered an error and decide whether it should be retried. This is ideal for implementing credential refresh logic (e.g., catching a 401 Unauthorized error, refreshing the token, and retrying the original request).

    You can assign an object conforming to these protocols to a SessionManager's adapter and retrier properties.

    // Example: Using an adapter to add an Authorization header
    class AccessTokenAdapter: RequestAdapter {
        private let accessToken: String
        init(accessToken: String) { self.accessToken = accessToken }
    
        func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
            var urlRequest = urlRequest
            if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix("https://httpbin.org") {
                urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
            }
            return urlRequest
        }
    }
    
    let sessionManager = SessionManager()
    sessionManager.adapter = AccessTokenAdapter(accessToken: "1234")
    sessionManager.request("https://httpbin.org/get")
  11. Implement Type-Safe Routing with URLConvertible and URLRequestConvertible

    master

    To manage complex network stacks, use the Router pattern by adopting these protocols:

    URLConvertible

    Use this to map domain models to URLs. Types like String, URL, and URLComponents conform by default. Custom types can implement asURL() to provide type-safe endpoint construction.

    URLRequestConvertible

    This is the recommended way to specify custom HTTP bodies and headers for individual requests. It allows you to encapsulate endpoint logic, parameter encoding, and authentication into a single type (often an enum).

    // Example: URLConvertible for type-safe user URLs
    extension User: URLConvertible {
        static let baseURLString = "https://example.com"
    
        func asURL() throws -> URL {
            let urlString = User.baseURLString + "/users/\(username)/"
            return try urlString.asURL()
        }
    }
    
    // Example: URLRequestConvertible Router for API Parameter Abstraction
    enum Router: URLRequestConvertible {
        case search(query: String, page: Int)
    
        static let baseURLString = "https://example.com"
        static let perPage = 50
    
        func asURLRequest() throws -> URLRequest {
            let result: (path: String, parameters: Parameters) = {
                switch self {
                case let .search(query, page) where page > 0:
                    return ("/search", ["q": query, "offset": Router.perPage * page])
                case let .search(query, _):
                    return ("/search", ["q": query])
                }
            }()
    
            let url = try Router.baseURLString.asURL()
            let urlRequest = URLRequest(url: url.appendingPathComponent(result.path))
            return try URLEncoding.default.encode(urlRequest, with: result.parameters)
        }
    }
    
    // Usage
    Alamofire.request(Router.search(query: "foo bar", page: 1)) // ?q=foo%20bar&offset=50
  12. How GPUImage architecture works

    master

    GPUImage uses OpenGL ES 2.0 shaders to perform high-speed image and video manipulation. It abstracts the complexity of the OpenGL ES API through a simplified Objective-C interface.

    The processing model follows a chain of objects:

    1. Input Sources (GPUImageOutput subclasses): These upload frames as textures. Common sources include:
      • GPUImageVideoCamera: Live video from the iOS camera.
      • GPUImageStillCamera: For taking photos with the camera.
      • GPUImagePicture: For still images.
      • GPUImageMovie: For movies.
    2. Filters (GPUImageInput protocol): These receive textures from the previous link in the chain, apply effects, and pass them down.
    3. Targets: Objects at the end of the chain that receive the processed output (e.g., displaying to a GPUImageView, saving to a UIImage, or writing to a movie file on disk).

    Processing can be branched by adding multiple targets to a single output or filter.