仓库已开源:NoLogMacro。
Swift Macro
Swift Macro 是 2023 WWDC 推出的。对于Swift Macro的介绍文章不少,但是大部分都是官方视频的翻译,例子和一些问题介绍处理真的是比较少。本文还真的有可能是第一个实践应用的…
特点
简单了解 WWDC https://developer.apple.com/videos/play/wwdc2023/10166/ 后,应该能得出一下结论:
Swfit Macro 大概分两种,表达式宏(#),和扩展宏(@)。这么一想限制还真多,但是保证了swift macro的安全性,就是说它支持类型判断,泛型等。
Swift Macro 和 OSLog 的 碰撞
Xcode15后,debug控制台提供了非常有用的日志输入,其中包括了可直接定位到代码行的功能。
初见便想到,如果我们自己的日志库能够实现相应的功能,那调试bug不是手到擒来。
要支持的功能很简单:
- 支持日志,转发到自己的日志库处理
- 支持代码行定位
咱就是说,搞个统一的消息转发,然后在使用宏进行OSLog处理,把两个表达式用宏定义,如果验证代码行能正确定位即可。
预验证确实能正确定位到代码行,那么可以开干了。
火花1,仅支持单行表达式
根据WWDC的文档,搞起:
- 项目创建
- 宏定义
- 测试success
- duang,运行起来报错了
这个报错没有有用信息,忽然想到表达式可以用于初始化变量,是不是有可能不支持多个表达式,通过修改宏,始终不能正确编译。
既然Swift 表达式宏不支持多个表达式,那么就用单行表达式吧。
火花2,不支持闭包
通过使用闭包,这样就能执行方法,然后再闭包里面执行log收归或者调用系统log即可。
方式1:
1
2
3
4
5
Logger()
.sync {
NoLog.noLog("msg", attrs: ["a": 1])
}
.log("msg")
可以正确定位到代码行,但是,NoLog消息中心收到的消息始终是第一个。 方式2:
1
2
3
4
5
NoLog
.sync {
Logger().log("msg")
}
.noLog("msg", attrs: ["a": 1])
不能正确定位到代码行,NoLog消息中心收到的正确。
这下闭包的路堵死了。猜测应该是宏展开,block被视为常量,最终展开时候相当于一个全局的block。
链式语法
条条大路,那么试试链式语法看看:
1
2
3
Logger()
.noLog("msg", attrs: ["a": 1])
.log("msg")
还是这个方法ok,既能够显示到代码行,也能到消息中心打出日志。剩下的就剩下封装宏了。按照文档一步步来即可。
那最后,简单看下 OC define 和 Swfit Macro 的区别:
· | OC define | Swfit Macro |
---|---|---|
代码编辑器类型检测 | N | Y |
定义单个表达式 | Y | Y |
定义多个表达式 | Y | N |
定义各种结构的扩展 | Y | Y |
也就是说,Swift Macro 在保证类型安全的情况下,支持单个表达式和数据结构的宏。
开源库
Not Only Log,(灵感来自nosql),比较贴切的有木有。
先看效果,常规方法的log无法定位到正确的代码行。通过本库提供的宏方法可以完美的以宏的方式支持,非常轻量级。
使用
库已开源。使用相当简单,但是考虑到很多小伙伴没使用过 swift package,特别录制了一些步骤:
- 右键添加 Package,通过链接
https://github.com/poos/NoLogMacro
添加,同时选择 Lib 和 Client import OSLog
和import NoLogMacro
,初始化NoLogger.callback
(可选)- 使用
#noLog()
代替Logger().log()
gif:
Code
具体代码,一共就几行,起飞:
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
//import
import OSLog
import NoLogMacro
// set once
NoLogger.callback = { (type: OSLogType, message: String, attrs: Dictionary<String, Any>?) in
print("simple type: \(type) message: \(message) dic: \(String(describing: attrs))")
}
// all log can send to `NoLogger.callback`
// **can be located to a line**
// same with `Logger().log(level: .default, "default")`
#noLog("message")
// and more custom info
#noLogError("error", attrs: ["code": 404])
// others
Logger().log(level: .info, "info")
#noLogInfo("info")
#noLogInfo("info", attrs: ["a": 2])
Logger().log(level: .debug, "debug")
#noLogDebug("debug")
#noLogDebug("debug", attrs: ["a": 3])
Logger().log(level: .error, "error")
#noLogError("error")
#noLogError("error", attrs: ["a": 4])
Logger().log(level: .fault, "fault")
#noLogFault("fault")
#noLogFault("fault", attrs: ["a": 5])
TODO List
当然还有几个TODO项,just waiting 一下子。
- support
Logger(subsystem: <#T##String#>, category: <#T##String#>)
- brew iOS 14, using print support
- using Swift Macro writing this lib