Swift语法熟悉(二)

功能编写

环境管理 ModeSwitch

新建ModeSwitch.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Foundation
/// 开发模式
public enum Mode: String {
/// 开发模式,打印日志,不上报日志
case develope = "[DEVELOPE MODE]"
/// 调试模式,关闭日志打印,上报日志
case prepared = "[PREPARED MODE]"
/// 生产模式,关闭日志打印,上报日志
case production = "[PRODUCTION MODE]"
}
/// 模式控制
public class INSModeSwitch {
/// 默认为调试模式
public static let `default` = INSModeSwitch(.develope)
public var currentMode: Mode {
didSet {
}
}
/// 私有初始化方法
private init(_ mode: Mode) {
self.currentMode = mode
}
}
/// 模式选择器
public let ModeSwitcher = INSModeSwitch.default

目前这是一个极其简单的模式控制,毫无别的作用,以后扩展功能

日志模块-基础日志 Console

第一步我们先完成日志输出,新建Logger.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import Foundation
/// 日志级别
public enum LogLevel: String {
/// 打印所有类型日志
case all = "🛢[ALL]"
/// 仅打印调试日志
case debug = "⚒[DEBUG]"
/// 仅打印警告日志
case warning = "❗️[WARN]"
/// 仅打印信息日志
case information = "📢[INFO]"
}
final public class INSLogger {
/// 默认为输出全部日志
public static let `default` = INSLogger(.all)
public var level: LogLevel {
didSet {
}
}
/// 日志输出
///
/// - parameter lev: 日志级别
/// - parameter content: 日志内容
public func printLog(_ lev: LogLevel, _ details: String, _ items: Any) {
guard level == .all || level == lev, ModeSwitcher.currentMode == .develope else {
return
}
print(lev.rawValue, details, "\n", items)
}
private init(_ level: LogLevel) {
self.level = level
}
}
/// 公开日志打印模块
public let Logger = INSLogger.default
/// 公开便捷调用方法
public func ILog(_ lev: LogLevel, _ items: Any, _ function: StaticString = #function, _ line: Int = #line, _ file: StaticString = #file) {
var details = "[\("\(file)".components(separatedBy: "/").last!): \(function): \(line)]"
Logger.printLog(lev, details, items)
}

这个时候,我们对前2部分进行简单的测试:

1
2
3
4
5
6
7
8
9
ModeSwitcher.currentMode = .develope
print(ModeSwitcher.currentMode)
Logger.level = .warning
ILog(.all, "Hello world! Current Mode is \(Logger.level)")
ILog(.debug, "Hello world! Current Mode is \(Logger.level)")
ILog(.warning, "Hello world! Current Mode is \(Logger.level)")
ILog(.information, "Hello world! Current Mode is \(Logger.level)")

.developer情况下会输出相应的日志,.all为输出所有日志,预发、生产模式下不会输出日志。

1
2
🛢 [WARN] [ViewController.swift: viewDidLoad(): 24]
["2": "3", "4": "5"]

分别为文件,方法,行号,内容。

功能扩展,界面扩展

一些FoundationUIKit的扩展。新建2个文件夹,分别为FoundationExtension,UIKitExtension。在文件夹内添加这些扩展方法。这一块内容暂时省略,接着往下写功能模块。

UISDK这里我依旧支持AsyncDisplayKit,从这个库诞生我就开始一直使用,所以对于我来说,用这个比UIKit顺手:

1
pod "AsyncDisplayKit"

Foundation扩展先添加Keychain的帮助模块

1
pod 'KeychainAccess'
权限服务

