Advertisement
Guest User

Untitled

a guest
Jun 16th, 2019
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.28 KB | None | 0 0
  1. //
  2. // ContentView.swift
  3. // TestingMoreSwiftUI
  4. //
  5. // Created by Chris Eidhof on 04.06.19.
  6. // Copyright © 2019 Chris Eidhof. All rights reserved.
  7. //
  8.  
  9. import SwiftUI
  10. import Combine
  11.  
  12. var newListCounter = 1
  13.  
  14. extension Array {
  15. mutating func remove(atOffsets indices: IndexSet) {
  16. for i in indices.reversed() {
  17. remove(at: i)
  18. }
  19. }
  20.  
  21. subscript(safe index: Int) -> Element? {
  22. get {
  23. guard (startIndex..<endIndex).contains(index) else { return nil }
  24. return self[index]
  25. }
  26. set {
  27. guard (startIndex..<endIndex).contains(index) else { return }
  28. if let v = newValue {
  29. self[index] = v
  30. }
  31. }
  32. }
  33. }
  34.  
  35. /// Similar to a `Binding`, but this is also observable/dynamic.
  36. @propertyDelegate
  37. @dynamicMemberLookup
  38. final class Derived<A>: BindableObject {
  39. let didChange = PassthroughSubject<A, Never>()
  40. fileprivate var cancellables: [AnyCancellable] = []
  41. private let get: () -> (A)
  42. private let mutate: ((inout A) -> ()) -> ()
  43. init(get: @escaping () -> A, mutate: @escaping ((inout A) -> ()) -> ()) {
  44. self.get = get
  45. self.mutate = mutate
  46. }
  47. var value: A {
  48. get { get() }
  49. set { mutate { $0 = newValue } }
  50. }
  51. subscript<U>(dynamicMember keyPath: WritableKeyPath<A, U>) -> Derived<U> {
  52. let result = Derived<U>(get: {
  53. let value = self.get()[keyPath: keyPath]
  54. return value
  55. }, mutate: { f in
  56. self.mutate { (a: inout A) in
  57. f(&a[keyPath: keyPath])
  58. }
  59. })
  60. var c: AnyCancellable! = nil
  61. c = AnyCancellable(didChange.sink { [weak result] in
  62. // todo cancel the subscription as well
  63. result?.didChange.send($0[keyPath: keyPath])
  64.  
  65. })
  66. cancellables.append(c)
  67. return result
  68. }
  69.  
  70. var binding: Binding<A> {
  71. return Binding<A>(getValue: { self.value }, setValue: { self.value = $0 })
  72. }
  73.  
  74. deinit {
  75. for c in cancellables {
  76. c.cancel()
  77. }
  78. }
  79. }
  80.  
  81. final class SimpleStore<A>: BindableObject {
  82. let didChange =
  83. PassthroughSubject<A, Never>()
  84. init(_ value: A) { self.value = value }
  85. var value: A {
  86. didSet {
  87. didChange.send(value)
  88. }
  89. }
  90.  
  91. var bindable: Derived<A> {
  92. let result = Derived<A>(get: {
  93. self.value
  94. }, mutate: { f in
  95. f(&self.value)
  96. })
  97. let c = self.didChange.sink { [weak result] value in
  98. result?.didChange.send(value)
  99. }
  100. result.cancellables.append(AnyCancellable(c))
  101. return result
  102. }
  103. }
  104.  
  105.  
  106.  
  107. struct TodoList: Codable, Equatable, Hashable {
  108. var items: [Todo] = []
  109. var name = "Todos"
  110. }
  111. struct Todo: Codable, Equatable, Hashable {
  112. var text: String
  113. var done: Bool = false
  114. }
  115.  
  116. struct MyState: Codable, Equatable, Hashable {
  117. var lists: [TodoList] = [
  118. TodoList(items: [
  119. Todo(text: "Buy Milk"),
  120. Todo(text: "Clean")
  121. ])
  122. ]
  123. }
  124.  
  125. struct ItemRow: View {
  126. @Binding var item: Todo?
  127. var body: some View {
  128. return Button(action: { self.item!.done.toggle() }) {
  129. HStack {
  130. Text(item!.text)
  131. Spacer()
  132. if item!.done {
  133. Image(systemName: "checkmark")
  134. }
  135. }
  136. }
  137. }
  138. }
  139.  
  140. struct ListRow: View {
  141. @ObjectBinding var item: Derived<TodoList>
  142. var body: some View {
  143. NavigationButton(destination: TodoListView(list: item)) {
  144. HStack {
  145. Text(item.value.name)
  146. Spacer()
  147. }
  148. }
  149. }
  150. }
  151.  
  152. struct TodoListView: View {
  153. @ObjectBinding var list: Derived<TodoList>
  154. var body: some View {
  155. List {
  156. ForEach((0..<list.value.items.count)) { index in
  157. ItemRow(item: self.list.items[safe: index].binding)
  158. }.onDelete { indices in
  159. // this crashes...
  160. self.list.value.items.remove(atOffsets: indices)
  161. }
  162. }
  163. .navigationBarTitle(Text("\(list.value.name) - \(list.value.items.count) items"))
  164. .navigationBarItems(leading:
  165. EditButton(),
  166. trailing: Button(action: { self.list.value.items.append(Todo(text: "New Todo")) }) { Image(systemName: "plus.circle")}
  167. )
  168. }
  169. }
  170.  
  171. struct AllListsView: View {
  172. @ObjectBinding var theState: Derived<MyState>
  173. var body: some View {
  174. List {
  175. ForEach(0..<theState.value.lists.count) { (index: Int) in
  176. ListRow(item: self.theState.lists[index])
  177. }
  178. }
  179. .navigationBarTitle(Text("All Lists"))
  180. .navigationBarItems(trailing:
  181. Button(action: {
  182. newListCounter += 1
  183. self.theState.value.lists.append(TodoList(items: [], name: "New List \(newListCounter)"))
  184. }) { Image(systemName: "plus.circle")}
  185. )
  186. }
  187. }
  188.  
  189. struct ContentView : View {
  190. @ObjectBinding var store: Derived<MyState>
  191. var body: some View {
  192. NavigationView {
  193. AllListsView(theState: store)
  194. }
  195. }
  196. }
  197.  
  198. #if DEBUG
  199. struct ContentView_Previews : PreviewProvider {
  200. static var previews: some View {
  201. ContentView(store: SimpleStore(MyState()).bindable)
  202. }
  203. }
  204. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement