Closed bcardarella closed 1 year ago
So in other words, this is could be the breakdown:
[templatename].[platform].[UIframework].heex
@immranderson does Jetpack Compose have similar target platforms as SwiftUI?
@bcardarella I believe that Android might be a bit different here?
I know generally that the dependencies to enable support + utilize the components will need to be added to the build.gradle dependency file, but compose for wearos + androidtv is a bit of a newer thing. I'll check out how it's configured and get back to you with more specifics 👍
@bcardarella I have some abstractions I've been playing around with for narwin_chat.
on_mount
hook that provides a :platform
assign to all LiveViews that inherit it via live_session
.
A helper module for generating platform-specific render functions. You just include it in your LiveView and it provides a render_native
function which uses that :platform
assign to determine which render function to call:
defmodule NarwinChatWeb.LoginLive do
use NarwinChatWeb, :live_view
use NarwinChatWeb.LiveViewNativeHelpers, template: "login_live"
# ...
@impl true
def render(assigns) do
# This will render one of the following templates depending on assigns.platform:
#
# :ios => login_live.ios.heex
# :android => login_live.android.heex
# :web => login_live.html.heex
#
render_native(assigns)
end
end
It's pretty basic right now but could be extracted into a standalone library, extended, etc.
@supernintendo what is missing for me is how deep we should go. Does it make sense to differentiate between platform variants? Like do we need to care which ipad is rendering because of the screen size?
Hmm, that's a good question. I think rendering different templates across platform variants could get difficult to maintain for the end user / developer. It also seems impractical for Android.
One thing we might consider is passing device-specific information to LiveView via something like UIScreen
which would then be included as assigns - that way, developers could target specific device sizes and configurations within platform templates without having to maintain separate templates for those variants.
I think rendering different templates across platform variants could get difficult to maintain for the end user / developer
This was my sense as well, I just don't know what is idiomatic on native for different screen sizes. Does SwiftUI adjust or must you render screen specific UI?
@bcardarella
So for Android, it gets a bit tricky:
@immranderson I'm OK with this
Does it make sense to differentiate between platform variants? Like do we need to care which ipad is rendering because of the screen size?
I don't think there need to be separate templates for different platform variants within an OS (e.g., iPhone vs iPad). Information about the current variant (device class, screen size) could be provided to the backend and then attached as socket assigns if the particular app needs it, letting the template make decisions about how it wants to render. That essentially mirrors how you'd go about handling different screen sizes in a purely native app.
Does SwiftUI adjust or must you render screen specific UI?
Some views do, but a good native app will make further changes to its layout (e.g., having a set of buttons be arranged horizontally or vertically) based on the device it's running on beyond just what SwiftUI will do.
Information about the current variant (device class, screen size) could be provided to the backend and then attached as socket assigns if the particular app needs it, letting the template make decisions about how it wants to render. That essentially mirrors how you'd go about handling different screen sizes in a purely native app.
I can't speak to SwiftUI specifically, but this similar to how you would handle it in React Native. There is a Platform module that you can use to make small platform-specific conditionals. For templates, platform-specific extensions are used, but are only scoped to the OS ('.ios', '.android') level.
Also, it is fairly common to use something like react-native-device-info to get extra device information like isTablet()
, hasNotch()
, and isLandscape()
, and use that to inform layout decisions. If device data like this could be optionally assigned in an on_mount
hook, that would probably be enough to make all the layout tweaks you need.
Something to also consider is that the device can change over time. Orientation may change from landscape to portrait, an iPad app may enter splitview, iOS apps can run on Macs, iPads can have external screens, etc. Device state changes will need to be passed back to the LiveView process so templates can be re-rendered.
To style a SwiftUI view for a particular screen size you'd use @Environment(\.verticalSizeClass) var verticalSizeClass
and the use the verticalSizeClass
in the body of the view to make decisions. I suppose you would need to have a way to shuttle those to the live view template somehow.
struct ContentView: View {
@Environment(\.verticalSizeClass) var verticalSizeClass
@State var coordinator: LiveViewCoordinator<EmptyRegistry> = {
var config = LiveViewConfiguration()
config.navigationMode = .enabled
return LiveViewCoordinator(URL(string: "http://localhost:4000/cats")!, config: config)
}()
var body: some View {
LiveView(coordinator: coordinator, verticalSizeClass: verticalSizeClass)
}
}
There are a lot of EnvironmentValue
s that exist though, and you would only want to have to do this for the once you need. So alternatively, perhaps the config of the coordinator would have a setting to enable those needed?
@supernintendo could you please advise here? i think it is done
@supernintendo could you please advise here? i think it is done
@AZholtkevych Sorry, just now getting to this. The original issue has been resolved. Platform libraries have been expected to define a platform_id
for a few versions now (for this repo it's :swiftui
). This is used to namespace HEEx templates both externally (i.e. home.swiftui.heex
) and inline when rendering with the non-platform-specific ~Z
(~LVN
after Elixir 1.15) sigil:
# match on `platform_id` (automatically assigned via `use LiveViewNative.LiveView`)
def render(%{platform_id: :swiftui} = assigns) do
~LVN"""
<VStack>
<Text>Hello world!</Text>
</VStack>
"""swiftui # render sigil is modified with `platform_id`
end
We've also since added various device-specific fields as part of the connection handshake (https://github.com/liveview-native/liveview-client-swiftui/pull/900). Those fields along with their possible values for the SwiftUI platform are as follows:
:os_name
- "iOS"
, "macOS"
, "tvOS"
, "watchOS"
:os_version
- any OS version represented with semantic versioning (i.e. "16.4.1"
)
:user_interface_idiom
- "watch"
, "mac"
, "phone"
, "pad"
, "tv"
, or "unspecified"
@bcardarella Let me know if you feel like anything has been left unaddressed but my sense is that this issue has long been resolved and we can go ahead and close it.
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: