Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hybrid apps? #1

Open
fairking opened this issue Nov 13, 2022 · 35 comments · Fixed by #2
Open

Hybrid apps? #1

fairking opened this issue Nov 13, 2022 · 35 comments · Fixed by #2
Labels
enhancement New feature or request

Comments

@fairking
Copy link

fairking commented Nov 13, 2022

This is awesome. Is there any plans to implement some kind of WebView, so we can port web SPA/PWA applications, the same way how tauri is doing? I am particularly interesting in Android. And of course I can rewrite my backend in vlang. ;-)

@malisipi
Copy link
Owner

Thank you for your attention. Embedding webview into same window is hard. So full-featured webview widget is like impossible. But you can look up vwebui if you can use webview inside another window. Also you can port your PWA application with Vebview.JS. But sad to say these solutions won't work inside Android.

@fairking
Copy link
Author

fairking commented Nov 13, 2022

Thanks for your reply, I hope there will be some solution to this, and it could be a deal breaker to bring more popularity to v language. I have several projects (Web/Electron) but there is no way to get them to mobile platforms, having a strong backend and native support. I am monitoring tauri, looks like they are currently working on the mobile support.

Why I am convinced to PWA over native UI:

  • Compatibility, one app - all platforms including Web (whatever trend is);
  • Flexibility, responsiveness, better touch support, animation, styling, many existing js/ui frameworks on the market;
  • All professional frontend designers prefer html/js over any backend (strong typed) languages;
  • Backend is strictly separated from the UI, so no spaghetti in the codebase;
  • Small and big projects, easy for improvements and new features;

The way how I am used to implement such applications:
The backend has services (DI) and expose those services to the public as an OpenAPI scheme. For web application such services consumed via http, for desktop/mobile app such services consumed via js interop (not localhost). During development the codegen uses API scheme to generate such services in JS/TS. All communication including native support happens via the API services (communication between frontend and backend using services).
Example:

  • Barcode scan. Frontend (js) calls a backend service (v) to get a barcode tag. The backend service opens a native ui window (over the webview) to scan the image. The scanned tag goes back to the frontend as a result of the service call. You can also open a data stream between backend and frontend to show the live cam picture in the actual webview instead of the native window if you want.

Another simple example of the implementation:
entities\article.v

struct Article {
	id    int    [primary; sql: serial]
	title string
	text  string
}

models\article.v

struct IdModel {
	id    int [required]
}

struct ArticleModel {
	id    int
	title string
	text  string
}

struct CreateArticleModel {
	title string [required; maxlength: 50]
	text  string
}

struct UpdateArticleModel { ... }

struct PublishArticleModel { ... }

...

services\article_service.v

[api]
struct ArticleService {
        db DbService // Injected service or repository
}

[api]
pub fn (article_service &ArticleService) get(query IdModel) ArticleModel {

	result = sql article_service.db {
		select from Article where id = $query.id
	}

        return result.map<ArticleModel>()
}

[api]
pub fn (article_service &ArticleService) create(form CreateArticleModel ) IdModel {

        form.validate_or_throw() or { ValidationError };

	result = sql article_service.db {
		insert into Article (id, title, text) set ($form.id, $form.title, $form.text)
	} or { DbError }

        return IdModel { result }
}

codegen generates the following js/ts (based on open api scheme, eg. https://github.com/Manweill/swagger-axios-codegen):
services.ts

export class IdModel {
  'id'?: number;

  constructor(data: undefined | any = {}) {
    this['id'] = data['id'];
  }

  public static validationModel = { id: { required: true } };
}

.. all other models

export class ArticleService {

  static get(params: { body?: IdModel;  } = {} as any, options: IRequestOptions = {}): Promise<ArticleModel> {
    return new Promise((resolve, reject) => {

      let url = basePath + '/ArticleService/Get';

      const configs: IRequestConfig = getConfigs('post', 'application/json', url, options);

      let data = params.body;
      configs.data = data;

      if (web) {
            axios(configs, resolve, reject);
      } else if (mobile) {
           js_interop(configs, resolve, reject);
      } else {
          throw Error("platform not supported");
      }
    });
  }

... etc other services

}

Example of js interop in .NET: https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/

I am aware of the following cons:

  • Increased memory usage
  • Extra communication backend <-> frontend
  • WebView limitations and security issues
  • Performance

So what is actually required is a WebView, and the ability to communicate between the WebView and the backend (in this case mui). Nothing else. It is easy as that.

@malisipi
Copy link
Owner

malisipi commented Nov 15, 2022

I can try to create WebView window with webview/webview. This library has good cross-platform support, uses system-webviews but it requires C++11 minimum (and C++17 for Windows). If I can make working the library with V (without very complicated compile steps), I will add a WebView window into mui for only desktop platforms (for now).

@malisipi malisipi added the enhancement New feature or request label Nov 15, 2022
@malisipi
Copy link
Owner

I got working webviews on Linux and Windows. That's not have API except C functions that provided by webview library for now, however i will write V-compatible functions (also that could be include functions not provided from C library like resize or move, but that is not first goal). You can follow updates from WebView branch.
Screenshot from WebView Example:
Linux (Ubuntu):
MUI-Linux
Windows:
MUI-Win

@fairking
Copy link
Author

fairking commented Nov 16, 2022

Cool. Thank you. 👍

As I can see there are two features required (if not already there) as a minimum to get hybrid app working:

  1. Local assets (js, css, images) consumed by WebView, something like wwwroot
  2. V <-> JS communication (v.call_js({ json } and js.call_v({ json }))

I will test it this week.

@malisipi
Copy link
Owner

After latest commit at webview branch

Local assets (js, css, images) consumed by WebView, something like wwwroot

You can load html files with load_html_file function.
Limitations:

  • It uses file protocol instead of localhost. It could make harder developing applications because of CORS and/or other policies.
  • If you use / inside path, that will refer to / (C:/ for Windows). And your assets will not load. You can use ./ inside all paths to solve it.

V <-> JS communication (v.call_js({ json } and js.call_v({ json }))

  • You can run JavaScript code inside webview, but if you want to get result of code, you must to bind a function into webview (ex. my_return) and run the code (ex. webview.eval("my_return(2+4);"))
  • You can not call V function directly from JavaScript (that needed by security reasons). But you can bind V function as JavaScript function (ex. important_v_fn) and you can call them from JavaScript code. (ex. important_v_fn("Param1","Param2").then(console.log))

You can browse example code at webview branch.

@fairking
Copy link
Author

fairking commented Nov 16, 2022

👍 I am going to test it now.

You can not call V function directly from JavaScript (that needed by security reasons).

Every hybrid app has such ability. It is not like js can eval any v code in the backend. It is just some kind of a channel to get/post information from/to the backend, same as it was a http request. Eg.:

in v:

app_data.webview.register_handler('my_handler', handler) // registers a `my_handler` method in webview

fn handler(message string) ?string {
    request := deserialize<ServiceRequest>(message)
    mut result = ''
    if request.route.starts_with('/Articles') {
        srv := ArticleService{ db }
        if request.route.starts_with('/Articles/Get') {
           params := deserialize<GetArticleQuery>(request.body)
           result = serialize<ArticleModel>(srv.get(params)?) or { error('could not execute service $data.route') }
        } else {
            error('could not find route $data.route')
        }
    } else {
        error('could not find route $data.route')
    }
    return result
}

// somewhere in my code

struct ServiceRequest {
pub:
    route string
    body string
}

module articles

struct GetArticleQuery {
pub:
    id int
}

struct ArticleModel {
pub:
    id int
    title string
    text string
}

struct ArticleService {
    db DatabaseService
}

pub fn (a ArticleService) get(q GetArticleQuery) ArticleModel {
    return a.db.query<ArticleModel>(select * from articles where id = $q.id)
}

and then js:

<script>
  function load_article(article_id) {
      call_v("my_handler", "{ route: '/Articles/Get', body: { id: article_id } }") // returns Promise
          .then(result => {
              article_page.form = result.data;
          }).catch(error => {
             alert("Error: " + error);
          });
  }
</script>

Same approach with Scan Barcode example. You can even build a file explorer in this way.

You can look at this as an example: https://tauri.app/v1/guides/features/command

@fairking
Copy link
Author

fairking commented Nov 16, 2022

It uses file protocol instead of localhost

👍
yes, no localhost, it will be over-complicated and not native.

If you use / inside path, that will refer to / (C:/ for Windows).

I guess the base path can be used. Or as an alternative some protocol mui://my_script.js pointing to the www root folder required.

@malisipi
Copy link
Owner

I made a basic Articles example. This is not use any db or anything but it's good to explain V-JavaScript inter-op. Also this example written by vanilla JavaScript but could be used Vue.JS or another JavaScript library to make the application more powerful.

You can browse the code.

1
2

@fairking
Copy link
Author

fairking commented Nov 16, 2022

You can browse the code.

I tried to to run the webview example but I am getting an error "cannot import module "malisipi.mui.webview" (not found)"
Do you know how to solve it?

btw, before I have run: v install https://github.com/malisipi/mui.

@malisipi
Copy link
Owner

malisipi commented Nov 16, 2022

Do you know how to solve it?

v installs default branch so you must install yourself,
cd ~/.vmodules/malisipi
rm -rdf mui
git clone https://github.com/malisipi/mui --branch webview
Also you must need to compile webview.o before use module.
So open terminal in ~/.vmodules/malisipi/mui/webview and run build_webview_for_<your_os>

PS: Also if you use Windows, compiling example is could be too challenging.
You need a few h file from W10 SDK. But you can not find/download without downloading entire SDK from Microsoft.
You must to set set W10_SDK=/path/to/sdk before compiling webview.o.
Also you need to have gcc to compile. TCC won't compile the library causes error that i can not understand why. So you need to gcc. For using gcc, v -cc gcc ...

@fairking
Copy link
Author

fairking commented Nov 16, 2022

Maybe I have missed something, but I got the following error:

C:\Code\mui\examples\webview\webview_example>v -cc gcc run webview_example.v
C:\Users\Admin\.vmodules\malisipi\mui\webview\webview.obj not found, building it (with msvc)...
builder error: MSVC cannot link against a dll (`#flag -l WebView2Loader.dll`)

C:\Code\mui\examples\webview\webview_example>

this is how I built the webview (copied from my terminal):

C:\Users\Admin\.vmodules\malisipi\mui\webview>set W10_SDK=C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0

C:\Users\Admin\.vmodules\malisipi\mui\webview>PATH %PATH%;C:\mingw64\bin

C:\Users\Admin\.vmodules\malisipi\mui\webview>build_webview_for_windows

C:\Users\Admin\.vmodules\malisipi\mui\webview>g++ -c C:\Users\Admin/.vmodules/malisipi/mui/webview/webview/webview.cc -lstdc++ -shared -static -std=c++17 -I C:\Users\Admin/.vmodules/malisipi/mui/webview/webview2/build/native/include -I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0/winrt/" -lWebView2Loader.dll -lole32 -lshell32 -lshlwapi -luser32 -o C:\Users\Admin/.vmodules/malisipi/mui/webview/webview.o

C:\Users\Admin\.vmodules\malisipi\mui\webview>

image

@malisipi
Copy link
Owner

malisipi commented Nov 17, 2022

Looks like webview.o created correctly.
But i guess v can not found gcc and so tried to use msvc, and failed. Did you add gcc to path before compiling v-example.
If that not works, please send v -showcc -cc gcc ... output. This output have infos like which compiler used or which flags used while compiling.
Also, if you see a error like that while running application. Code execution cannot continue because WebView2Loader.dll was not found. Reinstalling the program may fix this problem.. This is normal, you must copy ~/.vmodules/webview/webview2/runtimes/win-<your_arch>/native/WebView2Loader.dll into your app directory.

@fairking
Copy link
Author

fairking commented Nov 17, 2022

These what I have:

C:\Code\mui\examples\webview>gcc
'gcc' is not recognized as an internal or external command,
operable program or batch file.

C:\Code\mui\examples\webview>set W10_SDK=C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0

C:\Code\mui\examples\webview>PATH %PATH%;C:\mingw64\bin

C:\Code\mui\examples\webview>gcc
gcc: fatal error: no input files
compilation terminated.

C:\Code\mui\examples\webview>v -showcc -cc gcc run webview_example.v
v expects that `webview_example.v` exists, but it does not

C:\Code\mui\examples\webview>cd webview_example

C:\Code\mui\examples\webview\webview_example>v -showcc -cc gcc run webview_example.v
> C compiler cmd: "gcc" "@C:\Users\Admin\AppData\Local\Temp\v_0\webview_example.9499648542930297532.tmp.c.rsp"
> C compiler response file "C:\Users\Admin\AppData\Local\Temp\v_0\webview_example.9499648542930297532.tmp.c.rsp":
  -std=c99 -D_DEFAULT_SOURCE "C:\\Users\\Admin\\.vmodules\\cache\\28\\281ce86301abbcad64e66a688c6fcec7.module.builtin.o" "C:\\Users\\Admin\\.vmodules\\cache\\7e\\7e3c4e2c5f7170f871dbb70ff5415fae.module.json.o" "C:\\Users\\Admin\\.vmodules\\cache\\9d\\9d6a7c3263b5e61122c2cef48f3047cb.module.stbi.o" "C:\\Users\\Admin\\.vmodules\\cache\\2a\\2a1e94ac4ac24b07238d4d186bdbed9b.module.malisipi.mui.webview.o" -o "C:\\Code\\mui\\examples\\webview\\webview_example\\webview_example.exe" -Wl,-stack=16777216 -Werror=implicit-function-declaration -D GC_NOT_DLL=1 -D GC_WIN32_THREADS=1 -D GC_BUILTIN_ATOMIC=1 -D GC_THREADS=1 -D SOKOL_GLCORE33 -D SOKOL_NO_ENTRY -D SOKOL_WIN32_FORCE_MAIN -I "C:\\Code\\v\\thirdparty\\libgc\\include" -I "C:\\Code\\v\\thirdparty\\cJSON" -I "C:\\Code\\v\\thirdparty\\stb_image" -I "C:\\Code\\v\\thirdparty\\fontstash" -I "C:\\Code\\v\\thirdparty\\sokol" -I "C:\\Code\\v\\thirdparty\\sokol\\util" -L "C:\\Users\\Admin\\.vmodules\\malisipi\\mui\\webview\\webview2\\build\\native\\x64" -I "C:\\Users\\Admin\\.vmodules\\malisipi\\mui\\webview\\webview2\\build\\native\\include" -static -I "C:\\Code\\mui\\examples\\webview\\webview_example\\webview\\" -I "C:\\Code\\v\\thirdparty\\vschannel" C:\\Users\\Admin\\.vmodules\\malisipi\\mui/tinyfiledialogs/tinyfiledialogs.c "C:\\Users\\Admin\\AppData\\Local\\Temp\\v_0\\webview_example.9499648542930297532.tmp.c" -municode -ldbghelp -lgdi32 -ladvapi32 -lWebView2Loader.dll -lole32 -lshell32 -lshlwapi -luser32 -lstdc++ -lgdi32 -lws2_32 -lcrypt32 -lsecur32 -luser32 -lurlmon -lcomdlg32

C:\Code\mui\examples\webview\webview_example>v -cc gcc run webview_example.v

C:\Code\mui\examples\webview\webview_example>

On executing the v -showcc -cc gcc run webview_example.v or v -cc gcc run webview_example.v nothing happens. No window appears. Should I need to do anything else?

But these time that error disappeared.

@malisipi
Copy link
Owner

Your executables created correctly according V compiler logs, so should work fine and i can not recognize the bug.

  • Does works Articles example? (~/.vmodules/malisipi/mui/examples/webview/articles.v)
  • Does works MUI example? (you can try compiling ~/.vmodules/malisipi/mui/examples/demo.v)

If first example not works, you could be not have Microsoft Edge Webview 2 Runtime (but i guess you are using Windows 11 (according your uploaded folder photo) and this runtime come pre-installed.)

If second example not works, your OpenGL version not compatible with MUI.

@fairking
Copy link
Author

Same with articles.v, no window, compilation goes through silently.

For demo.v I have the following error:

Microsoft Windows [Version 10.0.22000.1165]
(c) Microsoft Corporation. All rights reserved.

C:\Code\mui\examples>v run demo.v
builder error: 'commdlg.h' not found

C:\Code\mui\examples>

@malisipi
Copy link
Owner

malisipi commented Nov 17, 2022

Same with articles.v, no window, compilation goes through silently.

If a executable was created, can you upload?

v run demo.v

I can reproduce the bug, i will fix it.

@fairking
Copy link
Author

fairking commented Nov 17, 2022

Ok, I get the demo app working with the command v -cc gcc run demo.v. It took about a minute to run. However if I try to add a new record it says "Successfully Completed", but no record shown on the list after that. Tried it several times.

image

However the v -cc gcc run articles.v is not working (no errors, empty terminal).

Do we have any error logs somewhere?

@malisipi
Copy link
Owner

malisipi commented Nov 17, 2022

Do we have any error logs somewhere?

Sorry, i haven't created any log system.

However the v -cc gcc run articles.v is not working.

Does this command create any executable? If yes, can you attach executable in your comment.

@fairking
Copy link
Author

Does this command create any executable? If yes, can you attach executable in your comment.

Cannot see anything in the current folder:
image

Or is it located somewhere else?

@malisipi
Copy link
Owner

malisipi commented Nov 17, 2022

Run without run keyword v -cc gcc articles.v

@malisipi
Copy link
Owner

malisipi commented Nov 17, 2022

Also the issue is about like webview, can you check out you have microsoft webview2 runtime. If you not, you can download from Microsoft

@fairking
Copy link
Author

ok, the v -cc gcc articles.v did the work. please find it here.

@fairking
Copy link
Author

Also the issue is about like webview, can you check out you have microsoft webview2 runtime. If you not, you can download from Microsoft

image

@malisipi
Copy link
Owner

malisipi commented Nov 17, 2022

ok, the v -cc gcc articles.v did the work. please find it here.

This example works (your compiled) on me. Did you copy WebView2Loader.dll to your app directory?

You should copy ~/.vmodules/webview/webview2/runtimes/win-<your_arch>/native/WebView2Loader.dll into your app directory.

@fairking
Copy link
Author

You should copy WebView2Loader.dll into your app directory.

Thanks, now everything is working. Would be nice to write a doc how to build the mui app. 👍

@malisipi
Copy link
Owner

Would be nice to write a doc how to build the mui app.

You're right. I will write a documentation before merge webview with main branch.

@fairking
Copy link
Author

fairking commented Nov 17, 2022

I will write a vuejs example and see how it works by the end of this week. I can fork your branch, so it will come back as an example if you are happy with that. Later we can automate the build a little bit, so no hassle with installing and setting up the environment for build.

@malisipi
Copy link
Owner

I will write a vuejs example and see how it works by the end of this week. I can fork your branch, so it will come back as an example if you are happy with that.

Thanks for dealing with the repo. I will be grateful for your pull request. If you have a problem with project, you can ask here.

Later we can automate the build a little bit, so no hassle with installing and setting up the environment for build.

That is good idea, that could be hard to make compatible with all operating systems. However it is worth to make.

@malisipi malisipi linked a pull request Nov 18, 2022 that will close this issue
@fairking
Copy link
Author

So as far as I can see the custom uri scheme is required in order to serve local files. This will be the best and most secure approach accessing local asset files by the webview. I will keep working on it https://github.com/fairking/mui/tree/vuejs_app_example

Also I asked WebView guys why it is not available out of the box: webview/webview#851

@malisipi
Copy link
Owner

malisipi commented Nov 19, 2022

I understand why we need custom uri scheme really. I will watch webview/webview#851 for now. If their response is positive, i will wait their implementation this feature. Else, i will patch webview again to get that.

Also, your example looks good especially for peoples wants use web & v together.

@malisipi malisipi reopened this Nov 19, 2022
@Wajinn
Copy link

Wajinn commented Nov 29, 2022

Looks to be a great project. Hope that it does not fall off from being sidetracked or distracted with webview or porting of PWA, and can maintain a balance. Not everyone is coming from or wants to go in that direction.

@fairking
Copy link
Author

fairking commented Nov 29, 2022

Looks to be a great project. Hope that it does not fall off from being sidetracked or distracted with webview or porting of PWA, and can maintain a balance. Not everyone is coming from or wants to go in that direction.

As a senior backend developer I can understand you. But it depends what are you going to build. Simple apps, tools, utils are best with native UI, no doubt. However more complex and constantly changing frontend requires html/js.
If you want to find a good graphic designer or UX guy, they are all HTML/JS duck-typing guys. Why? Because they are artists, they don't like strong type languages. And during my long career I had to accept that :-)

Just simple example. As a subscription manager and join membership app (web and android) we have to build a new unique app for every new customer (1 month of work). Please note, the API (backend) had minimal or no changes. If we go with the native approach, it will take at least 3 months instead. The second issue, we didn't have backend developers too much in which case native UI requires.

But if it comes to scan barcode, NFC, camera, files and many other features the webview lack of, the native tools are very useful as well in such situations.

@Wajinn
Copy link

Wajinn commented Nov 29, 2022

...depends what are you going to build. Simple apps, tools, utils are best with native UI, no doubt. However more complex and constantly changing frontend requires html/js.

First, let us acknowledge that it's fantastic what Malisipi has done, and that his project can accommodate both.

It is of course a matter of perspective, as to which people may prefer, native UI or HTML/JS. There are those that prefer native UIs too. It's not just one over the other. I'm just pointing out that originally it appears the project was not about webview or the porting of PWA, but native UIs and has various related items on the to-do list. Luckily, Malisipi was so far along that he could do both, and its great that it has gone that way.

@dumblob
Copy link

dumblob commented Feb 6, 2023

Yes, I agree with what is going on here and the direction MUI is generally taking. Much appreciated @malisipi !

I came here only to emphasize that the most successful apps from small dev teams use a hybrid strategy - they build part of the GUI (yes, not just backend!) in native technology (here either Android Java or V bindings to the Android Java API) and the other part from one (or multiple!) webviews.

Note the native GUI parts can arbitrarily overlap with webviews (with transparency both ways, events coming through both ways, and other goodies) and thus one can construct a really natively-feeling apps (relatively low latency, theme-aware, platform-specific gesture recognition, ...) while maintaining the rapid development pace.

@malisipi malisipi pinned this issue May 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants