Skip to content

Instantly share code, notes, and snippets.

@mzaks
Created July 2, 2017 14:00
Show Gist options
  • Save mzaks/c1428efcbbbb0c0c430537715afea8c6 to your computer and use it in GitHub Desktop.
Save mzaks/c1428efcbbbb0c0c430537715afea8c6 to your computer and use it in GitHub Desktop.
EntitasKit Options Sample
//
// AppDelegate.swift
// OptionsSampleApp
//
// Created by Maxim Zaks on 02.07.17.
// Copyright © 2017 Maxim Zaks. All rights reserved.
//
import UIKit
import EntitasKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var optionsLoop: ReactiveLoop?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
setupOptionsContext()
optionsLoop = ReactiveLoop(ctx: optionsCtx, systems: [
PersistOptionsSystem(),
SwitchChildrenOfOptionASystem(),
UpdateOptionsDisplaySystem(),
SwitchParentOfOptionASystem()
])
return true
}
func applicationWillResignActive(_ application: UIApplication) {
}
func applicationDidEnterBackground(_ application: UIApplication) {
}
func applicationWillEnterForeground(_ application: UIApplication) {
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
}
}
//
// OptionsContext.swift
// OptionsSampleApp
//
// Created by Maxim Zaks on 02.07.17.
// Copyright © 2017 Maxim Zaks. All rights reserved.
//
import Foundation
import EntitasKit
let optionsCtx = Context()
struct OptionsBoolValueComponent: Component {
let value: Bool
}
struct TitleComponent: Component {
let value: String
}
struct DescriptionComponent: Component {
let value: String
}
struct IndexComponent: Component {
let value: Int
}
enum OptionsKey: String {
case OptionA, OptionB, OptionA1, OptionA2
}
struct OptionsKeyComponent: Component {
let value: OptionsKey
}
struct OptionsConfigData {
let key: OptionsKey
let index: Int
let title: String
let description: String
let defaultValue: Bool
}
let optionsConfig = [
OptionsConfigData(key: .OptionA,
index: 0,
title: "Enable A",
description: "By enabling A we will get ...",
defaultValue: true
),
OptionsConfigData(key: .OptionB,
index: 3,
title: "Enable B",
description: "By enbaling B we will get ...",
defaultValue: false
),
OptionsConfigData(key: .OptionA1,
index: 1,
title: "Enable A1",
description: "By enabling A1 we will get ...",
defaultValue: true
),
OptionsConfigData(key: .OptionA2,
index: 2,
title: "Enable A2",
description: "By enbaling A2 we will get ...",
defaultValue: true
)
]
func setupOptionsContext() {
for option in optionsConfig {
let e = optionsCtx.createEntity()
.set(OptionsKeyComponent(value: option.key))
.set(IndexComponent(value: option.index))
.set(TitleComponent(value: option.title))
.set(DescriptionComponent(value: option.description))
let value = (UserDefaults.standard.object(forKey: option.key.rawValue) as? Bool) ?? option.defaultValue
e.set(OptionsBoolValueComponent(value: value))
}
}
let optionsKeyIndex = optionsCtx.index { (comp: OptionsKeyComponent) -> OptionsKey in
return comp.value
}
struct OptionsDisplayComponent: UniqueComponent {
weak var ref: ViewController?
}
//
// OptionsLoop.swift
// OptionsSampleApp
//
// Created by Maxim Zaks on 02.07.17.
// Copyright © 2017 Maxim Zaks. All rights reserved.
//
import Foundation
import EntitasKit
class PersistOptionsSystem: ReactiveSystem {
let collector: Collector = Collector(
group: optionsCtx.group(
Matcher(
all: [OptionsBoolValueComponent.cid, OptionsKeyComponent.cid]
)
),
type: .updated
)
let name = "Options persisting"
func execute(entities: Set<Entity>) {
for e in entities {
guard let boolComponent: OptionsBoolValueComponent = e.get(),
let keyComponent: OptionsKeyComponent = e.get() else {
continue
}
UserDefaults.standard.set(boolComponent.value, forKey: keyComponent.value.rawValue)
}
}
}
class SwitchChildrenOfOptionASystem: ReactiveSystem {
let collector: Collector = Collector(
group: optionsCtx.group(
Matcher(
all: [OptionsBoolValueComponent.cid, OptionsKeyComponent.cid]
)
),
type: .updated
)
let name = "Switching off A1 and A2 when A got switched off"
func execute(entities: Set<Entity>) {
guard
let optionA = entities.first(where: { (e) -> Bool in
return e.get(OptionsKeyComponent.self)?.value == .OptionA
}),
let aIsOn = optionA.get(OptionsBoolValueComponent.self)?.value,
let optionA1 = optionsKeyIndex[.OptionA1].first,
let optionA2 = optionsKeyIndex[.OptionA2].first
else {
return
}
if aIsOn == false {
if optionA1.get(OptionsBoolValueComponent.self)?.value != false {
optionA1.set(OptionsBoolValueComponent(value: false))
}
if optionA2.get(OptionsBoolValueComponent.self)?.value != false {
optionA2.set(OptionsBoolValueComponent(value: false))
}
}
}
}
class SwitchParentOfOptionASystem: ReactiveSystem {
let collector: Collector = Collector(
group: optionsCtx.group(
Matcher(
all: [OptionsBoolValueComponent.cid, OptionsKeyComponent.cid]
)
),
type: .updated
)
let name = "Switching on A when A1 or A2 got switched on"
func execute(entities: Set<Entity>) {
for e in entities {
guard e.get(OptionsKeyComponent.self)?.value == .OptionA1 || e.get(OptionsKeyComponent.self)?.value == .OptionA2 else {
continue
}
guard let isON = e.get(OptionsBoolValueComponent.self)?.value,
let optionA = optionsKeyIndex[.OptionA].first
else {
continue
}
if isON && optionA.get(OptionsBoolValueComponent.self)?.value != true {
optionA.set(OptionsBoolValueComponent(value: true))
}
}
}
}
class UpdateOptionsDisplaySystem: ReactiveSystem {
let collector: Collector = Collector(
group: optionsCtx.group(
Matcher(
all: [OptionsBoolValueComponent.cid, IndexComponent.cid]
)
),
type: .updated
)
let name = "Update options cells on value change"
func execute(entities: Set<Entity>) {
for e in entities {
guard let index = e.get(IndexComponent.self)?.value else {
continue
}
optionsCtx.uniqueComponent(OptionsDisplayComponent.self)?.ref?.updateCell(at: index)
}
}
}
import UIKit
import EntitasKit
class ViewController: UITableViewController {
var items = optionsCtx.group(Matcher(all:[OptionsKeyComponent.cid, IndexComponent.cid]))
override func viewDidLoad() {
super.viewDidLoad()
optionsCtx.setUniqueComponent(OptionsDisplayComponent(ref: self))
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BoolOptionCell") as? BoolOptionCell else {
return UITableViewCell()
}
cell.setEntity(e: getItem(indexPath: indexPath))
return cell
}
private func getItem(indexPath: IndexPath) -> Entity{
return items.sorted(forObject: ObjectIdentifier(self), by: { (e1, e2) -> (Bool) in
let i1 = e1.get(IndexComponent.self)?.value ?? 0
let i2 = e2.get(IndexComponent.self)?.value ?? 0
return i1 < i2
})[indexPath.row]
}
func updateCell(at: Int) {
let indexPath = IndexPath(row: at, section: 0)
guard let cell = tableView.cellForRow(at: indexPath) as? BoolOptionCell else {
return
}
cell.setEntity(e: getItem(indexPath: indexPath))
}
}
class BoolOptionCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var optionsSwitch: UISwitch!
weak var entity: Entity?
@IBAction func optionSwitched(_ sender: UISwitch) {
entity?.set(OptionsBoolValueComponent(value: sender.isOn))
}
func setEntity(e : Entity){
self.entity = e
titleLabel.text = e.get(TitleComponent.self)?.value
descriptionLabel.text = e.get(DescriptionComponent.self)?.value
if let boolComponent: OptionsBoolValueComponent = e.get() {
optionsSwitch.setOn(boolComponent.value, animated: true)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment