Popularity
8.3
Growing
Activity
5.3
-
2,056
38
100

Code Quality Rank: L5
Monthly Downloads: 0
Programming language: Swift
License: MIT License
Latest version: v7.0.3

Render alternatives and similar libraries

Based on the "Reactive Programming" category

Do you think we are missing an alternative of Render or a related project?

Add another 'Reactive Programming' Library

README

Render [Swift](#) [ObjC++](#) License

CoreRender is a SwiftUI inspired API for UIKit (that is compatible with iOS 10+ and ObjC).

Introduction

  • Declarative: CoreRender uses a declarative API to define UI components. You simply describe the layout for your UI based on a set of inputs and the framework takes care of the rest (diff and reconciliation from virtual view hierarchy to the actual one under the hood).
  • Flexbox layout: CoreRender includes the robust and battle-tested Facebook's Yoga as default layout engine.
  • Fine-grained recycling: Any component such as a text or image can be recycled and reused anywhere in the UI.

TL;DR

Let's build the classic Counter-Example.

The DSL to define the vdom representation is similiar to SwiftUI.

func makeCounterBodyFragment(context: Context, coordinator: CounterCoordinator) -> OpaqueNodeBuilder {
  Component<CounterCoordinator>(context: context) { context, coordinator in
    VStackNode {
      LabelNode(text: "\(coordinator.count)")
        .textColor(.darkText)
        .background(.secondarySystemBackground)
        .width(Const.size + 8 * CGFloat(coordinator.count))
        .height(Const.size)
        .margin(Const.margin)
        .cornerRadius(Const.cornerRadius)
      HStackNode {
        ButtonNode()
          .text("TAP HERE TO INCREASE COUNT")
          .setTarget(coordinator, action: #selector(CounterCoordinator.increase), for: .touchUpInside)
          .background(.systemTeal)
          .padding(Const.margin * 2)
          .cornerRadius(Const.cornerRadius)
      }
    }
    .alignItems(.center)
    .matchHostingViewWidth(withMargin: 0)
  }
}

Label and Button are just specialized versions of the Node<V: UIView> pure function. That means you could wrap any UIView subclass in a vdom node. e.g.


Node(UIScrollView.self) {
  Node(UILabel.self).withLayoutSpec { spec in 
    // This is where you can have all sort of custom view configuration.
  }
  Node(UISwitch.self)
}

The withLayoutSpec modifier allows to specify a custom configuration closure for your view.

Coordinators are the only non-transient objects in CoreRender. They yeld the view internal state and they are able to manually access to the concrete view hierarchy (if one desires to do so).

By calling setNeedsReconcile the vdom is being recomputed and reconciled against the concrete view hiearchy.

class CounterCoordinator: Coordinator{
  var count: UInt = 0

  func incrementCounter() {
    self.count += 1                      // Update the state.
    setNeedsReconcile()                  // Trigger the reconciliation algorithm on the view hiearchy associated to this coordinator.
  }
}

Finally, Components are yet again transient value types that bind together a body fragment with a given coordinator.

class CounterViewCoordinator: UIViewController {
  var hostingView: HostingView!
  let context = Context()

  override func loadView() {
    hostingView = HostingView(context: context, with: [.useSafeAreaInsets]) { context in
      makeCounterBodyFragment(context: context, coordinator: coordinator)
    }
    self.view = hostingView
  }

  override func viewDidLayoutSubviews() {
    hostingView.setNeedsLayout()
  }
}

Components can be nested in the node hierarchy.


func makeFragment(context: Context) {
  Component<FooCoordinator>(context: context) { context, coordinator in
    VStackNode {
      LabelNode(text: "Foo")
      Component<BarCoordinator>(context: context) { context, coordinator in
        HStackNode {
          LabelNode(text: "Bar")
          LabelNode(text: "Baz")
        }
      }
    }
  }
}

Use it with SwiftUI

Render nodes can be nested inside SwiftUI bodies by using CoreRenderBridgeView:


struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello From SwiftUI")
      CoreRenderBridgeView { context in
        VStackNode {
          LabelNode(text: "Hello")
          LabelNode(text: "From")
          LabelNode(text: "CoreRender")
        }
          .alignItems(.center)
          .background(UIColor.systemGroupedBackground)
          .matchHostingViewWidth(withMargin: 0)
      }
      Text("Back to SwiftUI")
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

Credits:

Layout engine:


*Note that all licence references and agreements mentioned in the Render README section above are relevant to that project's source code only.