{"data":{"post":{"title":"Using Functional Binding to Observe in SwiftUI","subtitle":"","isPublished":true,"createdTime":"2022-08-19T00:00:00.000Z","lastModifiedTime":null,"license":null,"tags":["SwiftUI","Binding","Swift","Observer"],"category":"Programming","file":{"childMdx":{"excerpt":"Story This week, my colleague asked me a question: how to observe user selection\nbehaviors on…","code":{"body":"function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\nconst layoutProps = {};\nreturn class MDXContent extends React.Component {\n  constructor(props) {\n    super(props);\n    this.layout = null;\n  }\n\n  render() {\n    const _this$props = this.props,\n          {\n      components\n    } = _this$props,\n          props = _objectWithoutProperties(_this$props, [\"components\"]);\n\n    return React.createElement(MDXTag, {\n      name: \"wrapper\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `Story`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `This week, my colleague asked me a question: how to observe user selection\nbehaviors on SwiftUI's `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Picker`), `?`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `This is a question came from real bussiness. Thus I think it worth to take\nme time to solve it.`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `The example code is as shown below and my colleague wanted to observe user's\nbehaviors on selecting candidates of the `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Picker`), `.`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `import SwiftUI\n\nlet labels = [\"One\", \"Two\", \"Three\", \"Four\"]\n\nstruct ContentView: View {\n  \n  var data = Array(labels.enumerated())\n  \n  @State\n  var selection: Int = 0\n  \n  var body: some View {\n    Picker(\"Picker\", selection: $selection) {\n      ForEach(data, id: \\\\.offset) { (_, label) in\n        Text(label)\n      }\n    }.pickerStyle(.inline)\n  }\n  \n}\n`)), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `Analysis`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `However, the meaning of \"observe\" varies over contexts:`), React.createElement(MDXTag, {\n      name: \"ul\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, `It can mean the time that user gets its finger down to the picker.`), React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, `It can mean the time that user gets its finger up from the picker.`), React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, `It can mean the time that the code changes the value of $selection.`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `Each of these leads to different solutions.`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `Since SwiftUI controls can adopt style modifiers which change the appearance\nand behavior of a control, to achieve the goal that observing the first two\nkinds of user behaviors that I mentioned above may need deep customizations\nover the control itself.`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `But if you just want to observe the time that the code changes the value of\n`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `$selection`), `, you must try functional `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), `.`), React.createElement(MDXTag, {\n      name: \"blockquote\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"blockquote\"\n    }, `\"Wait! There is `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `onChange(of:, perform:)`), ` modifier, why should I use what\nyou called functional `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), `?\"`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `OK. We just have touched the key to my colleague's question: the timing of\n`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `onChange(of:, perform:)`), ` is difficult to predict and control.`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `In my colleague's code, he triggers network request and user behavior\nobservation with `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `onChange(of: perform)`), `. But the callback of network\nrequest always comes about 30ms earlier than the callback of user behavior\nobservation. This phenomenon is caused by SwiftUI's evaluation order. You\ncan control the order by arranging the `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `onChange(of:, perform:)`), ` modifiers\non SwiftUI's `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` hierarchy in a fine-grained order.`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `But we are engineering -- we cannot make the position to put modifiers to be\ncoupled with SwiftUI's `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` evalution order!`), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `Solution`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `To solve this problem, I suggest my colleague to wrap `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `$selection`), ` with a\n`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), ` by initializing with the following initializer:`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `public struct Binding<Value> {\n\n  public init(\n    get: @escaping () -> Value,\n    set: @escaping (Value, Transaction) -> Void\n  )\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `This is what I mentioned functional `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), `.`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `Here is a use case of the initializer by combining the demo code I showned\nat the beginning of the post:`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `struct ContentView: View {\n  \n  // ...\n\n  var selectionBinding: Binding<Int> {\n    Binding(\n      get: {\n        $selection.wrappedValue\n      },\n      set: { (newValue, tnx) in\n        // call \\`.transaction\\` to make use of the incomming\n        // \\`tnx : Transaction\\` object.\n        $selection.transaction(tnx).wrappedValue = newValue\n        observeSelectionChange()\n      }\n    )\n  }\n\n  func observeSelectionChange() {\n    // do what you want to do\n  }\n \n  var body: some View {\n    Picker(\"Picker\", selection: selectionBinding) {\n      ForEach(data, id: \\\\.offset) { (_, label) in\n        Text(label)\n      }\n    }.pickerStyle(.inline)\n  }\n  \n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `In `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `observeSelectionChange`), ` function you can organize your logics on user\nbehavior observation.`), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `Conclusion`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `Here is a couple of reasons why I recommend this way of user behavior\nobservation:`), React.createElement(MDXTag, {\n      name: \"ul\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"li\"\n    }, `SwiftUI is driven by value changes. This means that value changes are\nubiquitous in a running SwiftUI program to offer a lot of observation\npoints.`)), React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"li\"\n    }, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), ` is more powerful than what you thought. It supports projection\nwith key-paths and collection subscripts which enables developers to\nconducts partial value changes to a control or a `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), `. This means that for\nApple shipped SwiftUI controls and well-designed third-party SwiftUI\ncontrols you can observe all value changes conducted with these `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), `s\nby wrapping a functional `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), `.`)), React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"li\"\n    }, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), ` can observe all kinds of changes that can drive SwiftUI to\nupdate `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` contents. In contrast, `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `onChange(of:, perform:)`), ` requires\ndevelopers to observe over `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Equatable`), ` values. But there are types that are\nnot of `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Equatable`), ` but also able to drive SwiftUI to update `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` contents.\nHere is one example: closures.`)), React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"li\"\n    }, `In a functional `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Binding`), ` you can choose whether to observe before or\nafter the value change and control the order of actions that triggerred by\nthe value change.`))));\n  }\n\n}\nMDXContent.isMDXComponent = true;","scope":""},"headings":[{"value":"Story","depth":2},{"value":"Analysis","depth":2},{"value":"Solution","depth":2},{"value":"Conclusion","depth":2}]}}},"earlierPostExcerpt":{"slug":"/post/2022/03/unexplained-swiftui-the-programming-language-nature-of-swiftui-d20e","title":"Unexplained SwiftUI - The Programming Language Nature of SwiftUI","subtitle":"","createdTime":"2022-03-06T00:00:00.000Z","tags":["Unexplained SwiftUI","SwiftUI","Swift"],"category":"Programming","file":{"childMdx":{"excerpt":"Preface Apple introduced SwiftUI at WWDC 2019. Most people may seem SwiftUI as yet another UI framework likes  Flutter ,  React.js  or  Vue.js  which rides on the trend of  declarative   stateless  UI programming. Even though there are some common points between them, SwiftUI is far different from…"}}},"laterPostExcerpt":{"slug":"/post/2023/01/a-study-of-llvm-adt-ilist-iplist-and-simple-ilist-938d","title":"A Study of LLVM ADT: ilist, iplist and simple_ilist","subtitle":"","createdTime":"2023-01-15T00:00:00.000Z","tags":["LLVM","ADT","C++"],"category":"Programming","file":{"childMdx":{"excerpt":"Introduction ilist  is an intrusive double-linked list -- which means that each linked node stores its data and the node pointers in the same structure. There is no such kind of linked-list implemented containers shipped with the C++ standard library. iplist  is a purely intrusive list. Currently…"}}}},"pageContext":{"postId":"e1149583-4058-5dd4-8fc3-25534469b2b2","earlierPostId":"f06c1b0c-88ad-59b4-9a2c-690037a03ade","laterPostId":"644e5f5d-bcdf-5700-9877-34088a7af8e8"}}