iOS-组件化

2019-08-08  本文已影响0人  JerrySi

在做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
上一篇下一篇

猜你喜欢

热点阅读