Skip to content

Commit

Permalink
feat(cli): add android dev and ios dev commands (#4982)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Aug 20, 2022
1 parent f445f37 commit 6f06150
Show file tree
Hide file tree
Showing 47 changed files with 1,629 additions and 691 deletions.
6 changes: 6 additions & 0 deletions .changes/cli-mobile-dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"cli.rs": minor
"cli.js": minor
---

Added `android dev` and `ios dev` commands.
5 changes: 5 additions & 0 deletions .changes/codegen-mobile-devurl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri-codegen": patch
---

Change `devPath` URL to use the local IP address on iOS and Android.
5 changes: 5 additions & 0 deletions .changes/dev-proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": major
---

**Breaking change:** Use the custom protocol as a proxy to the development server on all platforms except Linux.
2 changes: 2 additions & 0 deletions core/tauri-codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ semver = "1"
ico = "0.1"
png = "0.17"
json-patch = "0.2"
local-ip-address = "0.4"
url = "2"

[target."cfg(target_os = \"macos\")".dependencies]
plist = "1"
Expand Down
19 changes: 18 additions & 1 deletion core/tauri-codegen/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ enum Target {
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
let ContextData {
dev,
config,
mut config,
config_parent,
root,
} = data;
Expand Down Expand Up @@ -155,6 +155,23 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
panic!("unknown codegen target");
};

if dev && (target == Target::Ios || target == Target::Android) {
if let AppUrl::Url(WindowUrl::External(url)) = &mut config.build.dev_path {
let localhost = match url.host() {
Some(url::Host::Domain(d)) => d == "localhost",
Some(url::Host::Ipv4(i)) => {
i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED
}
_ => false,
};
if localhost {
if let Ok(ip) = local_ip_address::local_ip() {
url.set_host(Some(&ip.to_string())).unwrap();
}
}
}
}

let mut options = AssetOptions::new(config.tauri.pattern.clone())
.freeze_prototype(config.tauri.security.freeze_prototype)
.dangerous_disable_asset_csp_modification(
Expand Down
1 change: 1 addition & 0 deletions core/tauri-codegen/src/embedded_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ impl ToTokens for EmbeddedAssets {

// we expect phf related items to be in path when generating the path code
tokens.append_all(quote! {{
#[allow(unused_imports)]
use ::tauri::utils::assets::{CspHash, EmbeddedAssets, phf, phf::phf_map};
EmbeddedAssets::new(phf_map! { #assets }, &[#global_hashes], phf_map! { #html_hashes })
}});
Expand Down
2 changes: 1 addition & 1 deletion core/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2364,7 +2364,7 @@ fn default_dialog() -> bool {
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct IosConfig {
/// The development team. This value is required for iOS development because code signing is enforced.
/// The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.
/// The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.
#[serde(alias = "development-team")]
pub development_team: Option<String>,
}
Expand Down
4 changes: 2 additions & 2 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ base64 = { version = "0.13", optional = true }
clap = { version = "3", optional = true }
reqwest = { version = "0.11", features = [ "json", "stream" ], optional = true }
bytes = { version = "1", features = [ "serde" ], optional = true }
attohttpc = { version = "0.20", features = [ "compress", "json", "form" ], optional = true }
attohttpc = { version = "0.20", features = [ "compress", "json", "form" ] }
open = { version = "3.0", optional = true }
shared_child = { version = "1.0", optional = true }
os_pipe = { version = "1.0", optional = true }
Expand Down Expand Up @@ -146,7 +146,7 @@ updater = [
"dialog-ask",
"fs-extract-api"
]
http-api = [ "attohttpc" ]
http-api = [ ]
http-multipart = [ "attohttpc/multipart-form", "reqwest/multipart" ]
shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ]
fs-extract-api = [ "zip" ]
Expand Down
89 changes: 59 additions & 30 deletions core/tauri/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,6 @@ fn set_csp<R: Runtime>(
Csp::DirectiveMap(csp).to_string()
}

#[cfg(target_os = "linux")]
fn set_html_csp(html: &str, csp: &str) -> String {
html.replacen(tauri_utils::html::CSP_TOKEN, csp, 1)
}

// inspired by https://github.com/rust-lang/rust/blob/1be5c8f90912c446ecbdc405cbc4a89f9acd20fd/library/alloc/src/str.rs#L260-L297
fn replace_with_callback<F: FnMut() -> String>(
original: &str,
Expand Down Expand Up @@ -374,7 +369,13 @@ impl<R: Runtime> WindowManager<R> {
/// Get the origin as it will be seen in the webview.
fn get_browser_origin(&self) -> String {
match self.base_path() {
AppUrl::Url(WindowUrl::External(url)) => url.origin().ascii_serialization(),
AppUrl::Url(WindowUrl::External(url)) => {
if cfg!(dev) && !cfg!(target_os = "linux") {
format_real_schema("tauri")
} else {
url.origin().ascii_serialization()
}
}
_ => format_real_schema("tauri"),
}
}
Expand Down Expand Up @@ -820,8 +821,12 @@ impl<R: Runtime> WindowManager<R> {
>,
) -> Box<dyn Fn(&HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> + Send + Sync>
{
#[cfg(dev)]
let url = self.get_url().into_owned();
#[cfg(not(dev))]
let manager = self.clone();
let window_origin = window_origin.to_string();

Box::new(move |request| {
let path = request
.uri()
Expand All @@ -834,32 +839,47 @@ impl<R: Runtime> WindowManager<R> {
// the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows
// where `$P` is not `localhost/*`
.unwrap_or_else(|| "".to_string());
let asset = manager.get_asset(path)?;
let mut builder = HttpResponseBuilder::new()
.header("Access-Control-Allow-Origin", &window_origin)
.mimetype(&asset.mime_type);
if let Some(csp) = &asset.csp_header {
builder = builder.header("Content-Security-Policy", csp);
}
let mut response = builder.body(asset.bytes)?;
if let Some(handler) = &web_resource_request_handler {
handler(request, &mut response);

// if it's an HTML file, we need to set the CSP meta tag on Linux
#[cfg(target_os = "linux")]
if let Some(response_csp) = response.headers().get("Content-Security-Policy") {
let response_csp = String::from_utf8_lossy(response_csp.as_bytes());
let body = set_html_csp(&String::from_utf8_lossy(response.body()), &response_csp);
*response.body_mut() = body.as_bytes().to_vec();
}
} else {
#[cfg(target_os = "linux")]
{
if let Some(csp) = &asset.csp_header {
let body = set_html_csp(&String::from_utf8_lossy(response.body()), csp);
*response.body_mut() = body.as_bytes().to_vec();
let mut builder =
HttpResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin);

#[cfg(dev)]
let mut response = {
let mut url = url.clone();
url.set_path(&path);
match attohttpc::get(url.as_str()).send() {
Ok(r) => {
for (name, value) in r.headers() {
builder = builder.header(name, value);
}
builder.status(r.status()).body(r.bytes()?)?
}
Err(e) => {
debug_eprintln!("Failed to request {}: {}", url.path(), e);
return Err(Box::new(e));
}
}
};

#[cfg(not(dev))]
let mut response = {
let asset = manager.get_asset(path)?;
builder = builder.mimetype(&asset.mime_type);
if let Some(csp) = &asset.csp_header {
builder = builder.header("Content-Security-Policy", csp);
}
builder.body(asset.bytes)?
};
if let Some(handler) = &web_resource_request_handler {
handler(request, &mut response);
}
// if it's an HTML file, we need to set the CSP meta tag on Linux
#[cfg(all(not(dev), target_os = "linux"))]
if let Some(response_csp) = response.headers().get("Content-Security-Policy") {
let response_csp = String::from_utf8_lossy(response_csp.as_bytes());
let html = String::from_utf8_lossy(response.body());
let body = html.replacen(tauri_utils::html::CSP_TOKEN, &response_csp, 1);
*response.body_mut() = body.as_bytes().to_vec();
}
Ok(response)
})
Expand Down Expand Up @@ -1061,7 +1081,10 @@ impl<R: Runtime> WindowManager<R> {
#[allow(unused_mut)] // mut url only for the data-url parsing
let (is_local, mut url) = match &pending.webview_attributes.url {
WindowUrl::App(path) => {
#[cfg(target_os = "linux")]
let url = self.get_url();
#[cfg(not(target_os = "linux"))]
let url: Cow<'_, Url> = Cow::Owned(Url::parse("tauri://localhost").unwrap());
(
true,
// ignore "index.html" just to simplify the url
Expand All @@ -1078,7 +1101,13 @@ impl<R: Runtime> WindowManager<R> {
}
WindowUrl::External(url) => {
let config_url = self.get_url();
(config_url.make_relative(url).is_some(), url.clone())
let is_local = config_url.make_relative(url).is_some();
let mut url = url.clone();
if is_local && !cfg!(target_os = "linux") {
url.set_scheme("tauri").unwrap();
url.set_host(Some("localhost")).unwrap();
}
(is_local, url)
}
_ => unimplemented!(),
};
Expand Down
Loading

0 comments on commit 6f06150

Please sign in to comment.