Swift elegant protocol extension
Call form of imitating view.snp.xxx
First look at the call effect: "123456". yy_Str.MD5ForLower32Bate()
"123456".yy_Str.MD5ForLower32Bate() //Similar: view.snp.xxx
Why do you do this? What are the benefits?
- Reduce coupling: in traditional writing, add a method or attribute directly to the category, for example:
extension String { ///Authentication ID No. func validateidnum() -> Bool { let idRegex = "^(\\d{14}|\\d{17})(\\d|[xX])$" let idTest:NSPredicate = NSPredicate(format: "SELF MATCHES %@", idRegex) return idTest.evaluate(with: self) } }
- Reduce unnecessary conflicts: when another engineer has similar requirements, he also creates a new file and writes the same method, which will lead to conflicts, and then troubleshoot...
- Only one attribute is needed to realize the desired extension function, and the code is simple.
How to achieve
Definition protocol: yprotocollextension.swift
//Protocol extension protocol YExtensionProtocol { associatedtype YExtensionType //Write extension to: UIView, String, UIButton var value: YExtensionType { get } }
- Define a structure to implement the protocol
struct YExtensionKitStructTypeEncodable<T>: YExtensionProtocol { ///Implementation protocol properties var value: T typealias YExtensionType = T //Specify type (pass in generics) ///Construction method init(kit: T) { self.value = kit } }
- We can also specify the type directly
struct YExtensionStringEncodable: YExtensionProtocol { ///Implementation protocol properties var value: String typealias YExtensionType = String //Specified type ///Construction method init(kit: String) { self.value = kit } }
- Extend YExtensionProtocol to realize MD5 encryption in related strings
Note: if the function defined in the protocol is implemented in the protocol itself, it is an optional type. Otherwise, the inheritance protocol must be implemented.
Take String for example: MD5 encryption
import CommonCrypto //MARK:String-MD5 encryption 32-bit //where YExtensionType == String specifies YExtensionType: the type extension YExtensionProtocol where YExtensionType == String { ///MD5 encryption 32-bit lowercase func MD5ForLower32Bate() -> String { let str = value.cString(using: .utf8) let strLen = CUnsignedInt(value.lengthOfBytes(using: .utf8)) let digestLen = Int(CC_MD5_DIGEST_LENGTH) let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen) CC_MD5(str!, strLen, result) let hash = NSMutableString.init() for i in 0..<digestLen { hash.appendFormat("%02x", result[i]) } result.deinitialize(count: digestLen) return String(hash) } ///MD5 encryption 32-bit uppercase func MD5ForUpper32Bate() -> String { let str = value.cString(using: .utf8) let strLen = CUnsignedInt(value.lengthOfBytes(using: .utf8)) let digestLen = Int(CC_MD5_DIGEST_LENGTH) let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen) CC_MD5(str!, strLen, result) let hash = NSMutableString() for i in 0..<digestLen { hash.appendFormat("%02X", result[i]) } result.deinitialize(count: digestLen) return String(hash) } }
- Add extension to String: add attribute
//MARKL: String extension extension String { ///First: directly specify the type of var yy_Str: YExtensionStringEncodable { return YExtensionStringEncodable.init(kit: self) } ///Second: generic passing var yy_kit: YExtensionKitStructTypeEncodable<String> { return YExtensionKitStructTypeEncodable.init(kit: self) } }
- The test results in ViewController are as follows
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. print("123456".yy_kit.MD5ForLower32Bate()) print("123456".yy_kit.MD5ForUpper32Bate()) print("-----------------") print("123456".yy_Str.MD5ForLower32Bate()) print("123456".yy_Str.MD5ForUpper32Bate()) } // The output results are as follows: e10adc3949ba59abbe56e057f20f883e E10ADC3949BA59ABBE56E057F20F883E ----------------- e10adc3949ba59abbe56e057f20f883e E10ADC3949BA59ABBE56E057F20F883E
The complete core code is as follows:
// // YExtensionProtocol.swift // YExtentionDemo // // Created by bruce yao on 2019/7/18. // Copyright © 2019 bruce yao. All rights reserved. // import Foundation import UIKit import CommonCrypto //Protocol extension protocol YExtensionProtocol { associatedtype YExtensionType var value: YExtensionType { get } } ///Extension basis of Struct type struct YExtensionKitStructTypeEncodable<T>: YExtensionProtocol { var value: T typealias YExtensionType = T ///Construction method init(kit: T) { self.value = kit } } struct YExtensionStringEncodable: YExtensionProtocol { ///Implementation protocol properties var value: String typealias YExtensionType = String //Specify type (pass in generics) ///Construction method init(kit: String) { self.value = kit } } /////Extension basis of Class type //final class YExtensionKitClassTypeEncodable<T>: YExtensionProtocol { // / / generic // var value: T // typealias YExtensionType = T // ///Construction method // init(kit: T) { // self.value = kit // } //} //MARK:String-MD5 encryption 32-bit extension YExtensionProtocol where YExtensionType == String { ///MD5 encryption 32-bit lowercase func MD5ForLower32Bate() -> String { let str = value.cString(using: .utf8) let strLen = CUnsignedInt(value.lengthOfBytes(using: .utf8)) let digestLen = Int(CC_MD5_DIGEST_LENGTH) let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen) CC_MD5(str!, strLen, result) let hash = NSMutableString.init() for i in 0..<digestLen { hash.appendFormat("%02x", result[i]) } result.deinitialize(count: digestLen) return String(hash) } ///MD5 encryption 32-bit uppercase func MD5ForUpper32Bate() -> String { let str = value.cString(using: .utf8) let strLen = CUnsignedInt(value.lengthOfBytes(using: .utf8)) let digestLen = Int(CC_MD5_DIGEST_LENGTH) let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen) CC_MD5(str!, strLen, result) let hash = NSMutableString() for i in 0..<digestLen { hash.appendFormat("%02X", result[i]) } result.deinitialize(count: digestLen) return String(hash) } } //MARK: extension about string extension String { var yy_Str: YExtensionStringEncodable { return YExtensionStringEncodable.init(kit: self) } var yy_kit: YExtensionKitStructTypeEncodable<String> { return YExtensionKitStructTypeEncodable.init(kit: self) } }