权限服务:获得一些系统的权限,新建Permission.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import Foundation
import CoreBluetooth
import AddressBook;
import AVFoundation;
import CoreBluetooth;
import CoreLocation;
import CoreMotion;
import EventKit;
import Photos
/// 当前的状态
public enum kPermissionAccess {
/// 用户拒绝
case denied
/// 用户同意
case granted
/// 系统设置拉黑等错误
case restricted
/// 未知错误
case unknown
/// 设备不支持
case unsupported
/// 开发者没有导入相应的库
case missingFramework
/// 用户没选
case notDetermined
}
public func hasAccessToBluetoothLE() ->kPermissionAccess {
switch CBCentralManager().state {
case .unsupported:
return .unsupported;
break;
case .unauthorized:
return .denied;
break;
default:
return .granted;
break;
}
}
public func hasAccessToCalendar() ->kPermissionAccess {
switch EKEventStore.authorizationStatus(for: .event) {
case .authorized:
return .granted;
break;
case .denied:
return .denied;
break;
case .restricted:
return .restricted;
break;
case .notDetermined:
return .notDetermined;
break;
default:
return .unknown;
break;
}
}
public func hasAccessToContacts() ->kPermissionAccess {
switch (ABAddressBookGetAuthorizationStatus()) {
case .authorized:
return .granted;
break;
case .denied:
return .denied;
break;
case .restricted:
return .restricted;
break;
case .notDetermined:
return .notDetermined;
break;
default:
return .unknown;
break;
}
}
public func hasAccessToLocation() ->kPermissionAccess {
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways:
return .granted;
break;
case .authorizedWhenInUse:
return .granted;
break;
case .denied:
return .denied;
break;
case .restricted:
return .restricted;
break;
case .notDetermined:
return .notDetermined;
break;
default:
return .unknown;
break;
}
}
public func hasAccessToPhotos() ->kPermissionAccess {
switch PHPhotoLibrary.authorizationStatus() {
case .authorized:
return .granted;
break;
case .denied:
return .denied;
break;
case .restricted:
return .restricted;
break;
case .notDetermined:
return .notDetermined;
break;
default:
return .unknown;
break;
}
}
public func hasAccessToReminders() ->kPermissionAccess {
switch EKEventStore.authorizationStatus(for: .reminder) {
case .authorized:
return .granted;
break;
case .denied:
return .denied;
break;
case .restricted:
return .restricted;
break;
case .notDetermined:
return .notDetermined;
break;
default:
return .unknown;
break;
}
}
public typealias requestAccessHandler = (Bool?, Any?) ->()
public func requestAccessToCalendarWithSuccess(_ handler: @escaping requestAccessHandler) {
EKEventStore().requestAccess(to: .event) {
handler($0, $1)
}
}
public func requestAccessToContactsWithSuccess(_ handler: @escaping requestAccessHandler) {
if let addressBook = ABAddressBookCreateWithOptions(nil, nil) {
ABAddressBookRequestAccessWithCompletion(addressBook as ABAddressBook!) {
handler($0, $1)
}
}
}
public func requestAccessToMicrophoneWithSuccess(_ handler: @escaping requestAccessHandler) {
let session = AVAudioSession.sharedInstance()
session.requestRecordPermission {
handler($0, nil)
}
}
public func requestAccessToPhotosWithSuccess(_ handler: @escaping requestAccessHandler) {
PHPhotoLibrary.requestAuthorization {
if $0 == .authorized {
handler(true, nil)
} else {
handler(false, nil)
}
}
}
public func requestAccessToRemindersWithSuccess(_ handler: @escaping requestAccessHandler) {
EKEventStore().requestAccess(to: .reminder) {
handler($0, $1)
}
}
/// 获取位置访问权限
///
/// - parameter whenInUse: 如果为False,则请求总是需要的权限
public func requestAccessToLocationWithSuccess(_ handler: @escaping requestAccessHandler, _ whenInUse: Bool = true) {
let locationManager = CLLocationManager()
if whenInUse, let content = Bundle.main.object(forInfoDictionaryKey: "NSLocationWhenInUseUsageDescription") {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.requestAlwaysAuthorization()
}
handler(true, nil)
}
public func requestAccessToMotionWithSuccess(_ handler: @escaping requestAccessHandler) {
let motionManager = CMMotionActivityManager()
let motionQueue = OperationQueue()
motionManager.startActivityUpdates(to: motionQueue) {_ in
handler(true, nil)
motionManager.stopActivityUpdates()
}
}

可是你们忘记了,我很懒,所以这段代码不需要了,直接:

1
pod 'PermissionScope'

来嗨吧!

组件处理(核心模块)

要解决的问题暂时有:

  1. 如何成为组件(实现协议与方法即可)
  2. 如何启动组件(调用组件必须实现的启动方法)
  3. 组件之间如何数据传输(实现Router模拟HTTP请求数据传递)
  4. 组件之间如何做界面跳转(利用上述Router模拟OpenURL方式调起)
  5. 组件服务如何停止(调用协议实现的方法并从组件管理中心移除注册)

规则1:只有已经注册了的组件才可以成为Router的操作对象,想成为组件则必须有单独类实现组件协议。

规则2:Router仅负责数据传递与获取视图实例(可传值,后者拥有前者的弱引用)

规则3:仅在每个模块的组件中心的代理方法中注册相应的URL供被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import Foundation
/// 数据流
public typealias INSDataFlowHandler = (Any...) ->Any
/// 错误的Scheme
fileprivate let INSDataUnknowedScheme = "INSDataUnknowedScheme_Key"
/// 插件协议
public protocol INSPlugin {
var pluginLoaded: Bool { get }
/// 插件唯一标识
/// - returns: 插件标识,用来获取插件
func pluginScheme() ->String!
// MARK: - Mappings
/// 视图控制器Map
/// - returns: 视图控制器的对应字典
func classMapping() ->Dictionary<String, AnyClass>?
/// 数据流Map
/// - returns: 数据流字典
func dataflowMapping() ->Dictionary<String, INSDataFlowHandler>?
// MARK: - Plugin status
/// 当插件注册成功的时候会调用这个方法
func serviceStarted()
/// 当插件取消注册的时候会调用这个方法
func serviceStopped()
}
extension INSPlugin {
/// 插件加载状态
public var pluginLoaded: Bool {
return (ModuleCenter.getPlugin(for: pluginScheme()) != nil)
}
}
/// 插件基类,成为模块插件管理器则必须继承该类,并实现方法
open class Plugin: INSPlugin {
final public func registePlugin() {
ModuleCenter.registePlugin(self)
}
final public func deRegistePlugin() {
ModuleCenter.deRegistePlugin(self)
}
open func pluginScheme() -> String! {
return nil
}
open func serviceStopped() {}
open func serviceStarted() {}
open func dataflowMapping() ->Dictionary<String, INSDataFlowHandler>? {
return nil
}
open func classMapping() ->Dictionary<String, AnyClass>? {
return nil
}
public init() {
// I'm a plug-in manager 🏵
}
}
/// 插件控制中心
final public class ModuleManager: NSObject {
public static let `default` = ModuleManager()
/// 已经加载的插件
private(set) var loadedPlugins: Dictionary<String, Plugin> = [:]
/// 注册新的插件
@discardableResult
public func registePlugin(_ item: Plugin) ->Bool {
guard let scheme = item.pluginScheme(), loadedPlugins[scheme] == nil else {
return false
}
loadedPlugins[scheme] = item
/// 注册组件
item.serviceStarted()
return true
}
/// 取消注册插件
public func deRegistePlugin(_ item: Plugin) {
guard let scheme = item.pluginScheme() else {
return
}
loadedPlugins.removeValue(forKey: scheme)
/// 移除组件
item.serviceStopped()
}
/// 根据唯一标识获取插件
public func getPlugin(for scheme: String) ->Plugin? {
return loadedPlugins[scheme]
}
/// 数据流
@discardableResult
public func send(to url: String, and param: Any...) ->Any? {
let (scheme, host) = validateURL(url)
guard let pluginItem = getPlugin(for: scheme!), let dataMapping = pluginItem.dataflowMapping() else {
ILog(.warning, "找不到scheme对应的插件或插件没有dataMapping!")
return nil
}
guard let sendHandler = dataMapping[host!] else {
ILog(.warning, "该路径没有对应的Handler")
return nil
}
return sendHandler(param)
}
/// 视图流
@discardableResult
public func `class`(for url: String) ->AnyClass? {
let (scheme, host) = validateURL(url)
guard let pluginItem = getPlugin(for: scheme!), let dataMapping = pluginItem.classMapping() else {
ILog(.warning, "找不到scheme对应的插件或插件没有classMapping!")
return nil
}
return dataMapping[host!]
}
@discardableResult
private func validateURL(_ url:String) ->(String?, String?) {
guard let URLObject = URL(string: url), let scheme = URLObject.scheme else {
ILog(.warning, "输入的url不能转换为URL对象或URL对象没有Scheme!")
return (INSDataUnknowedScheme, "")
}
return (scheme, URLObject.host)
}
private override init() {
// I'm a module manager 🏵
}
}
public let ModuleCenter = ModuleManager.default

接下来,我们实现一个小的插件继承自Plugin并实现一些代理方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import UIKit
class NetworkPlugin: Plugin {
override func serviceStarted() {
ILog(.debug, "Network service started")
}
override func serviceStopped() {
ILog(.debug, "Network service stopped")
}
override func pluginScheme() -> String! {
return "Network"
}
override func dataflowMapping() ->Dictionary<String, INSDataFlowHandler>? {
let mapping: [String: INSDataFlowHandler] = [
"getData": {
ILog(.warning, "receive call \($0)")
return "Custom data"
},
"supportMethod": {
ILog(.warning, "receive call \($0)")
return [
"GET",
"POST",
"PUT",
"DELETE"
]
}
]
return mapping
}
override func classMapping() -> Dictionary<String, AnyClass>? {
let mapping: [String: AnyClass] = [
"vc1": UIViewController.self,
"vc2": NSObject.self
]
return mapping
}
static let `default` = NetworkPlugin()
}

然后在ViewController.swift中测试:

1
2
3
4
5
6
7
8
9
10
11
12
ModeSwitcher.currentMode = .develope
Logger.level = .all
let sharedNetwork = NetworkPlugin.default
sharedNetwork.registePlugin()
ILog(.information, ModuleCenter.send(to: "Network://getData", and: "Hello World"))
ILog(.information, ModuleCenter.send(to: "Network://supportMethod", and: "Hello World", "Hello real world!", ["a": "b"]))
ILog(.information, ModuleCenter.class(for: "Network://vc1"))
ILog(.information, ModuleCenter.class(for: "Network://vc2"))
sharedNetwork.deRegistePlugin()

看到输出为:

说明功能已经基本的完成了。这里并没有将打开界面写入到当前模块,还是感觉直接获取类好一点,所以直接把OpenURL的方式改成了获取类型,然后由使用者自己去创建对象分情况使用,这样自定义的程度也高一点。 如果写为OpenURL的方式,那我们又要分很多种了,push还是present,是否需要导航,考虑到仅获取不使用的情况,要写控制器基类,还有必要统一一个初始化方法供使用。但是这些似乎已经不是一个框架应该做的事情了(仅鉴于我对Swift框架的理解,框架不干涉业务,框架不干涉UI)。

文章篇幅过长并且均为代码,所以本文只写到这里。

下篇文章会开始编写数据模块、网络模块。

评论