项目作者: feilfeilundfeil

项目描述 :
Declarative, state-driven UI framework
高级语言: Swift
项目地址: git://github.com/feilfeilundfeil/Cyanic.git
创建时间: 2019-03-30T19:14:27Z
项目社区:https://github.com/feilfeilundfeil/Cyanic

开源协议:MIT License

下载


Cyanic

Cyanic is an iOS framework created at Feil, Feil, & Feil GmbH in response to a need for state-driven UI. It borrows heavily
from the concepts of Airbnb’s MvRx framework (which our Android developers use) to create a very similar
code base with Android thereby unifying the business logic in both platforms. We use this framework to create complex, performant, and reactive
screens in our projects.

Cyanic is a Swift only framework. There are no plans to make it compatible with Objective-C.

Installation

CocoaPods

Requirements:

  • Swift 5.0+
  • iOS 10.0+
  1. Add the following to your Podfile:

    1. pod 'Cyanic'
    2. pod 'LayoutKit', :git => 'https://github.com/hooliooo/LayoutKit.git' // Use this fork until LayoutKit is updated
  2. Integrate your dependencies using frameworks: add use_frameworks! to your Podfile.

  3. Run pod install.

Why we use a forked version of LayoutKit

LayoutKit is the library that is responsible for most of the UI logic in Cyanic. However, as of April 17, 2019, there are some limitations to the current LayoutKit version in Cocoapods:

  1. It is not updated to use Swift 5
  2. Cyanic needs access to an internal initializer of the Layouts that allows you to declare the UIView subclass type as an argument.

Without these changes, Cyanic will continue to use the forked version.

Documentation

Check out our wiki for full documentation.

A Simple Example

A very simple example with expandable functionality:

  1. struct YourState: ExpandableState {
  2. enum Section: String, CaseIterable {
  3. case first
  4. case second
  5. }
  6. static var `default`: YourState {
  7. return YourState(
  8. text: "Hello, World!",
  9. expandableDict: YourState.Section.allCases.map { $0.rawValue }
  10. .reduce(into: [String: Bool](), { (current: inout [String: Bool], id: String) -> Void in
  11. current[id] = false
  12. }
  13. )
  14. }
  15. var text: String
  16. var expandableDict: [String: Bool]
  17. }
  18. class YourViewModel: ViewModel<YourState> {
  19. func showCyanic() {
  20. self.setState { $0.text = "Hello, Cyanic!" }
  21. }
  22. }
  23. class YourComponentViewController: SingleSectionCollectionComponentViewController {
  24. private let viewModel: YourViewModel = YourViewModel(initialState: YourState.default)
  25. override var viewModels: [AnyViewModel] {
  26. return [self.viewModel.asAnyViewModel]
  27. }
  28. override func buildComponents(_ componentsController: inout ComponentsController) {
  29. withState(self.viewModel) { (state: YourState) -> Void in
  30. componentsController.staticTextComponent {
  31. $0.id = "title"
  32. $0.text = state.text
  33. }
  34. componentsController.buttonComponent {
  35. $0.id = "button"
  36. $0.onTap = { [weak self]
  37. self?.viewModel.showCyanic()
  38. }
  39. }
  40. let firstExpandableID: String = YourState.Section.first.rawValue
  41. let yourExpandable = components.expandableComponent { [weak self] in
  42. guard let s = self else { return }
  43. $0.id = firstExpandableID
  44. $0.contentLayout = LabelContentLayout(text: Text.unattributed("Hello, World!"))
  45. $0.isExpanded = state.expandableDict[firstExpandableID] ?? false
  46. $0.setExpandableState = self.viewModel.setExpandableState
  47. $0.backgroundColor = UIColor.lightGray
  48. $0.height = 55.0
  49. }
  50. // These ButtonComponents will only show up when yourExpandable is expanded.
  51. if yourExpandable.isExpanded {
  52. for number in 1...5 {
  53. componentsController.buttonComponent {
  54. $0.id = "button\(number)"
  55. $0.title = "\(number)"
  56. $0.onTap = { [weak self]
  57. print("Hello, World from Button \(number)")
  58. }
  59. }
  60. }
  61. }
  62. }
  63. }
  64. }

Contributors