美文网首页
iOS-组件化

iOS-组件化

作者: JerrySi | 来源:发表于2019-08-08 10:42 被阅读0次

    在做iOS组件化的时候,我没有找到像ARouter一样兼容性和使用性比较好的框架。经过权衡后,决定使用CTMediator来作为iOS组件化底层结构,再对CTMediator进行扩展来满足我们的要求。

    扩展支持Swift跳转

    Swift和OC在类和方法字符串展示上稍有不同:
    类名:OC直接通过类名就能反射出Class,Swift有一套特殊格式生成
    方法:OC直接通过action name就能反射出action,Swift需要加冒号:

    1. 类名扩展
    open func transformString(targetName: String, isTargetClass: Bool, shouldCacheTarget:Bool = false) -> AnyObject? {
        
        var targetClass: AnyClass? = self.cachedTarget[targetName]
        if targetClass == nil {
            
            // 首先通过OC的方式去反射Class
            targetClass = NSClassFromString(targetName)
            if targetClass == nil {
                // 如果OC的方式反射失败,再使用Swift的方式
                targetClass = NSObject.swiftClassFromString(targetName)
            }
        }
        /***/
    }
    

    通过字符串去反射Class的时候,首先通过OC的方式,如果OC的方式反射失败,再使用Swift的方式。关键点就是swiftClassFromString是如何找回Swift 类名的:

    extension NSObject {
        
        // create a static method to get a swift class for a string name
        @objc public class func swiftStringClassFromString(_ className: String, bundle: Bundle = Bundle.main) -> String? {
            // get the project name
            if  let appName = bundle.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
                // generate the full name of your class (take a look into your "YourProject-swift.h" file)
                let classStringName = "_TtC\(appName.utf16Length)\(appName)\(className.count)\(className)"
                // return the class!
                return classStringName
            }
            return nil
        }
        
        // create a static method to get a swift class for a string name
        public class func swiftClassFromString(_ className: String) -> AnyClass? {
            
            if let classStringName = NSObject.swiftStringClassFromString(className) {
                return NSClassFromString(classStringName)
            }
            return nil
        }
    }
    

    在我们项目中对NSObject进行扩展方法,在swiftStringClassFromString生成Swift类名。特别注意这个方法前面要加上@objc,让其通过OC方式调用。

    1. 方法扩展
      和获取Class的时候类似,先使用OC方式去查看是否能响应,没有响应再使用Swift方式去查看。否则处理异常。
    fileprivate func performSelector(_ isTargetClass: Bool, targetName: String, actionName: String, params: Any, shouldCacheTarget: Bool, shouldReturn: Bool) -> Any? {
        /***/
        var action = NSSelectorFromString(actionString)
        // 通过OC方式去看是否响应该方法
        if target.responds(to:action) {
            let obj = target.perform(action, with:params)
            return shouldReturn ? obj?.takeUnretainedValue() : nil
        } else {
            // 有可能target是Swift对象
            actionString = actionName + ":"
            action = NSSelectorFromString(actionString)
            
            if target.responds(to:action) {
                let obj = target.perform(action, with:params)
                return shouldReturn ? obj?.takeUnretainedValue() : nil
            } else {
                /*无响应请求的地方*/
            }
        }
    }
    

    提供程序接口Provide

    在iOS里面,该功能需要我们自己实现。这里我参考ARouter里面IProvide的实现方法实现了自己的一套提供程序接口。
    在基础Module里面定义Provide方法,在具体Module里面继承该Provide后重写该方法。因为项目主项目肯定都包含这些具体Module的,所以可以在主项目里面初始化这些子类,并把父类里面公共对象指向子类。 以后直接调用父类里面公共对象就可以了。
    talk is cheap, show me the code

    1. 基础Module里面父类
    open class PassportModuleProvider {
        
        public static var shareInstance: PassportModuleProvider?
        
        public init() {
            PassportModuleProvider.shareInstance = self
        }
        
        open func passportUpdatePWD() {
            fatalError("子类实现passportUpdatePWD")
        }
    
        open func passportPrepareVC() -> UIViewController? {
            fatalError("子类实现passportPrepareVC")
        }
    }
    
    1. 具体Module里面实现类
    public class PassportProviderLmpl: PassportModuleProvider {
        open override func passportUpdatePWD() {
           /**/
        }
        open override func passportPrepareVC() -> UIViewController? {
            return UIViewController()
        }
    }
    
    1. 主项目里初始化实现类
    class func initProvider() {
        _ = PassportProviderLmpl()
        /***/
    }
    

    这样在初始化实现类的时候,就会自动把PassportModuleProvider.shareInstance指向PassportProviderLmpl了。然后使用的时候,通过该shareInstance就能在每个Module中调用实现类里面的方法了。

    我一直在想能不能把主项目里初始化实现类这个步骤去除,没有想到特别好的方法。事实上虽然Swift无法像Android一样定义自己的注解,不过OC可以,只不过这里我们项目的原则是尽可能Swift,所以通过OC添加注解,再和Swift混编的方式我们就暂时没有使用。 不过如果你的项目是OC项目,那么第3步完全可以通过注解处理的。

    页面跳转拦截

    针对页面跳转拦截,iOS中我们之前已经使用RxSwift处理了这部分流程,所以这部分暂时还没有放入组件化中。不过这部分后续我会放入组件化中,到时候会更新本文章。


    经过上面方式的处理,我们项目结构变成和Android类似的结构


    image.png image.png

    相关文章

      网友评论

          本文标题:iOS-组件化

          本文链接:https://www.haomeiwen.com/subject/hqbodctx.html