Convert Figma logo to code with AI

alexdrone logoRender

UIKit a-là SwiftUI.framework [min deployment target iOS10]

2,159
98
2,159
3

Top Related Projects

A React-inspired view framework for iOS.

8,494

Epoxy is an Android library for building complex screens in a RecyclerView

12,844

A data-driven UICollectionView framework for building fast and flexible lists.

LayoutKit is a fast view layout library for iOS, macOS, and tvOS.

Quick Overview

Render is a declarative, functional, and reactive UI framework for building native iOS, iPadOS, and macOS applications. It provides a simple and intuitive API for creating complex user interfaces, with a focus on performance, flexibility, and ease of use.

Pros

  • Declarative Approach: Render uses a declarative approach to UI development, which makes it easier to reason about and maintain complex user interfaces.
  • Functional and Reactive: The framework is built on functional and reactive programming principles, which can lead to more predictable and testable code.
  • Performance: Render is designed to be highly performant, with efficient rendering and update mechanisms.
  • Flexibility: The framework is highly customizable and can be integrated with other libraries and frameworks.

Cons

  • Learning Curve: Developers who are new to functional and reactive programming may find the learning curve for Render to be steeper than traditional imperative UI frameworks.
  • Ecosystem: Render is a relatively new framework, and the ecosystem of third-party libraries and tools may not be as mature as more established UI frameworks.
  • Documentation: While the documentation for Render is generally good, some areas may be less comprehensive or up-to-date than users would like.
  • Adoption: As a newer framework, Render may not have the same level of adoption and community support as more established UI frameworks.

Code Examples

Here are a few examples of how to use Render to build user interfaces:

// Creating a simple button
let button = Button(
    title: "Click me",
    action: {
        print("Button clicked!")
    }
)
// Building a list of items
let items = [
    "Item 1",
    "Item 2",
    "Item 3"
]

let list = List(items) { item in
    Text(item)
}
// Handling state and updates
struct CounterState: State {
    var count: Int = 0
}

let counter = Counter(CounterState()) { state, dispatch in
    VStack {
        Text("Count: \(state.count)")
        Button(
            title: "Increment",
            action: {
                dispatch(CounterState(count: state.count + 1))
            }
        )
    }
}

Getting Started

To get started with Render, you can follow these steps:

  1. Install the Render framework using your preferred package manager (e.g., CocoaPods, Carthage, or Swift Package Manager).
  2. Create a new Xcode project and import the Render framework.
  3. Define your application's state and create a root component that represents the main user interface.
  4. Use Render's declarative API to build your UI, handling user interactions and updating the state as needed.
  5. Customize the appearance and behavior of your UI components by leveraging Render's flexible and extensible design.
  6. Test and debug your application using Render's built-in debugging tools and integration with other testing frameworks.

For more detailed instructions and documentation, please refer to the Render GitHub repository.

Competitor Comparisons

A React-inspired view framework for iOS.

Pros of ComponentKit

  • Backed by Facebook, ensuring long-term support and active development
  • Extensive documentation and community resources
  • Optimized for performance with large-scale apps

Cons of ComponentKit

  • Steeper learning curve due to its complexity
  • Limited to Objective-C, which may not be ideal for newer iOS projects
  • Requires more boilerplate code compared to Render

Code Comparison

Render:

class MyComponent: Component {
    @State var count = 0
    
    func render() -> View {
        Button("Count: \(count)") {
            count += 1
        }
    }
}

ComponentKit:

@implementation MyComponent
+ (instancetype)newWithCount:(NSInteger)count {
  return [super newWithComponent:
    [CKButtonComponent
     newWithAction:@selector(incrementCount)
     title:[NSString stringWithFormat:@"Count: %ld", (long)count]]];
}
- (void)incrementCount {
  [self updateState:^(MyComponentState *state) {
    state.count++;
  } mode:CKUpdateModeSynchronous];
}
@end

