Skip to content

Latest commit

Β 

History

117 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

miso-lynx

A tasty mobile Haskell framework 🍜 🐈 πŸ“±

Matrix #haskell-miso:matrix.org Haskell Cachix Build Status LICENSE

Tip

This project has been upstreamed into miso proper. Please see the docs for more information

miso-lynx 🍜 🐈 is a mobile framework that uses miso and LynxJS to facilitate drawing and API interaction on iOS, Android, and HarmonyOS devices. The miso project is excited to use LynxJS to advance native mobile app development in the functional programming space.

Why Lynx

Since 2017, miso has sought ways to run on mobile devices that take advantage of native drawing facilities. Flutter, SwiftUI produce stunning, performant user experiences but are not necessarily designed for integration with open standards, are effectively only usable with a specific IDE / choice languages, and can lack in cross-platform capability (SwiftUI specifically). React Native improves upon this situation by allowing open web standards for development and cross-platform capability, but wasn't designed for easy integration with any web framework (rather for use specifically with react).

Lynx addresses the aforementioned issues with a new architectural approach.

  • Dual-threaded interpreter runtime

    The Lynx engine uses two embedded JavaScript interpreters to selectively schedule / offload computation to free up the render thread. This avoids drawing lag as commonly seen with scroll events in react native applications.

  • Element PAPI (PrimJS API)

    Exposing a DOM API for rendering allows any JavaScript (or compile-to-JavaScript 🍜) web framework to produce cross-platfom mobile applications. Seen here in Miso.Native (upstreamed from miso-lynx).

  • Cross platform capability

    Lynx targets iOS, Android, HarmonyOS, Web by default, and has a roadmap that mentions Desktop UI as well (OSX, etc.)

  • Instant first-frame rendering - IFR

    The flagship isomorphic (server side rendering) feature in miso can be repurposed as IFR in miso-lynx (one of the flagship features of lynx).

  • Extensibility

    Custom native components can be written in Objective-C, Swift, Kotlin or Java and accessed via the Lynx Native Module system.

For framework implementors, this is a dream come true, and we hope miso-lynx can be an ideal development environment for building Lynx applications with miso.

Table of Contents

React Summit

As seen @ React Summit by @huxpro !

Alt text

The miso portion is queued here.

Fireship

See Fireship πŸ”₯ πŸš€ video

Demo

Preview

To build the example locally, via the flake (which pulls in miso's ghcNative package set and its shared mkLynxBundle helper):

$ git clone git@github.com:haskell-miso/miso-lynx.git
$ cd miso-lynx
$ nix build .#counter-bundle
$ http-server ./result

This will host the main.lynx.bundle which can be loaded into the LynxExplorer for interactive development.

Note

You will need to have the LynxExplorer installed which works with the iOS simulator. Please see the LynxJS getting started guide for installation.

Quick Start

To start developing applications with miso and LynxJS see the miso-lynx-gallery example project.

Example

Main.hs

This file contains a simple miso-lynx counter application (see examples/counter/Main.hs).

-----------------------------------------------------------------------------
{-# LANGUAGE LambdaCase                  #-}
{-# LANGUAGE RecordWildCards             #-}
{-# LANGUAGE OverloadedStrings           #-}
{-# LANGUAGE GeneralizedNewtypeDeriving  #-}
{-# LANGUAGE StaticPointers              #-}
{-# LANGUAGE DeriveGeneric               #-}
{-# LANGUAGE DeriveAnyClass              #-}
{-# LANGUAGE DerivingStrategies          #-}
-----------------------------------------------------------------------------
module Main where
-----------------------------------------------------------------------------
import           Miso hiding (text_)
import           Miso.Native
import           Miso.Native.Element.View.Event (onTap)
-----------------------------------------------------------------------------
import           Miso.Lens
import           Miso.String
import qualified Miso.CSS as CSS
import           Miso.JSON (ToJSON, FromJSON)
import           GHC.Generics (Generic)
-----------------------------------------------------------------------------
-- | Application model
newtype Model = Model { _value :: Int }
  deriving stock (Show, Eq, Generic)
  deriving anyclass (ToJSON, FromJSON)
  deriving newtype (ToMisoString)
-----------------------------------------------------------------------------
value :: Lens Model Int
value = lens _value $ \m v -> m { _value = v }
-----------------------------------------------------------------------------
data Action
  = AddOne
  | SubtractOne
  deriving stock (Show, Eq, Generic)
  deriving anyclass (ToJSON, FromJSON)
-----------------------------------------------------------------------------
-- | Entry point for a native miso application
main :: IO ()
main = native nativeEvents (static (mountStatic_ counterComponent))
-----------------------------------------------------------------------------
counterComponent :: Component () () Model Action
counterComponent = component (Model 0) updateModel viewModel
-----------------------------------------------------------------------------
updateModel
  :: Action
  -> Effect () () Model Action
updateModel = \case
  AddOne ->
    value += 1
  SubtractOne ->
    value -= 1
-----------------------------------------------------------------------------
-- | Constructs a virtual DOM from a model
viewModel :: () -> () -> Model -> View () Model Action
viewModel _ _ m = view_
  [ CSS.style_
    [ CSS.height "200px"
    , CSS.display "flex"
    , CSS.alignItems "center"
    , CSS.justifyContent "center"
    ]
  ]
  [ view_
    [ onTap AddOne
    , CSS.style_
        [ CSS.backgroundColor CSS.yellow
        , CSS.width "100px"
        , CSS.height "100px"
        , CSS.margin "2px"
        , CSS.display "flex"
        , CSS.alignItems "center"
        , CSS.justifyContent "center"
        ]
    ]
    [ text_
      [ CSS.style_
        [ CSS.fontSize "48px"
        ]
      ]
      [ "🐈"
      ]
    ]
  , view_
    [ CSS.style_
        [ CSS.backgroundColor CSS.orange
        , CSS.width "100px"
        , CSS.height "100px"
        , CSS.display "flex"
        , CSS.alignItems "center"
        , CSS.justifyContent "center"
        ]
    ]
    [ text_
      [ CSS.style_
        [ CSS.fontSize "48px"
        ]
      ]
      [ text $ ms (m ^. value)
      ]
    ]
  , view_
    [ onTap SubtractOne
    , CSS.style_
        [ CSS.backgroundColor CSS.pink
        , CSS.width "100px"
        , CSS.height "100px"
        , CSS.margin "2px"
        , CSS.display "flex"
        , CSS.alignItems "center"
        , CSS.justifyContent "center"
        ]
    ]
    [ text_
      [ CSS.style_
        [ CSS.fontSize "48px"
        ]
      ]
      [ "🍜"
      ]
    ]
 ]
-----------------------------------------------------------------------------

Now that your project files are populated, development can begin.

Haddocks

See the official Haskell documentation

Examples

Binary cache

nix users on Linux or OSX distros can take advantage of a binary cache for faster builds. To use the binary cache follow the instructions on cachix.

Tip

We highly recommend nix users consume the cachix cache. cachix use haskell-miso-cachix.

$ cachix use haskell-miso-cachix

Maintainers

@dmjio

Contributing

Feel free to dive in! Open an issue or a submit Pull Request.

See CONTRIBUTING for more info.

License

BSD3 Β© dmjio

Releases

Sponsor this project

Packages

Contributors

Languages