文章简书链接

最近一直在写 Swift 代码,闲来总结下使用 Swift 的一些经验。在使用第三方 SDK 的时候,我们为了方便使用,会针对某些特殊场景对 SDK 的方法进行 extension,但是随着各种 extension 的增加,就很难和 SDK 原生方法区别起来,造成后期维护成本增加,比如说:

原生方法

sdk.method()

扩展方法

sdk.extensionMethod()

在调用方的代码中,你是无法区别 method() 和 extensionMethod() 这两个方法是来自原生 SDK 的,还是 extension SDK 的。如果该 SDK 是闭源的,你执行 extensionMethod() 方法出现 bug 时,你的第一反应不是去检查是否是自己写的代码有问题,而是去怀疑是否是 SDK 的 bug,所以有必要对其进行统一管理。

实现

这里以极光 IM JMessage SDK 为例,实现一个 ExJMessage 泛型:

public final class ExJMessage<Base> {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}

为了兼容 SDK 的各种类,这里实现了 ExJMessageCompatible 协议,并通过 extension ExJMessageCompatible 生成一个 ex 标识。

/**
 A type that has ExJMessage extensions.
 */
public protocol ExJMessageCompatible { }

public extension ExJMessageCompatible {
    public static var ex: ExJMessage<Self>.Type {
        get { return ExJMessage.self }
    }
    public var ex: ExJMessage<Self> {
        get { return ExJMessage(self) }
    }
}

SDK 里面的类实现 ExJMessageCompatible 协议时就可以实现 ex 标识:

extension JMSGConversation: ExJMessageCompatible { }
extension JMSGMessage: ExJMessageCompatible { }
extension JMSGOptionalContent: ExJMessageCompatible { }

然后就可以对相应的类进行 extension:

extension ExJMessage where Base: JMSGOptionalContent {
    /**
     default optional content
     */
    static var `default`: JMSGOptionalContent {
        let optionalContent = JMSGOptionalContent()
        #if READ_VERSION
            optionalContent.needReadReceipt = true
        #else
            optionalContent.needReadReceipt = false
        #endif
        return optionalContent
    }
}

extension ExJMessage where Base: JMSGConversation {
    /**
     conversation target type is group
     */
    var isGroup: Bool {
        return base.conversationType == .group
    }
}

使用方法:

// instance mothed or property
conversation.ex.isGroup

// static mothed or property
JMSGOptionalContent.ex.default

这样就可以通过 ex 来调用 extensions 的 mothed 或 property,从而与 SDK 原方法区分开来,并且方便 extensions 的管理。

目录结构大体是这样:

null