Both frameworks aim to simplify UI development, but Render offers a more modern, Swift-based approach with less boilerplate. ComponentKit provides robust performance optimizations but requires more setup and is limited to Objective-C. Render's syntax is more concise and intuitive for Swift developers, while ComponentKit offers a more structured approach that may be beneficial for larger teams and complex applications.

8,494

Epoxy is an Android library for building complex screens in a RecyclerView

Pros of Epoxy

  • More mature and widely adopted, with extensive documentation and community support
  • Built-in support for diffing and efficient updates, optimizing performance
  • Comprehensive set of features, including data binding and view holders

Cons of Epoxy

  • Steeper learning curve due to its extensive feature set
  • More verbose implementation compared to Render's concise syntax
  • Primarily focused on Android development, limiting cross-platform use

Code Comparison

Epoxy:

class HeaderModel(
  @EpoxyAttribute var title: String
) : EpoxyModelWithHolder<HeaderHolder>() {
  override fun bind(holder: HeaderHolder) {
    holder.titleView.text = title
  }
}

Render:

class HeaderComponent: Component {
  var title: String
  func render() -> Node {
    Label(title)
  }
}

Summary

Epoxy is a robust, feature-rich library for building complex RecyclerViews in Android, offering excellent performance and a wide range of capabilities. Render, on the other hand, provides a more lightweight and concise approach to building user interfaces, with a focus on simplicity and ease of use. While Epoxy excels in Android-specific development, Render offers a more flexible, cross-platform solution. The choice between the two depends on the specific project requirements, target platform, and developer preferences.

12,844

A data-driven UICollectionView framework for building fast and flexible lists.

Pros of IGListKit

  • Robust and battle-tested in production by Instagram
  • Extensive documentation and community support
  • Optimized for performance with large datasets

Cons of IGListKit

  • Steeper learning curve due to its complexity
  • Primarily focused on UICollectionView, less flexible for other UI components
  • Requires more boilerplate code for simple use cases

Code Comparison

IGListKit:

let adapter = ListAdapter(updater: ListAdapterUpdater(), viewController: self)
adapter.collectionView = collectionView
adapter.dataSource = self

Render:

let component = MyComponent()
let node = Node(component)
node.render(in: view)

Key Differences

  • IGListKit is specifically designed for UICollectionView, while Render is a more general-purpose UI framework
  • Render uses a declarative approach, similar to React, while IGListKit follows a more traditional iOS programming model
  • IGListKit provides advanced diffing algorithms for efficient updates, whereas Render focuses on simplicity and ease of use

Use Cases

  • IGListKit: Ideal for complex, data-driven list interfaces with frequent updates
  • Render: Better suited for building custom UI components and simpler view hierarchies

Both libraries aim to improve UI development on iOS, but they take different approaches and cater to different needs within the ecosystem.

LayoutKit is a fast view layout library for iOS, macOS, and tvOS.

Pros of LayoutKit

  • More mature and battle-tested, having been used in production by LinkedIn
  • Supports a wider range of layout types, including collection views and table views
  • Better documentation and examples available

Cons of LayoutKit

  • No longer actively maintained (in "attic" status)
  • Less flexible for custom layouts compared to Render's component-based approach
  • Steeper learning curve due to its more complex API

Code Comparison

LayoutKit:

let layout = InsetLayout(
    insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10),
    sublayout: LabelLayout(text: "Hello, World!")
)

Render:

class HelloWorldComponent: Component {
    override func render() -> NodeType {
        return Label().text("Hello, World!").padding(10)
    }
}

Summary

LayoutKit offers a more comprehensive solution for complex layouts, especially for large-scale applications. However, its development has stalled, and it may not be suitable for new projects. Render, on the other hand, provides a more modern, component-based approach that aligns well with current UI development trends, albeit with a potentially smaller feature set.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Render Swift ObjC++ License

Render

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)
  }
}
screen

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: