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

iOS support #11

Closed
wusyong opened this issue Feb 4, 2021 · 4 comments
Closed

iOS support #11

wusyong opened this issue Feb 4, 2021 · 4 comments
Labels
help wanted Help is requested to fix this issue platform: iOS type: feature request

Comments

@wusyong
Copy link
Member

wusyong commented Feb 4, 2021

Perhaps it's possible with the help of this crate: https://github.com/ryanmcgrath/cacao

@wusyong
Copy link
Member Author

wusyong commented Feb 12, 2021

Here are some good news and bad news.
The good news is it can be built. I use cargo mobile to set up a basic winit workplace to explore it.
Once the project is generated. Enter cargo apple open can open it via xcode.
Be sure to add WebKit framework as follows:
圖片
Connect your device and run it on xcode or cargo apple run will run the app on your phone.

The bad news is it will crash whenever I touch the screen. The error message is like below:

2021-02-12 21:53:58.252035+0800 wryyyyyyyyy[38573:2578915] *** Assertion failure in -[UIGestureGraphEdge initWithLabel:sourceNode:targetNode:directed:], UIGestureGraphEdge.m:24
fatal runtime error: Rust cannot catch foreign exceptions
Process 38573 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00000001bcb0a414 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`__pthread_kill:
->  0x1bcb0a414 <+8>:  b.lo   0x1bcb0a434               ; <+40>
    0x1bcb0a418 <+12>: pacibsp
    0x1bcb0a41c <+16>: stp    x29, x30, [sp, #-0x10]!
    0x1bcb0a420 <+20>: mov    x29, sp
Target 0: (wryyyyyyyyy) stopped.

Here's my minimum example for anyone who want to try it:

#[macro_use]
extern crate objc;

use mobile_entry_point::mobile_entry_point;
use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    platform::ios::{WindowBuilderExtIOS, WindowExtIOS, ScreenEdge},
    window::WindowBuilder,
};
use std::ffi::CString;
use cocoa::base::id;
use core_graphics::geometry::{CGPoint, CGRect, CGSize};

#[cfg(not(target_os = "android"))]
fn init_logging() {
    simple_logger::SimpleLogger::new().init().unwrap();
}

#[mobile_entry_point]
fn main() {
    unsafe {
        init_logging();
        let event_loop = EventLoop::new();
        let window = WindowBuilder::new()
            .with_preferred_screen_edges_deferring_system_gestures(ScreenEdge::ALL)
            .build(&event_loop)
            .unwrap();
        
        let wkwebview = class!(WKWebView);
        let webview: id = msg_send![wkwebview, alloc];

        let rect = CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(400., 900.));
        let () = msg_send![webview, initWithFrame:rect];

        let view = window.ui_view() as id;
        let () = msg_send![view, addSubview:webview];
        
        event_loop.run(move |event, _, control_flow| {
            *control_flow = ControlFlow::Wait;
            println!("{:?}", event);

            match event {
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    window_id,
                } if window_id == window.id() => *control_flow = ControlFlow::Exit,
                _ => (),
            }
        });
    }
}

@wusyong wusyong added the help wanted Help is requested to fix this issue label Feb 12, 2021
@lemarier
Copy link
Member

I got it to works ;)

#[macro_use]
extern crate objc;

use std::{slice, str, os::raw::c_char};
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
use cocoa::base::id;
use mobile_entry_point::mobile_entry_point;
use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    platform::ios::{WindowBuilderExtIOS, WindowExtIOS, ScreenEdge},
    window::WindowBuilder,
};
use objc::runtime::Object;
use objc_id::Id;

#[cfg(target_os = "android")]
fn init_logging() {
    android_logger::init_once(
        android_logger::Config::default()
            .with_min_level(log::Level::Trace)
            .with_tag("Test"),
    );
}

#[cfg(not(target_os = "android"))]
fn init_logging() {
    simple_logger::SimpleLogger::new().init().unwrap();
}

#[mobile_entry_point]
fn main() {
    init_logging();
    let event_loop = EventLoop::new();

    let wkwebview = class!(WKWebView);
    let webview: id = unsafe { msg_send![wkwebview, alloc] };
    

    let window = WindowBuilder::new()
        .with_preferred_screen_edges_deferring_system_gestures(ScreenEdge::ALL)
        .build(&event_loop)
        .unwrap();

    let rect = CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(400., 900.));
    let () = unsafe { msg_send![webview, initWithFrame:rect] };

    let view = window.ui_view() as id;
    let () = unsafe { msg_send![view, addSubview:webview] };

    let sample_html_to_load = r#"
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      </head>
      <body>
        <h1>Welcome to WRY!</h1>
        <a href="/hello.html">Link</a>
        <script type="text/javascript" src="/hello.js"></script>
      </body>
    </html>"#;

    // load content
    let empty: id = unsafe { msg_send![class!(NSURL), URLWithString: NSString::new("")] };
    let () = unsafe { msg_send![webview, loadHTMLString:NSString::new(sample_html_to_load) baseURL:empty] };

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;
        println!("{:?}", event);

        match event {
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                window_id,
            } if window_id == window.id() => *control_flow = ControlFlow::Exit,
            Event::MainEventsCleared => {
                window.request_redraw();
            }
            _ => (),
        }
    });
}

const UTF8_ENCODING: usize = 4;

struct NSString(Id<Object>);

impl NSString {
  fn new(s: &str) -> Self {
    // Safety: objc runtime calls are unsafe
    NSString(unsafe {
      let nsstring: id = msg_send![class!(NSString), alloc];
      Id::from_ptr(
        msg_send![nsstring, initWithBytes:s.as_ptr() length:s.len() encoding:UTF8_ENCODING],
      )
    })
  }

  fn to_str(&self) -> &str {
    unsafe {
      let bytes: *const c_char = msg_send![self.0, UTF8String];
      let len = msg_send![self.0, lengthOfBytesUsingEncoding: UTF8_ENCODING];
      let bytes = slice::from_raw_parts(bytes as *const u8, len);
      str::from_utf8_unchecked(bytes)
    }
  }
}

@simlay
Copy link

simlay commented Apr 22, 2021

Note:

    let rect = CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(400., 900.));
    let () = unsafe { msg_send![webview, initWithFrame:rect] };

To make this fullscreen, you'll want something like UIScreen::mainScreen().bounds(); but without the uikit-sys crate because I never finished a last rust-bindgen PR. I'd link to the docs src but it kinda crashes my browser due to how long the file is haha.

I think you want like:

let screen = unsafe { msg_send![UIScreen, mainScreen] };
let rect = unsafe { msg_send![rect bounds] };
let () = unsafe { msg_send![webview, initWithFrame:rect] };

@wusyong
Copy link
Member Author

wusyong commented Jun 15, 2021

Closed by #238

@wusyong wusyong closed this as completed Jun 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Help is requested to fix this issue platform: iOS type: feature request
Projects
None yet
Development

No branches or pull requests

3 participants