Giter Club home page Giter Club logo

liveview-client-swiftui's People

Contributors

bcardarella avatar bkilshaw avatar byeongukchoi avatar carson-katri avatar cmnstmntmn avatar dbii avatar dginsburg avatar drewchandler avatar kgautreaux avatar lostkobrakai avatar mrluc avatar murtza avatar nduatik avatar shadowfacts avatar supernintendo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liveview-client-swiftui's Issues

[idea] [ios] Alert Controller

Why?

AlertControllers (https://developer.apple.com/documentation/uikit/uialertcontroller?language=objc) are a way of presenting and requesting information to the user.

They are useful in Forms and other events.

How?

This is just a sample syntax. Considering that this element only will be presented on a boolean condition only.
This syntax is inspired by https://surface-ui.org/

<alert :if={show_alert} title="My Title" message="My Message">
  <button style="cancel">Cancel</button>
  <button>OK</button>
</alert>

TabView

Tracking issue/discussion for an element representing SwiftUI's TabView.

Design Considerations

Would you want tabs to be separate pages (similar to how hierarchical navigation works) or have the view tree for each page specified together?

Linked:

<tabview>
	<tab title="Tab One" target="/one" />
	<tab title="Tab Two" target="/two" />
</tabview>

Embedded:

<tabview>
	<tab title="Tab One">
		<text>One</text>
	</tab>
	<tab title="Tab Two">
		<text>Two</text>
	</tab>
	<!-- etc. -->
</tabview>

Implementation

Initially at least, it would likely be limited to client-side navigation (no switching tabs from the backend). Backend navigation is blocked by the ability to create two-way bindings between the client and the backend LiveView, as I discussed in #34.

In iOS, the convention is navigation happens within the tab, but the current navigation implementation expects to be at the outermost level of the view tree.

  • There would need to be separate navigation coordinators for each tab (since there's 1 per navigation stack)
  • The configuration needs to separate whether navigation is permitted from at what level of the view tree navigation happens

Platform/Version handling

I'm not sure if this would turn into a mess within the template or if it would just really impact performance but I've been thinking about a no-op method within the template for future proofing against server-side and client being out of sync. (i.e. server wants to push templates with newer components but client is not on latest iOS or hasn't updated clent)

<Version swift="A.B.C" client="~> 0.1">
   ...
</Version>

Within the client if there is not support for the version of Swift or the client app version then just don't render what is within the block.

Optionally, this could be extended to platforms:

<AppleTV>
   ...
</AppleTV>

This would make template editing and platform support super easy to add, however it may make the template a complete mess. I'd be OK sticking with our original idea of packing the platform information into the connection negotiation that way you can pattern match on it for mount and other functions. It should be in the state object and provided on each reconnect

Connection refused | can't find any relevant logs

I've followed the tutorial (for the most part, only the name of the app changed) and everything seems like it should be okay, but I'm seeing this in the output panel in XCode

2022-09-08 16:30:48.822624-0400 AshHq[44093:19117824] [LiveViewCoordinator] Connecting to http://localhost:4000/cats
2022-09-08 16:30:48.889501-0400 AshHq[44093:19118202] [connection] nw_socket_handle_socket_event [C1.1:2] Socket SO_ERROR [61: Connection refused]

Connecting to the page in the browser works fine (I'm on the <text> Hello world </text> step, very early on).

Simulating: iPhone 13 Pro
OS: Mac OS Monterey, apple silicon
Phoenix: 1.6.12
LiveView: 0.17.11

Happy to provide any other details! Super excited about this project!

End of "Style the Loading Screen to Match Your App" tutorial has unrelated change that causes Xcode comple error.

I was working through the tutorial, and at the very end, I observed the tutorial instructions trying to have me change the return value of lookupModifier, but that resulted in a compile error.

I think the change it recommends is unintended, as when I skipped it, I got the tutorial outcome I expected.

I think this might be a bug.

Screen Shot 2022-09-24 at 1 31 48 PM

Screen Shot 2022-09-24 at 1 31 53 PM

Screen Shot 2022-09-24 at 1 34 41 PM

https://github.com/liveviewnative/liveview-client-swiftui/blob/dceac9c74708ce06fbc30c3d2792756e592a19c9/Sources/PhoenixLiveViewNative/PhoenixLiveViewNative.docc/Resources/06-01-03-error.swift#L22-L23

please make library available for xcode version 13.2.1

Hi,

My Macbook Pro from 2014 cannot be upgraded to OSX 12.. which leaves me stuck at XCode 13.2.1 and unable to install this package with the following error:

package at 'https://github.com/liveviewnative/liveview-client-swiftui' @ 3a67fcd5bd488aefa9e01eeb65aea8684360c6c6 is using Swift tools version 5.6.0 but the installed version is 5.5.0 in https://github.com/liveviewnative/liveview-client-swiftui

ios client crashes if server disconnects

Steps to reproduce:

  1. Run through Step 6 in the tutorial and get to the Hello World rendering in the native client.
  2. Kill the phx server

The client will crash too. The client should instead go into a reconnect effort. Ideally in the future we will show a graceful error page or something if reconnects are taking too long

disconnection is sometimes unrecoverable without restarting the app

I was testing what happens when closing down the server started with iex -S phx.server and restarting, and ran into some crashes, and more frequently, unrecoverable error messages. (Nothing but ❤️ for this project, when it's connected it's already magical, totally understand that it's v0.0.1-alpha software, totally fine if answer is 'WONTFIX, wait for next version' or something. I'm documenting this in case it's helpful to someone. I'm curious if the details I mention here are known current limitations, or if those who have used this a bit more know something I don't -- like, is this only an issue in dev but not really in practice? maybe due to livereload paths being hit? etc -- be cool to look at the elixirconf ios chat app to see how it's handled there 😄).

Observed in: a working 'Getting Started' tutorial app, at the point of list > detail navigation, but before registering a custom component, ie at the end of this tutorial). Edit: in the Simulator, on an M1 Air.

Problem: killing the Elixir server, and then restarting it, often results in the app being 'stuck' in a broken state, at least in my experience. Restarting the mobile app fixes it.

This can happen in a variety of ways:

  • 👍 If I restart the Elixir server very quickly, within 1-2 seconds, all is usually well despite the momentary error.

  • 👎 👍 If I restart the Elixir server somewhat quickly, approx 3-5 seconds, the app gets backgrounded! Kind of funny. When I un-background it, it shows the error screen initially and then recovers.

  • ❌ however, from the root view, if I kill the server and restart in about 10 seconds or more, I usually encounter this most-frequent kind of fail, where the UI disagrees with logging! The error continues to be displayed, but both the server and the XCode console show that reconnection succeeded, and log heartbeat messages, etc; screenie showing this case:

    image
  • 👍 likewise, if I disconnect the server while in a child view, see the error, and then restart the server after 20 seconds or so, the child view does not recover -- but when I navigate back to the parent view, it will sometimes look good, and continue to function normally (perhaps due to navigation triggering an explicit reconnection?)

  • ❌ however, if I kill phx.server and wait a minute or two in the detail view, I sometimes experience a variety of harder fails, from the app exiting, to triggering this supposedly unreachable codepath in NavStackEntryView.elementTree
    image

    • These warnings are usually associated in time with failed reconnection attempts:

      image

Step 6: Incorrect folder spelling in path

In step 6 the directory is stated as lib/lvn_tutorial_web/templates/layouts/live.html.heex

Should be lib/lvn_tutorial_web/templates/layout/live.html.heex (layout vs layouts)

Repo changes

I'd like to start imposing some project constraints. The following changes need to be made on the Core branch:

SwiftUI Modifiers Syntax Discussion

This started over on the LiveViewNative Slack channel but I was asked to open a discussion issue on the repo by @bcardarella

My brief proposals on slack as follows:

re: Modifiers. They take a View and return a modified View so it seems to me that we should model this as an Elixir pipeline.

defmodule WelcomeTextComponent do
  use LiveViewNative, :component

  def modifiers(core_component) do
    core_component
    |> background()
    |> linear_gradient()
    |> gradient(colors: [:red, :blue], start_point: :top, end_point: :bottom)
  end
end

That's pretty rough but I think it conveys the idea. It would be relatively straightforward for someone to translate from SwiftUI modifier chains into an Elixir pipeline to achieve the desired modifier chain.

If you wanted to be less explicit with naming and not have to write this as:
<WelcomeTextComponent style={@style}>Example text</WelcomeTextComponent>

Maybe you could curry the function if you wanted to apply the same pipeline to a different component?
<asyncimage modifiers={&WelcomeTextComponent.modifiers/1(&1)} src={"/images/cats/#{name}" />

If you wanted to reduce module combinatorial explosion a syntax modeled after liveview JS commands might also be considered:

defmodule WelcomeTextLive do
  import LiveViewNative.Modifiers

  def render(assigns) do
    ~H"""
        <text modifiers={
           background()
           |> linear_gradient()
           |> gradient(colors: [:red, :blue], start_point: :top, end_point: :bottom)
          }
         style={@styles} id={#{@cat.id}}>
           #{@cat.name}
      </text>
    """
  end
end

Modifiers could be implemented as behaviours to ensure that they will have their API cover all applicable modifiers for the SwiftUI view they receive.

A brief examination of Jetpack Compose shows it to be less pipeline like and more about passing the modifiers into the component args so maybe this isn't a great approach. I would love to have @shadowfacts or @KronicDeth weigh in on how feasible each of these syntaxes would be in their respective declarative UI toolkits.

Scoping: Working list of SwiftUI views

Active list of all available views we will wrap. From:

Discussion on how and if a particular view will be implemented should happen within the associated issue not in this issue.

SwiftUI:

[idea][ios] Send Native Events to Server

In order to obtain realtime data without user interaction.

For example:

  • GPS realtime position
  • Magnetometer
  • Audio Recording
  • Video/Image recording
  • Bluetooth Low Energy Devices
  • Push Notifications
  • Wifi

And so on.

It is needed a bridge to communicate native App events that do not require direct user input
but the server can be listening to.

Some ideas is using bindings

https://hexdocs.pm/phoenix_live_view/bindings.html

<button phx-click="app:gps">+</button>
def handle_event("app:gps", _value, socket) do
  {:ok, new_temp} = Thermostat.inc_temperature(socket.assigns.id)
  {:noreply, assign(socket, :temperature, new_temp)}
end

Maybe a subset of the form?

https://liveviewnative.github.io/liveview-client-swiftui/documentation/phoenixliveviewnative/formcontrols

In my experience a way of doing this is using the service locator pattern (http://gameprogrammingpatterns.com/service-locator.html) of listener objects in the app. Just a simple dictionary with instances created in AppDelegate
And these object will call a specific method or send a specific request to the server when an event in the app happens.
this would require some modifications in the native app. The user would have to choose which events would be sent to the server.
Since, these events would require special configs for requesting permissions. They have to edit the app anyways.

Just some ideas.

Assertion when LiveView changes state

I have the following template:

<list>
  <% messages = assigns[:messages] || [] %>

  <%= for message_group <- messages |> Enum.chunk_by(& &1.user.id) do %>
    <% first_message = List.first(message_group) %>
    <% user_name = get_in(first_message, [:user, :name]) %>
    <% message_group_id = "message_group_id_" <> first_message.id %>

    <hstack alignment="center" id={message_group_id} list-row-separator="hidden" pad-top="8">
      <text><%= user_name %></text>

      <%= for message <- message_group do %>
        <% user_message_id = "user_message_" <> message.id %>
        <hstack alignment="center" id={user_message_id} list-row-separator="hidden" pad-top="8">
          <text><%= message.text %></text>
        </hstack>
      <% end %>
    </hstack>
  <% end %>
</list>

When a change is made to the LiveView assigns, this assertion is raised: https://github.com/liveviewnative/liveview-client-swiftui/blob/4e341d4617119da6a9092ebd8e4f3c7721f79e0c/Sources/PhoenixLiveViewNative/FragmentDiff.swift#L49

When I comment out that line, the LiveView re-renders on state changes as expected with no issues. It's not clear what this assertion is intended for. Consider fixing or improving the error handling around this.

[idea] [ios] Translatable String Component

Why?

For easier translation of strings. Currently there are awesome translation solution for Phoenix
and this component may not be needed.

But there are lot of apps that have Localizable.strings file that would benefit for using the Device Translation tools instead
of the server translation tools.

How

Example Syntax For a Single String

<trans fallback="Key Not Found" table="" bundle="">
  My Translatable String Key
</trans>

Example Syntax for Inside another component

<button><trans>My Translatable Button</trans></button>

Example as a global. All strings would be translated

<trans :all>
<myComponent title="My Title"></mycomponent>
</trans>

This is an Objective-C with a sample way of doing this:

/*
Mappings to the
`localizedStringForKey:value:table:`

https://developer.apple.com/documentation/foundation/nsbundle/1417694-localizedstringforkey

Returns a localized version of the string designated by the specified key and residing in the specified table.

- If table is null, it will use the file named Localizable.strings
- Bundle is the bundle identifier string. If bundle is null, it will use [NSBundle mainBundle]
- value is the default value if the key is not found.
*/

NSString *key = [params string:@"key" default:@""];
NSString *value = [params string:@"value" default:@""];
NSString *table = [params string:@"table"];
NSString *bundleIdentifier = [params string:@"bundle"];

NSBundle *bundle = [NSBundle mainBundle];

if (bundleIdentifier) {
    bundle = [NSBundle bundleWithIdentifier:bundleIdentifier];

    if (!bundle) {
        bundle = [NSBundle mainBundle];
    }
}

return [bundle localizedStringForKey:key value:value table:table];

Core LiveView Features To Implement

Hello. Upon quick experimentation I've discovered that LVN doesn't currently support push_redirect from the server side (it just makes the view hang).

Of course I can post a separate issue for that particular limitation but maybe - in the spirit of #2 - it'd be more handy to first assemble an as-exhaustive-as-possible list of core LiveView features e.g. by taking a look at docs and/or JS client source and create a list of everything that either new or seasoned LV developer should expect not to work at the moment. We then can extract them into separate issues if that'd be more handy.

  • Server-side redirects (push_redirect)

Issues with Xcode 14.0

After updating to Xcode 14 I am getting compilation errors like the following on bits of code in PhoenixLiveViewNative that are under if compiler(>=5.7):

LVNTutorialApp-bkjfahisyiztfhdjztpcsewumvjo/SourcePackages/checkouts/liveview-client-swiftui/Sources/PhoenixLiveViewNative/Utils/Alignment.swift:35:21: error build: Type 'Alignment' has no member 'leadingLastTextBaseline'

LVNTutorialApp-bkjfahisyiztfhdjztpcsewumvjo/SourcePackages/checkouts/liveview-client-swiftui/Sources/PhoenixLiveViewNative/LiveView.swift:108:13: error build: Cannot find 'NavigationStack' in scope

Swift Charts

Apple recently released a new charting framework https://developer.apple.com/documentation/charts. It's API seems a good fit for LiveView Native because it allows the data to be declared as child elements within the charts e.g.

var body: some View {
    Chart {
        BarMark(
            x: .value("Shape Type", "Cube"),
            y: .value("Surface count", 6)
        )
        BarMark(
             x: .value("Shape Type", "Cone"),
             y: .value("Surface Count", 2)
        )
        BarMark(
             x: .value("Shape Type", "Dodecahedron"),
             y: .value("Surface Count", 12)
        )
    }
}

which might be represented in an a heex template with something like:

<chart>
  <%= for {shape, surface_count} <- @datapoints do %>
    <bar-mark
       x-label="Shape Type"
       x-type="Text"
       x-value={shape}
       y-label="Surface"
       y-type="Int"
       y-value={surface_count}
    >
    </bar-mark
  <% end %>
</chart>

The nice thing about passing data to the frontend this way (rather than perhaps encoding it in an attribute as json or similar) is that dynamic updates to the chart data can make use of the usual LiveView diffing algorithm.

I had a quick look at implementing a POC but soon discovered that the Charts framework has it's own ChartContentBuilder instead of the usual ViewBuilder. This means you can't simply hook into the current custom registry that LiveView Native provides.

A couple of approaches that I considered:

  1. Register a top level Chart component in the existing LiveView Native custom registry and then take care of mapping the Chart's child elements to ChartContent separately.

  2. Perhaps it's possible to update the ViewTree etc. to handle building a hierarchy of arbitrary types (rather than some View). It seems unlikely that there will be many custom DSLs used with SwiftUI though so perhaps it wouldn't be worth the effort.

I'll end by sharing the use case that I have in mind. I want to build a chart of data from a glucose sensor. The data spans several years so infinite scrolling would be needed to make it efficient. I've built a component that adds scrolling and position tracking to Swift Charts and the plan is to send the current position to the backend over the websocket then update the datapoints rendered in the heex template (with a suitable buffer either side of the current scroll window).

Make left nav clickable

Screen Shot 2022-08-12 at 1 54 01 PM
I was expecting the steps to be clickable and had to figure out I had to scroll the left pane. Can we make it so that if you click some part, everything scrolls to the correct place?

Kinder onboarding

Screen Shot 2022-08-12 at 1 51 26 PM
We should tell the learner to install the deps and open the project in their editor

Which Elixir and Phoenix version do I need?

I think it's important to show the project depends on elixir and phoenix and their respective versions.. otherwise we may end up with an error like this:

tiagodavi:~λ mix phx.new lvn_tutorial --no-ecto --no-gettext --no-mailer
** (Mix) The task "phx.new" could not be found
Note no mix.exs was found in the current directory

Chat App Architecture

Real-time chat application

There should be rooms that correspond to the schedule of events at ElixirConf. So if it is a matter of scraping the schedule or just having general rooms that correspond to the tracks themselves is OK

When a user installs the app they’ll be prompted with their email address. Once this is submitted an email will be sent to that address if it is on the list of attendees. Either a magic link or a passcode for authentication will be provided. First name and last name will be pre-seeded associated with the attendee’s email address. This allows users to (mostly) confirm their identity. We don’t want anonymous users on this app.

Users should have the option to silence any one specific user. Silencing a user also means you cannot see them in the list of other users and they cannot see you. This is a permanent selection and cannot be undone.

The backoffice admin side of this should have a way to load in all users. So there needs to be an admin authentication function then essentially a way to upload probably a CSV of:

email,first_name,last_name

When logged into the app you should be in the “lobby” and have a list of the different room tracks.

Selecting a track brings you to the chat room for that track where the real-time conversation is happening. You should be able to see the entire chat history of the room.

There is no concept of DMs in this app. Only rooms-specific conversation

We are working off of the full names, no usernames. So when in a room you would see a list of everyone in the room. Names are sorted alphabetically last_name, first_name

Platform / device specific template conventions

We should probably start to develop what the naming convention should be for template extensions

I'd like to propose the following:

[name].swiftui.heex

All swift UI devices connecting could render the template of this name. We can offer more detail by targeting the platform to:

[name].ios.swiftui.heex
[name].tvos.swiftui.heex
[name].macos.swiftui.heex
[name].watchos.swiftui.heex

XCode also supports multiplatform so maybe [name].swiftui.heex is just an alias for [name].multi.swiftui.heex

Similarly, on other devices we can follow this convention so for Android:

[name].jetpack.heex

We should package this up into a support lib for Phoenix so we can do away with:

  EEx.function_from_file(
    :def,
    :render,
    "lib/lvn_tutorial_web/live/cats_list_live.ios.heex",
    [:assigns],
    engine: Phoenix.LiveView.HTMLEngine
  )

Cannot open the tutorial project in Xcode

Any idea why I'm getting this error using Xcode Version 13.4.1 (13F100)?

The project at 'LVNTutorialApp.xcodeproj' cannot be opened because it is in a future Xcode project file format. Adjust the project format using a compatible version of Xcode to allow it to be opened by this version of Xcode.

Components To Implement

  • AsyncImage
  • Button
  • Canvas (probably not necessary)
  • Capsule
  • Circle
  • ColorPicker
  • ControlGroup
  • DatePicker
  • DisclosureGroup
  • Divider
  • Ellipse
  • Form
  • Gauge
  • Grid
  • GridRow
  • GroupBox
  • HStack
  • Image
  • Label
  • Link
  • List
  • Menu
  • MultiDatePicker
  • NavigationLink
  • OutlineGroup
  • PasteButton
  • Picker
  • ProgressView
  • Rectangle
  • RoundedRectangle
  • ScrollView
  • SecureField
  • ShareLink
  • Slider
  • Spacer
  • Stepper
  • Table
  • Text
  • TextEditor
  • TextField
  • Toggle
  • VStack
  • ViewThatFits
  • ZStack

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.