liveview-native / liveview-client-swiftui Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
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.
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>
Tracking issue/discussion for an element representing SwiftUI's TabView.
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>
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.
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
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!
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.
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
Steps to reproduce:
Hello World
rendering in the native client.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
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:
👍 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
Fix favourited to favourite.
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)
I'd like to start imposing some project constraints. The following changes need to be made on the Core branch:
LiveView-Native/
Phx
. This needs to be done away with, for example: https://github.com/liveviewnative/liveview-client-swiftui/blob/core/Sources/PhoenixLiveViewNative/Views/PhxHStack.swift should be renamed to HStack.swift
and the corresponding struct needs to be named HStack
as well under the implied namespace of the library. We can reference the original SwiftUI view: https://github.com/liveviewnative/liveview-client-swiftui/blob/core/Sources/PhoenixLiveViewNative/Views/PhxHStack.swift#L19 with SwiftUI.HStack
No Operating Systems available for download instead of seeing the Xcode..
We need to click on the applications on the top of the page to download Xcode.
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 behaviour
s 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.
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:
When LiveViewCoordinator
disconnects it should send a phx_leave
event to allow proper handling of closed sockets on the backend. See: https://github.com/phoenixframework/phoenix_live_view/blob/4815d37accbfbb0288de5a211ac3211b05f399b9/lib/phoenix_live_view/channel.ex#L102
In order to obtain realtime data without user interaction.
For example:
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?
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.
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.
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.
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];
@shadowfacts is there any way we can prevent LiveView Native from being used in Carplay?
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.
push_redirect
)Step 3
Add a new Swift package dependency by selecting File -> Add Packages…, and in the search field, enter https://github.com/liveviewnative/liveview-client-swiftui, and click Add Package
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
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:
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.
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).
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
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
This might cause issues for users who are directly copying the code.
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
)
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.