UIViews that update themselves when your data changes, like React.

Overview

Views last updated: just now.

StateView is a UIView substitute that automatically updates itself when data changes.

Contents:

Overview

StateView is a UIView subclass that uses modern thinking and inspiration from what Facebook has done with React and the DOM to make displaying and updating your views easier, simpler, and more fun.

With StateView...

  • Your views update themselves when your data changes.
  • Your views add, remove, and update their subviews by themselves when your data changes.
  • Your views only update themselves if they need to. Each StateView calculates a diff (powered by a wonderful package named Dwifft) when your data changes to understand which subviews can stay, which can go, and which can be refreshed. Then, your StateView makes only those minimal changes.
  • You can write any custom view as a StateView.
  • You can write StateViews that contain other StateViews, standard UIViews, or a mix of both.
  • Your StateViews can automatically update a subview that is a standard UIView without any new code or special wrapping around that UIView.
  • You are encouraged to keep state across different views in your view hierarchy. Managing this state is easier when you use StateView and don't have to think about when, where, or how to call to methods like init, addSubview, and removeFromSuperview on your subviews.
  • You don't need to re-architect your app to be a declarative, functional, event-streamed, sequence-based, event-catching app to enjoy the benefits of reactivity and a family of views that are all pure functions of their state.

What's it like?

When you create your first StateView, you will become familiar with props, state, and render().

You can use props to pass values from one StateView to another, state to keep values in a StateView privately, and render() to describe how a StateView looks. StateView is simply a subclass of UIView that uses these three to update itself when your data changes.

Both props and state allow values of type Any to encourage you to keep and pass around anything that works for you.

When you add your first subview to a StateView inside render(), you will become familiar with place().

You can use place() to add a subview to your StateView. Then, your StateView will call addSubview and removeFromSuperview by itself so you don't have to.

In render(), simply look at your state and any passed-in props, and write code like...

override func render() {
	
	if let selectedImage = self.state["selectedImage"] as? UIImage {
		place(ImageViewWithTags.self, "image") { make in
			make.size.equalTo(self)
			make.center.equalTo(self)
		}
	} else {
		place(PlaceholderImageView.self, "placeholder") { make in
			make.size.equalTo(self)
			make.center.equalTo(self)
		}
	}
}

If in state, the key "selectedImage" contains a UIImage, an 'ImageViewWithTags' is placed, given the key "image", and given some AutoLayout constraints (using a wonderful library named SnapKit). If in state, "selectedImage" is nil or missing altogether, a PlaceholderImageView is placed instead.

Simply update your data and the view will update itself.

Change the value of "selectedImage" in state and this StateView will display, or not display, another custom StateView named ImageViewWithTags. Your StateView will call addSubview and removeFromSuperview by itself to render an updated view.

This, the way you can use render() and place(), is one of the most fun parts of using StateView.

Your code to decide which subviews to add to a StateView can go in render() where it will run any time your data changes. The results of a render() pass are then diffed with the results of the previous render() pass and your StateView only makes up the difference.

You can use props to pass values from one StateView to another. You can propagate new data in the state of one StateView to its children this way. In this case, to pass the new image in state to ImageViewWithTags, you can write code like...

override func render() {

	if let selectedImage = self.state["selectedImage"] as? UIImage {
		let imageView = place(ImageViewWithTags.self, "image") { make in
			make.size.equalTo(self)
			make.center.equalTo(self)
		}
		imageView.prop(forKey: Home.image, is: selectedImage)
	} else {
		place(PlaceholderImageView.self, "placeholder") { make in
			make.size.equalTo(self)
			make.center.equalTo(self)
		}
	}
}

Now this instance of ImageViewWithTags will receive selectedImage in its props. ImageViewWithTags can then access the new value of selectedImage anywhere in any of its methods, and especially in its own render method to do something like update a UIImageView.

ImageViewWithTags can access the value of selectedImage by using the same key used here, Home.image, which is an enum. You can create any number of your own enums to name your values any way that works for you. Any time state has a new value for selectedImage, ImageViewWithTags will receive the new value and update itself.

The second value in place, key, is used to help understand which views are the same between renders. The value of key can be anything you’d like, as long as no other subviews in that StateView have the same key. If the key of something you’ve placed changes between renders, StateView will render that subview from scratch since there won't be any existing views placed with the new key that can be preserved.

When you create your first StateView, you will become familiar with the following thought process:

  • How can this view change? I can leave variables in state to describe these different changes.
  • Now in render(), when state has this value for that key, I should place this subview, but if it has this value for that other key, I should place this one instead.
  • I can enumerate all the values I plan on passing between these views so that I can see them in one place and make it easy for others to understand data flow in my application.
  • That subview will get its props from this StateView's parent view, so I can pass along two of the props passed into this StateView into that StateView.
  • Which code is responsible for changing that value in state again? Oh, this callback here. I should add an initial value for that key in state to this StateView so that my render() has something concrete to use to decide what to display before that callback returns something.
  • Is there anything else I should put in render() since render runs any time my data changes? Is there anything else I'd like to be subtly different in my view when my data looks like this but not like that?

A full documentation of StateView and a getting started guide is in the wiki.

Sample apps

Frame is on the App Store and was made with StateView. With StateView, Frame ended up being just four classes, three of which are well under one hundred lines.

SwiftHub is an iOS app that displays Swift repositories from GitHub.

How does it work?

When you use a StateView, there is a ShadowView behind-the-scenes that helps understand which subviews can stay, which can go, and which can be refreshed between calls to render().

Each instance of StateView has a one-to-one matching instance of ShadowView that uses lightweight structs and references to keep a record of the view hierarchy and the data that the hierarchy represents.

Both render() and place() work closely with a StateView’s ShadowView to make the smallest number of changes to the view hierarchy to update what you see on screen.

When state changes in one of your views, a ShadowView orchestrates the calculation of the diff, the adding and removing of any needed or not needed subviews, and the passing of props from StateViews to their contained StateViews.

Installation

You can install StateView from CocoaPods. The latest release of StateView supports Swift 3.

pod 'StateView'

For Swift 2, use the last tag of StateView before StateView 2.

pod 'StateView', '~> 1.3'

What's next?

StateView was made to give iOS developers a safe, easy way to see their views update themselves. Above all, the goal is for StateView to be easy to use.

Any help towards making the methods, parameters, and patterns contained inside feel simpler despite the complexity of what StateView does is appreciated greatly. As are improvements to the performance of StateView.

If you’d like to contribute, take a look at the list of known issues in the wiki or start a new conversation in this project's issues!

Credits

StateView was written by Sahand Nayebaziz. StateView was inspired by React and the DOM.

License

StateView is released under the MIT license. See LICENSE for details.

You might also like...
ios-queryable is an implementation of IQueryable/IEnumerable for Core Data

#ios-queryable is an Objective-C category that provides IQueryable and IEnumerable-like functionality to Core Data. Tired of writing boilerplate Core

A iOS SwiftUI framework for displaying data from an api inside a List.

FeedListKit FeedListKit is a high level framework for representing data from an Api inside a SwiftUi List. It automatically handles refreshing and loa

App in Swift that shows a picture of an astronomical phenomenon and a brief explanation of it everyday, allowing you to save that data or share it on a social network.

DailyUniverse 👷‍♂️ 🛠 App under construction iOS App that shows a picture of an astronomical phenomenon and a brief explanation of it everyday. Daily

Make your notification banners smaller and add some color to them

Liddell Liddell notification banners Installation Add this repository to your Package Manager: https://repo.litten.love Install Liddell Compiling Depe

How to build and sign your iOS application using Azure DevOps

How to build and sign your iOS application using Azure DevOps Sample source code

Registre-aqui - Mobile Application that displays infrastructure issues that your city may have
Registre-aqui - Mobile Application that displays infrastructure issues that your city may have

Registre Aqui About | Features | Technologies | Requirements About Mobile Applic

Snitch - A handy library to access useful information about your application from the Home Screen
Snitch - A handy library to access useful information about your application from the Home Screen

Snitch Access your app's useful information from Home Screen Table of Contents I

PizzInfo - A SwiftUI based app to know a bit bout your favourite pizzas

PizzInfo Downloading all the playgrounds Unless otherwise indicated, all playgro

Add validations to your text fields, Group them together and navigate through them via keyboard's return button and accessory view.
Add validations to your text fields, Group them together and navigate through them via keyboard's return button and accessory view.

TFManager Let's say you have multiple UITextFields to get data from users. You need to handle each field keyboard's return key and add an accessory vi

Comments
  • Manual Installation

    Manual Installation

    CocoaPods is an awesome tool and make our life really easier, but there are some devs who still don't know how to use them.

    It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

    question 
    opened by lfarah 2
  • Using StateView and StateKeys

    Using StateView and StateKeys

    Hi, very excited about the promise of StateView but it seems like it's not quite working correctly for me. Here's what I have:

    import StateView
    
    struct STStashStateViewLabelKey: StateKey {
        var hashValue: Int = 1
    }
    
    
    class STStashChildView: StateView {
    
        override func render() {
    
            print("rendering STStashChildView")
    
            let label = UILabel()
            label.text = self.prop(withValueForKey: STStashStateViewLabelKey()) as! String
            place(label, key: "label") { (make) in
                make.center.equalTo(self)
            }
        }
    
        override func viewWillUpdate(newState: [String : Any?], newProps: [StateViewProp]) {
            print("viewWillUpdate in STStashChildView: \(newProps)")
        }
    
    }
    
    class STStashStateView: StateView {
    
        override func render() {
    
            print("rendering STStashStateView")
    
            let labelView = place(STStashChildView.self, key: "labelContainerView1") { (make) in
                make.center.equalTo(self)
            }
            labelView.prop(forKey: STStashStateViewLabelKey(), is: self.state["text"] as! String)
    
            let labelView2 = place(STStashChildView.self, key: "labelContainerView2") { (make) in
                make.center.equalTo(self)
            }
            labelView2.prop(forKey: STStashStateViewLabelKey(), is: "Same Text" as! String)
        }
    
        override func viewWillUpdate(newState: [String : Any?], newProps: [StateViewProp]) {
            print("viewWillUpdate in STStashStateView: \(newProps)")
        }
    
    }
    

    And I initiate state change like that somewhere else in my code:

    let stashStateView = STStashStateView(parentViewController: self)
    view.addSubview(stashStateView)
    stashStateView.setRootView()
    
    stashStateView.state = ["text": "some new text value"]
    

    after stashStateView state changes I get viewWillUpdate and render calls in STStashStateView and in both STStashChildViews even though only one of them suppose to change due to new props but another one still renders "Same Text" unchanged. Seems like the diff doesn't work correctly or am I missing something?

    question 
    opened by alexvbush 2
  • Does StateView support Objective-C or OS X(NSView), too?

    Does StateView support Objective-C or OS X(NSView), too?

    It seems like a great idea that bringing react concept to native ios app. So i would like to try StateView. Since I code mostly Objective-C for OS X, I am curious if StateView works with them.

    question 
    opened by ryuheechul 2
