Mirror.handleChildren()
recursive reflections: inspect/manipulate on children's children.
💾 程式: replit
// *: born, +: new, /: update
// ─────────────────────────────────────
// 2022.01.27 * (v.1) + handleChildren
// 2022.01.28 / handleChildren: global func -> Mirror method
extension Mirror {
/// ⭐ handle children of type T
public static func handleChildren<T>( // T: child's type ⭐
of subject : Any, // parent (⭐ of type `Any`)
type : T.Type, // child type
recursive : Bool = false, // first level only or all levels of children
with handler: (T)->Void) // child handler
{
// ⭐ mirror of `subject`
let mirror = Mirror(reflecting: subject)
// ⭐ do somethingfor each child.value of type `T`
// ⭐ for case let ... where
// ----------------------
// (pattern-match tuples + data binding + condition)
// ↱ ⭐ child.label ignored ╭── ⭐ where ───╮
for case let (_, value) in mirror.children where value is T {
// handle T value
handler(value as! T) // ⭐ force type casting (Any -> T)
// ⭐ handle children's T value recursively
if recursive {
handleChildren(of: value, type: T.self, recursive: true, with: handler)
}
}
}
}
⬆️ 需要: Logger
// supporting types (Credentials, ...) not listed here.
struct Session {
let credentials = Credentials()
let favorites = Favorites()
let settings = Settings()
}
extension Session {
// 1. ⭐ reset everything one by one
func logOut1() {
Logger.box("# 1: manual reset")
credentials.reset()
favorites.reset()
settings.reset()
}
// 2. ⭐ use `Mirror` to reset every `Resettable` (first level) child
func logOut2() {
Logger.box("# 2: use 'Mirror' directly")
// 2.1: mirror self
let mirror = Mirror(reflecting: self)
// 2.2: inspect children
// ⭐ `for case let ... where` (pattern matching + binding + condition)
// ---------------------------------------------------------------------
// ↱ ⭐ child.label ignored ╭─── ⭐ where clause ────╮
for case let (_, value) in mirror.children where value is Resettable {
// ⭐ 2.2.1: `if let ... as?` (optional binding)
// ----------------------------------------------
// if let resettable = child.value as? Resettable {
// resettable.reset()
// }
// ⭐ 2.2.2: `(... as? ...)?.method()` (optional chaining)
// -------------------------------------------------------
// for child in mirror.children {
// (child.value as? Resettable)?.reset()
// }
// ⭐ 2.2.3: `as!` force type casting (from Any to Resettable)
// -----------------------------------------------------------
// doing this is safe because `value` IS `Resettable`!
(value as! Resettable).reset()
}
}
// 3. ⭐ wrap "method 2" in `Mirror.handleChildren` meth
func logOut3(){
Logger.box("# 3: Mirror.handleChildren() (level 1)")
// ⭐ use `handleChildren` on level 1 children
Mirror.handleChildren(of: self, type: Resettable.self) { child in
child.reset()
}
}
// 4. ⭐ use `handleChildren` recursively
func logOut4(){
Logger.box("# 4: Mirror.handleChildren() (recursive)")
Mirror.handleChildren(of: self, type: Resettable.self, recursive: true) { child in
child.reset()
}
Logger.topline("⭐ this method has recursive mode.")
}
}
執行結果:
// ┌─────────────────────────┐
// │ # 1: manual reset │
// └─────────────────────────┘
// 🔸 resetting `Credentials` ...
// 🔸 resetting `Favorites` ...
// 🔸 resetting `Settings` ...
// ┌──────────────────────────────────┐
// │ # 2: use 'Mirror' directly │
// └──────────────────────────────────┘
// 🔸 resetting `Credentials` ...
// 🔸 resetting `Favorites` ...
// 🔸 resetting `Settings` ...
// ┌──────────────────────────────────────────────┐
// │ # 3: Mirror.handleChildren() (level 1) │
// └──────────────────────────────────────────────┘
// 🔸 resetting `Credentials` ...
// 🔸 resetting `Favorites` ...
// 🔸 resetting `Settings` ...
// ┌────────────────────────────────────────────────┐
// │ # 4: Mirror.handleChildren() (recursive) │
// └────────────────────────────────────────────────┘
// 🔸 resetting `Credentials` ...
// 🔸 resetting `Favorites` ...
// 🔸 resetting `Settings` ...
// 🔸 resetting `PreferredLanguages` ...
// ─────────────────────────────────
// ⭐ this method has recursive mode.\
Pattern Matching - pattern-match on mirror's children.
for case let ... where - (pattern matching + binding + condition)
if let ... as? - optional binding.
(... as? ...)?.method() - optional chaining.
... as! ... - force type casting.
Logger is used in [💈範例].
History
(2022.01.27) - first version. (global function)
這個程式碼應該要寫成 global function,如果寫成 protocol method 會出現許多問題 (例如:children of children 可能根本不遵循這個 protocol,導致無法使用此 method recursively)
👉 💾 程式: attemp 1 (with lots of errors)
/// ⭐ handle children of type T
func handleChildren<T>( // T: child's type ⭐
of subject : Any, // parent (⭐ of type `Any`)
type : T.Type, // child type
recursive : Bool = false, // first level only or all levels of children
with handler: (T)->Void) // child handler
{
// ⭐ mirror of `subject`
let mirror = Mirror(reflecting: subject)
// ⭐ do somethingfor each child.value of type `T`
// ⭐ for case let ... where
// ----------------------
// (pattern-match tuples + data binding + condition)
// ↱ ⭐ child.label ignored ╭── ⭐ where ───╮
for case let (_, value) in mirror.children where value is T {
// handle T value
handler(value as! T) // ⭐ force type casting (Any -> T)
// ⭐ handle children's T value recursively
if recursive {
handleChildren(of: value, type: T.self, recursive: true, with: handler)
}
}
}
Last updated
Was this helpful?