A tasty mobile Haskell framework π π π±
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.
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.
-
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.
-
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 frommiso-lynx). -
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
misocan be repurposed as IFR inmiso-lynx(one of the flagship features of lynx). -
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.
- React Summit
- Fireship
- Demo
- Preview
- Quick Start
- Example
- Haddocks
- Binary cache
- Maintainers
- Contributing
- License
As seen @ React Summit by @huxpro !
The miso portion is queued here.
See Fireship π₯ π video
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 ./resultThis 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.
To start developing applications with miso and LynxJS see the miso-lynx-gallery example project.
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.
See the official Haskell documentation
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-cachixFeel free to dive in! Open an issue or a submit Pull Request.
See CONTRIBUTING for more info.
BSD3 Β© dmjio