Releases(2.0)
  • 2.0(Sep 14, 2016)

  • 1.3(May 19, 2016)

    1.3

    Released on May 19th, 2016

    Additions

    • You can now use viewDidInitialize(), viewWillUpdate(newState, newProps), and viewDidUpdate() to run code at special points in the lifetime of your view.
      • viewDidInitialize() is called when your StateView subclass is initialized and has received its initial state and props.
      • viewWillUpdate(newState, newProps) is called after your StateView subclass receives new values in state or props and is about to update its subviews. This method is not called when your view is initialized.
      • viewDidUpdate() is called after your StateView has updated its subviews after receiving new values in state or props. This method is not called when your view is initialized.
    Source code(tar.gz)
    Source code(zip)
  • 1.2(May 15, 2016)

  • 1.1(Apr 27, 2016)

  • 1.0(Apr 25, 2016)

    1.0

    Released on April 25th, 2016

    Additions

    • You can now pass values between views by naming them with an enum of your choice. With enums, the names you have chosen for your values are clearer to see together and can make use of Xcode's built-in autocomplete. To use an enum of your choice, create an enum that conforms to the StateKey protocol. See the latest documentation for more information.

    Deprecations

    • You can no longer name values being passed between views with Strings. Use an enum of your choice that conforms to the StateKey protocol.
    Source code(tar.gz)
    Source code(zip)
Owner
Sahand Nayebaziz
Previously @Apple Senior Software Engineer
Sahand Nayebaziz
Enables you to hide ur UIViews and make them screen/screen shot proof. objective c/c++ only

SecureView Enables you to hide ur UIViews and make them screen/screen shot proof. objective c/c++ only Usage UIWindow* mainWindow; - (void) setup {

Red16 6 Oct 13, 2022
NSFileProviderManager.signalEnumerator does not trigger update of UIDocumentBrowserViewController

FB9715717 Which area are you seeing an issue with? FileProvider Framework Incorrect/Unexpected Behavior Subject NSFileProviderManager.signalEnumerator

Marcin Krzyzanowski 2 Dec 11, 2021
Server Driven UI can enable faster iterations and allowing apps to instantly update on multiple platforms.

Pets App Server Driven UI can enable faster iterations and allowing apps to instantly update on multiple platforms Steps to run Pets-Web: Download or

Metin Atalay 0 Jun 11, 2022
⌨️ KeyboardToolbar - Add tools above your keyboard with iOS-like keyboard buttons

KeyboardToolbar ?? Overview Use KeyboardToolbar to add tools as an input accessory view to a UITextField, UITextView, or any other view conforming to

Simon Støvring 197 Dec 21, 2022
This library allows you to make any UIView tap-able like a UIButton.

UIView-TapListnerCallback Example To run the example project, clone the repo, and run pod install from the Example directory first. Installation UIVie

wajeehulhassan 8 May 13, 2022
A mental health app designed to help users track their emotions with short, tweet-like journals.

Objective The purpose of this project is to create a mental health app where users will input a short journal each day that is no longer than a tweet

Isha Mahadalkar 0 Dec 25, 2021
iOS constraint maker you always wanted. Write constraints like sentences in English. Simple

YeahLayout iOS constraint maker you always wanted. Write constraints like sentences in English. Simple. Intuitive. No frightening abstractions. One fi

Андрей Соловьев 1 Jan 10, 2022
Allows users to pull in new song releases from their favorite artists and provides users with important metrics like their top tracks, top artists, and recently played tracks, queryable by time range.

Spotify Radar Spotify Radar is an iOS application that allows users to pull in new song releases from their favorite artists and provides users with i

Kevin Li 630 Dec 13, 2022
SwiftUI views that arrange their children in a Pinterest-like layout

SwiftUI Masonry SwiftUI views that arrange their children in a Pinterest-like layout. HMasonry A view that arranges its children in a horizontal mason

Ciaran O'Brien 88 Dec 27, 2022
API Calling - Made an app in swift that gets data from Randomuser api and uses it in UITableView

API_Calling Made an app in swift that gets data from Randomuser api and uses it

Arnav Chhokra 1 Feb 4, 2022