zoukankan      html  css  js  c++  java
  • URL Handle in Swift (一) -- URL 分解

    更新时间: 2018-6-6

    在程序开发过程之中, 我们总是希望模块化处理某一类相似的事情。 在 ezbuy 开发中, 我接触到了对于 URL 处理的优秀的代码, 学习、改进、记录下来。希望对你有所帮助。

    对于 URL 处理, 我将分为两个部分来说明:

    • URL Handle in Swift (一) - URL 分解;
    • URL Handle in Swift (二) - 响应链处理 URL 。

    一、 URL 格式分析

    URL(Uniform Resource Locator) 全称为“统一资源定位符”, 有时也被俗称为网页地址网址)

    URL标准格式如下:

    url标准格式

    截取《网络是怎样连接的》的图片来解释一下(图侵删)

    实际上, URL的标准格式中, 我们可以通过参数来传递相关的信息, 如 https://www.google.com.hk/search?q=rsenjoyer

    二、 iOS 客户端处理 URL

    iOS客户端的URL大体可以分为两类:

    • 自定义协议的URL, 如: igame://myNote
    • 标准的HTTP(S) URL;

    理论上你可以自定义任意的协议来构建URL, 响应协议URL。 在 iOS 开发中,你可以来自定义 URL Scheme 来实现应用程序间的通信。 URL Scheme 相关的信息你可以参考URL Schemes 使用详解 以及 Inter-App Communication

    对于标准的 HTTP(S) URL,不同的URL有着不一样的处理的方式。如:大多情况下, 应用程序受到一个URL就直接用 WKWebView 或者 UIWebView 直接打开; 但处理 Universal Link是需要打开应用程序内对应的页面等。

    根据需求, 可以设计出如下的结构图:

    不同的URL通过Bridge最终都转换为 “统一”的 Instruction, Instruction保存着 URL所有的信息, “Handler”通过 Instruction的信息执行不一样的处理。

    Talk is Cheap, Show me the code!!

    2.1 Bridge代码的实现

    Bridge 根据URL的不同而转换成 Instruction。

    //
    //  IGBridge.swift
    //  eziNode
    //
    //  Created by enjoy on 2018/4/9.
    //  Copyright © 2018年 enjoy. All rights reserved.
    //
    
    import Foundation
    
    protocol IGBridge {
        
        func bridgeToIG(from url: URL?) -> IGInstruction?
        
        func bridgeToURL(from instruction: IGInstruction?) -> URL?
    }
    
    class IGameAppURLBridge: IGBridge {
        
        private static let iGameAppScheme = "igame"
        
        static let `default` = IGameAppURLBridge()
        
        func bridgeToIG(from url: URL?) -> IGInstruction? {
            
            guard let url = url else { return nil }
            
            guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true), urlComponents.scheme?.lowercased() == IGameAppURLBridge.iGameAppScheme else { return nil }
            
            guard let host = urlComponents.host else { return nil }
            
            let queries = makeDictionary(fromQueryItems: urlComponents.queryItems)
            
            let components = urlComponents.path.components(separatedBy: "/").filter { !$0.isEmpty }
            
            let instruction = IGInstruction(type: IGInstructionType(identifier: host), components: components, queryItems: queries, bridge: self)
            
            return instruction
        }
        
        func bridgeToURL(from instruction: IGInstruction?) -> URL? { 
            // 转换为 URL
        }
    }
    
    class HttpWebURLBridge: IGBridge {
        // HttpWebURLBridge 的实现
    }
    
    class UniversalLinURLBridge: IGBridge {
        // UniversalLinURLBridge 的实现
    }
    
    

    2.2 Instruction代码实现

    “Instruction”需要包含着 URL 完整的以及需要怎样被处理. 客户端所支持的 URL 类型实际上可以用一个枚举来列出。

    
    import Foundation
    
    // 客户端支持的URL 类型
    public enum IGInstructionType: String {
        
        case specialExercise
        case examinationPaper
        case redoWrong
        case collection
        case myNote
        case refreshQuestionSet
        case unKnown
        
        init(identifier: String?) {
            guard let identifier = identifier else { self = .unKnown;  return }
            
            switch identifier {
            case IGInstructionType.specialExercise.rawValue:
                self = .specialExercise
            case IGInstructionType.examinationPaper.rawValue:
                self = .examinationPaper
            case IGInstructionType.redoWrong.rawValue:
                self = .redoWrong
            case IGInstructionType.collection.rawValue:
                self = .collection
            case IGInstructionType.myNote.rawValue:
                self = .myNote
            case IGInstructionType.refreshQuestionSet.rawValue:
                self = .refreshQuestionSet
            default:
                self = .unKnown
            }
        }
        
    }
    
    struct IGInstruction {
        
        let type: IGInstructionType
        
        let components: [String]
        
        var path: String {
            return self.components.isEmpty ? "" : ([""] + self.components).joined(separator: "/")
        }
        
        let queryItems: [String: String]
        
        var bridge: IGBridge?
        
        init(type: IGInstructionType,  components: [String] = [], queryItems: [String: String] = [:], bridge: IGBridge? = nil ) {
            self.type = type
            self.components = components
            self.queryItems = queryItems
            self.bridge = bridge
        }
    }
    
    extension IGInstruction {
        
        // Options 下面会提到
        init?(url: URL?, options: IGURLBridgeOptionsInfo = [.iGameApp]) {
            
            if let bridge = options.iGameAppUrlBridge, let ins = bridge.bridgeToIG(from: url) {
                self = ins
                return
            }
            
            if let bridge = options.httpWebUrlBridge, let ins = bridge.bridgeToIG(from: url) {
                self = ins
                return
            }
            
            if let bridge = options.universalUrlBridge, let ins = bridge.bridgeToIG(from: url) {
                self = ins
                return
            }
            
            return nil
        }
    }
    
    extension IGInstruction {
        
        var url: URL? {
            return self.bridge?.bridgeToURL(from: self)
        }
    }
    
    

    2.3 Options 可选值

    import Foundation
    
    typealias IGURLBridgeOptionsInfo = [IGURLBridgeInfoItem]
    
    enum IGURLBridgeInfoItem {
        
        case iGameApp
        
        case httpWeb
        
        case universalLink
    }
    
    precedencegroup ItemComparisonPrecedence {
        associativity: none
        higherThan: LogicalConjunctionPrecedence
    }
    
    infix operator <== : ItemComparisonPrecedence
    
    func <== (lhs: IGURLBridgeInfoItem, rhs: IGURLBridgeInfoItem) -> Bool {
        
        switch (lhs, rhs) {
        case (.iGameApp, .iGameApp): return true
        case (.httpWeb, .httpWeb): return true
        case (.universalLink, .universalLink): return true
        default: return false
        }
    }
    
    extension Collection where Iterator.Element == IGURLBridgeInfoItem {
        
        func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? {
            return reversed().first { $0 <== target }
        }
        
        func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] {
            return filter { !($0 <== target) }
        }
    }
    
    extension Collection where Iterator.Element == IGURLBridgeInfoItem {
        
        var iGameAppUrlBridge: IGameAppURLBridge? {
            if let item = lastMatchIgnoringAssociatedValue(.iGameApp),
                case .iGameApp = item
            {
                return IGameAppURLBridge.default
            }
            return nil
        }
        
        var httpWebUrlBridge: HttpWebURLBridge? {
            if let item = lastMatchIgnoringAssociatedValue(.httpWeb),
                case .httpWeb = item
            {
                return HttpWebURLBridge.default
            }
            return nil
        }
        
        var universalUrlBridge: UniversalLinURLBridge? {
            if let item = lastMatchIgnoringAssociatedValue(.universalLink),
                case .universalLink = item
            {
                return UniversalLinURLBridge.default
            }
            return nil
        }
    }
    
    

    AppDelegate实现

    iOS AppDelegate 默认是一个全局的单例,因此可以将其设置为处理的入口;

        
        @objc static let current: AppDelegate = UIApplication.shared.delegate as! AppDelegate
        
        @discardableResult
        func handleIGURL(_ url: URL, httpConvertible: Bool = true) -> Bool {
            
            guard let instruction = IGInstruction(url: url, options: httpConvertible ? [.iGameApp, .httpWeb] : [.iGameApp]) else { return false }
            
            return handleIGInstruction(instruction)
        }
        
        func handleIGInstruction(_ ins: IGInstruction) -> Bool {
            
            switch ins.type {
            case .specialExercise, .examinationPaper, .redoWrong, .collection, .myNote, .refreshQuestionSet:
                print("handler --- (ins.type.rawValue) 类型数据")
            default:
                break
            }
            return true
            
        }
    }
    
  • 相关阅读:
    史上最刁钻的十道英语面试题
    99%的人连Where are you from都不会回答?
    库存管理与订单的控制
    订单处理逻辑
    配送规划
    多商家电子商务解决方案
    电商库存规划
    库存管理从入门到精通
    商家报名系统
    经销商管理
  • 原文地址:https://www.cnblogs.com/gaox97329498/p/11069433.html
Copyright © 2011-2022 走看看