iOS framework for making Turbo native apps

Overview

Turbo Native for iOS

Note: The Hotwire frameworks are presented in beta form. We're using them all in production with HEY, but expect that significant changes might be made in response to early feedback.


Build high-fidelity hybrid apps with native navigation and a single shared web view. Turbo Native for iOS provides the tooling to wrap your Turbo 7-enabled web app in a native iOS shell. It manages a single WKWebView instance across multiple view controllers, giving you native navigation UI with all the client-side performance benefits of Turbo.

Features

  • Deliver fast, efficient hybrid apps. Avoid reloading JavaScript and CSS. Save memory by sharing one WKWebView.
  • Reuse mobile web views across platforms. Create your views once, on the server, in HTML. Deploy them to iOS, Android, and mobile browsers simultaneously. Ship new features without waiting on App Store approval.
  • Enhance web views with native UI. Navigate web views using native patterns. Augment web UI with native controls.
  • Produce large apps with small teams. Achieve baseline HTML coverage for free. Upgrade to native views as needed.

Requirements

Turbo iOS is written in Swift 5.3 and requires iOS 12 or higher, but we'll most likely drop iOS 12 in the near future. It supports web apps using either Turbo 7 or Turbolinks 5. The Turbo iOS framework has no dependencies.

Note: You should understand how Turbo works with web applications in the browser before attempting to use Turbo iOS. See the Turbo 7 documentation for details. Ensure that your web app sets the window.Turbo global variable as it's required by the native apps:

import { Turbo } from "@hotwired/turbo-rails"
window.Turbo = Turbo

Getting Started

The best way to get started with Turbo iOS to try out the demo app first to get familiar with the framework. The demo app walks you through all the basic Turbo flows as well as some advanced features. To run the demo, clone this repo and open Demo/Demo.xcworkspace in Xcode and run the Demo target. See Demo/README.md for more details about the demo. When you’re ready to start your own application, read through the rest of the documentation.

Documentation

Contributing

Turbo iOS is open-source software, freely distributable under the terms of an MIT-style license. The source code is hosted on GitHub. Development is sponsored by Basecamp.

We welcome contributions in the form of bug reports, pull requests, or thoughtful discussions in the GitHub issue tracker.

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.


Β© 2020 Basecamp, LLC

Comments
  • Form submissions with response HTML don't work

    Form submissions with response HTML don't work

    There's a potential bug in Turbo-iOS when it comes to submitting forms and rendering the response HTML.

    I notice the following flow when submitting a form in Turbo iOS:

    1. The form is submitted.
    2. The server issues a redirect upon successful form response.
    3. The redirect is followed, and the response is returned.
    4. The WebView bridge is alerted to a "Visit Proposed" event containing the response HTML.
    5. You're free to route the proposal as desired, usually ended with calling the "window.turboNative.visitLocationWithOptionsAndRestorationIdentifier" with the response HTML.

    The problem is, calling "window.turboNative.visitLocationWithOptionsAndRestorationIdentifier" containing response HTML returns the exact same page, completely ignoring the response HTML.

    If you try this in the demo app, it will work the first time and the first time only. Subsequent form submissions will continue to return the same page over and over again. This is because of a "Back Navigation" bug, where submitting the form the first time, the app thinks you hit the back button, and issues another get request.

    Walking through step by step in the Demo app, using Proxyman to see requests/responses to/from the server:

    1. Run the app
    2. Tap "Load a web page modally"
    3. Submit the form. Proxyman shows two get requests, the first from the redirect, the second from the above mentioned "Back Navigation" bug: The rendered screen is "correct" only because of the second get request.
    Screen Shot 2022-04-08 at 5 57 56 PM
    1. Hit the back button.

    2. Tap "Load a web page modally" again.

    3. Submit the form. Proxyman now shows one get request, as expected. However, the response HTML was ignored. This can be seen by comparing the time stamps of the response HTML with the time stamp rendered on screen:

    Screen Shot 2022-04-08 at 6 34 43 PM

    Walking through the "Back Navigation" bug:

    1. Run the app
    2. Tap "Load a web page modally"
    3. Place breakpoints on line 207 of "Session.swift" and line 47 of "Visit.swift".
    4. Submit the form.
    5. Breakpoint: The first time and first time only, a "visit did complete" message comes from the ScriptMessageHandler, setting the state of the Visit to complete. I have no idea why this event is sent, but it's the reason for the Back Navigation bug. Hit continue.
    6. Breakpoint Session's visitableViewWillAppear activates, and ends up in the "Navigating Backward" path, issuing another visit (GET request 2). It is this second GET request that gives the appearance of "correct" behavior when submitting the form the first time.
    Screen Shot 2022-04-08 at 6 09 48 PM

    Subsequent form submissions skip step 5, so on visitableViewWillAppear, it ends up in the "Navigating Forward" path, issuing only one GET request as it should. But because the response HTML is ignored by Turbo, it re-renders the same screen again and again. Refreshing the page renders the correct response.

    I believe this is bug in turbo.js, but I can't say for sure because I can't set breakpoints in the javascript code in Xcode. 😒 I need to do more research into how Turbo itself works.

    My current workaround is to set the response HTML to nil before passing the proposal to "window.turboNative.visitLocationWithOptionsAndRestorationIdentifier". This seems to trick Turbo into not knowing that the request came a redirected form submission, so it issues another GET request. Submitting two GET requests per form submission is not ideal, though.

    Screen Shot 2022-04-08 at 6 31 15 PM
    opened by izaguirrejoe 17
  • Delegate callback when web view process terminates

    Delegate callback when web view process terminates

    This is an intermediary solution for #50.

    In summary, WKWebView can terminate under certain circumstances when in the background. When a Turbo-powered app is re-opened after this occurs the app can be left in a weird state.

    This PR adds a new delegate callback, sessionWebViewProcessDidTerminate(_:), that developers can hook into to do what they see fit. @tony42, for example, is resetting the state of their app. I'm following that same approach.


    I'm not sure if/how this can be handled better inside of this library short of iOS 16 fixing the issue entirely. So this feels like the next best approach as it puts the power back in the developer's hands.

    opened by joemasilotti 14
  • Improve logging by displaying more relevant details and additional arguments

    Improve logging by displaying more relevant details and additional arguments

    The current debug logging is insufficient to understand what's going on behind the scenes and investigate issues. This is a step in the right direction.

    Previous log output:

    2022-06-30 03:29:06 +0000 - [ColdBootVisit] startVisit()
    2022-06-30 03:29:07 +0000 - [Bridge] ← pageLoaded
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitProposed
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] startVisit()
    2022-06-30 03:29:09 +0000 - [Bridge] β†’ window.turboNative.visitLocationWithOptionsAndRestorationIdentifier
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitStarted
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] webView(_:didStartVisitWithIdentifier:hasCachedSnapshot:)
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitRequestStarted
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] webView(_:didStartRequestForVisitWithIdentifier:date:)
    2022-06-30 03:29:09 +0000 - [Bridge] = window.turboNative.visitLocationWithOptionsAndRestorationIdentifier evaluation complete
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitRequestFinished
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] webView(_:didFinishRequestForVisitWithIdentifier:date:)
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitRequestCompleted
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] webView(_:didCompleteRequestForVisitWithIdentifier:)
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitCompleted
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] webView(_:didCompleteVisitWithIdentifier:restorationIdentifier:)
    2022-06-30 03:29:09 +0000 - [Bridge] ← visitRendered
    2022-06-30 03:29:09 +0000 - [JavaScriptVisit] webView(_:didRenderForVisitWithIdentifier:)
    2022-06-30 03:29:11 +0000 - [JavaScriptVisit] startVisit()
    2022-06-30 03:29:11 +0000 - [Bridge] β†’ window.turboNative.visitLocationWithOptionsAndRestorationIdentifier
    2022-06-30 03:29:11 +0000 - [Bridge] ← visitStarted
    2022-06-30 03:29:11 +0000 - [JavaScriptVisit] webView(_:didStartVisitWithIdentifier:hasCachedSnapshot:)
    2022-06-30 03:29:11 +0000 - [Bridge] = window.turboNative.visitLocationWithOptionsAndRestorationIdentifier evaluation complete
    2022-06-30 03:29:11 +0000 - [Bridge] ← visitCompleted
    2022-06-30 03:29:11 +0000 - [JavaScriptVisit] webView(_:didCompleteVisitWithIdentifier:restorationIdentifier:)
    2022-06-30 03:29:11 +0000 - [Bridge] ← visitRendered
    2022-06-30 03:29:11 +0000 - [JavaScriptVisit] webView(_:didRenderForVisitWithIdentifier:)
    

    New log output:

    2022-06-30 03:30:11 +0000 [Session] visit ["reload": false, "location": https://turbo-native-demo.glitch.me, "options": Turbo.VisitOptions(action: Turbo.VisitAction.replace, response: nil)]
    2022-06-30 03:30:11 +0000 [ColdBootVisit] startVisit https://turbo-native-demo.glitch.me [:]
    2022-06-30 03:30:12 +0000 [Bridge] ← pageLoaded ["timestamp": 1656559812055, "restorationIdentifier": 42918dac-ca08-4d87-ae88-d866b21e9e14] [:]
    2022-06-30 03:30:12 +0000 [ColdBootVisit] completeVisit https://turbo-native-demo.glitch.me [:]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitProposed ["location": https://turbo-native-demo.glitch.me/one, "options": {
        action = advance;
    }, "timestamp": 1656559818238] [:]
    2022-06-30 03:30:18 +0000 [Session] visit ["options": Turbo.VisitOptions(action: Turbo.VisitAction.advance, response: nil), "location": https://turbo-native-demo.glitch.me/one, "reload": false]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] startVisit https://turbo-native-demo.glitch.me/one [:]
    2022-06-30 03:30:18 +0000 [Bridge] β†’ window.turboNative.visitLocationWithOptionsAndRestorationIdentifier [Optional("https://turbo-native-demo.glitch.me/one"), Optional({
        action = advance;
    }), nil] [:]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitStarted ["identifier": 6cd858b3-5a0c-4695-a5b0-48597a97ea1c, "hasCachedSnapshot": 0, "timestamp": 1656559818246] [:]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] didStartVisitWithIdentifier https://turbo-native-demo.glitch.me/one ["identifier": "6cd858b3-5a0c-4695-a5b0-48597a97ea1c", "hasCachedSnapshot": false]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitRequestStarted ["identifier": 6cd858b3-5a0c-4695-a5b0-48597a97ea1c, "timestamp": 1656559818248] [:]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] didFinishRequestForVisitWithIdentifier https://turbo-native-demo.glitch.me/one ["identifier": "6cd858b3-5a0c-4695-a5b0-48597a97ea1c", "date": 2022-06-30 03:30:18 +0000]
    2022-06-30 03:30:18 +0000 [Bridge] = window.turboNative.visitLocationWithOptionsAndRestorationIdentifier evaluation complete [:]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitRequestCompleted ["identifier": 6cd858b3-5a0c-4695-a5b0-48597a97ea1c, "timestamp": 1656559818344] [:]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] didCompleteRequestForVisitWithIdentifier https://turbo-native-demo.glitch.me/one ["identifier": "6cd858b3-5a0c-4695-a5b0-48597a97ea1c"]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitRequestFinished ["identifier": 6cd858b3-5a0c-4695-a5b0-48597a97ea1c, "timestamp": 1656559818344] [:]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] didFinishRequestForVisitWithIdentifier https://turbo-native-demo.glitch.me/one ["identifier": "6cd858b3-5a0c-4695-a5b0-48597a97ea1c", "date": 2022-06-30 03:30:18 +0000]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitCompleted ["identifier": 6cd858b3-5a0c-4695-a5b0-48597a97ea1c, "restorationIdentifier": 4904de48-531e-42ec-b9e2-6ec8304108a9, "timestamp": 1656559818347] [:]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] didCompleteVisitWithIdentifier https://turbo-native-demo.glitch.me/one ["identifier": "6cd858b3-5a0c-4695-a5b0-48597a97ea1c", "restorationIdentifier": "4904de48-531e-42ec-b9e2-6ec8304108a9"]
    2022-06-30 03:30:18 +0000 [Bridge] ← visitRendered ["identifier": 6cd858b3-5a0c-4695-a5b0-48597a97ea1c, "timestamp": 1656559818366] [:]
    2022-06-30 03:30:18 +0000 [JavascriptVisit] didRenderForVisitWithIdentifier https://turbo-native-demo.glitch.me/one ["identifier": "6cd858b3-5a0c-4695-a5b0-48597a97ea1c"]
    2022-06-30 03:30:20 +0000 [Session] visit ["location": https://turbo-native-demo.glitch.me, "options": Turbo.VisitOptions(action: Turbo.VisitAction.restore, response: nil), "reload": false]
    2022-06-30 03:30:20 +0000 [JavascriptVisit] startVisit https://turbo-native-demo.glitch.me [:]
    2022-06-30 03:30:20 +0000 [Bridge] β†’ window.turboNative.visitLocationWithOptionsAndRestorationIdentifier [Optional("https://turbo-native-demo.glitch.me"), Optional({
        action = restore;
    }), Optional("42918dac-ca08-4d87-ae88-d866b21e9e14")] [:]
    2022-06-30 03:30:20 +0000 [Bridge] ← visitStarted ["identifier": a966548e-8e36-4b39-a039-e4a03ec27734, "hasCachedSnapshot": 1, "timestamp": 1656559820532] [:]
    2022-06-30 03:30:20 +0000 [JavascriptVisit] didStartVisitWithIdentifier https://turbo-native-demo.glitch.me ["hasCachedSnapshot": true, "identifier": "a966548e-8e36-4b39-a039-e4a03ec27734"]
    2022-06-30 03:30:20 +0000 [Bridge] = window.turboNative.visitLocationWithOptionsAndRestorationIdentifier evaluation complete [:]
    2022-06-30 03:30:20 +0000 [Bridge] ← visitCompleted ["restorationIdentifier": 42918dac-ca08-4d87-ae88-d866b21e9e14, "identifier": a966548e-8e36-4b39-a039-e4a03ec27734, "timestamp": 1656559820533] [:]
    2022-06-30 03:30:20 +0000 [JavascriptVisit] didCompleteVisitWithIdentifier https://turbo-native-demo.glitch.me ["restorationIdentifier": "42918dac-ca08-4d87-ae88-d866b21e9e14", "identifier": "a966548e-8e36-4b39-a039-e4a03ec27734"]
    2022-06-30 03:30:20 +0000 [Bridge] ← visitRendered ["identifier": a966548e-8e36-4b39-a039-e4a03ec27734, "timestamp": 1656559820549] [:]
    2022-06-30 03:30:20 +0000 [JavascriptVisit] didRenderForVisitWithIdentifier https://turbo-native-demo.glitch.me ["identifier": "a966548e-8e36-4b39-a039-e4a03ec27734"]
    
    opened by jayohms 7
  • Accessing Camera / GPS

    Accessing Camera / GPS

    Does turbo-ios have inbuilt APIs to request access to Camera or GPS? Or is there an expectation that users of turbo-ios will have to write additional swift code to handle such requests?

    opened by daya 7
  • Demo doesn't work against a Turbo/Rails app with importmap-rails 0.9.1

    Demo doesn't work against a Turbo/Rails app with importmap-rails 0.9.1

    The latest importmap-rails (included in Rails 7 rc1) includes es-module-shims.js version 1.3.5. This latter library creates blob URLs that the Turbo Demo app tries to visit. This causes errors with http status code 0.

    One current workaround is to fork importmap-rails and downgrade es-module-shims.js to version 1.2.0 so the backend is serving this downgraded shim to the Demo app.

    opened by hudon 7
  • WebView crashes on iOS 15

    WebView crashes on iOS 15

    I don't think this relates to Turbolinks but it likely affects all apps which run it (or some of them at leats).

    When our app is in background for some time and one opens a bunch of other apps so that the phone is low on memory, our app will get to a weird state the next time you open it. No content is shown in the webview and there is a bunch of messages in the XCode console. My understanding is that WebView runs a service which gets killed by the OS and is vital for WebView to run properly.

    I do have a workaround which requires a slight change in the Turbo library. I changed the method webViewWebContentProcessDidTerminate() in Session.swift to post a notification when the webview process terminates:

    public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
            debugLog("[Session] webViewWebContentProcessDidTerminate")
            NotificationCenter.default.post(name: Notification.Name("webViewWebContentProcessDidTerminate"), object: nil)
        }
    

    Then I basically restart our App with a fresh WebView. It's not ideal as I loose the app state but it's the best I could come up with.

    I'd like to ask two things:

    • do you have the same issue with other Turbo apps or is it just us?
    • would it be possible to add my change to the official Turbo code? I think it might be useful for other developers as well.

    I've found this forum post which is likely exactly the issue we have:

    https://developer.apple.com/forums/thread/684843

    Please note that this is not happening on iOS 14.

    Thanks a lot

    Tony

    opened by tony42 7
  • Demo crashes when rendering native numbers view in modal

    Demo crashes when rendering native numbers view in modal

    We noticed this issue because we'd like to access the TurboNavigationController inside of a native modal in order to invoke some navigation from there.

    To reproduce the issue you simply need to add "presentation": "modal", in line 21 of path-configuration.json: https://github.com/hotwired/turbo-ios/blob/2277f7dcd52c3f8eac735980166e64bd5745272b/Demo/path-configuration.json#L21

    After that, when you run the demo, you can click on "intercept with a native view" and click on one of the rows. Now the app will crash.

    It seems as if the navigationController in this case is simply an instance of UINavigationController and not of TurboNavigationController, so the forced cast lets the app crash. I think this is caused by this line here: https://github.com/hotwired/turbo-ios/blob/2277f7dcd52c3f8eac735980166e64bd5745272b/Demo/Navigation/TurboNavigationController.swift#L89

    So the questions are:

    1. Is this a bug, or intended behavior?
    2. If it's intended, how do we achieve navigation from within a native modal instead?

    N. b. I tested the same thing with the android demo where it doesn't crash, so I suspect this is indeed a bug.

    opened by mradke 6
  • [Question] How to send cookies with OAuth response?

    [Question] How to send cookies with OAuth response?

    The end of the Authorization doc mentions getting cookies alongside an OAuth response.

    For HEY, we have a slightly different approach. We get the cookies back along with our initial OAuth request and set those cookies directly to the web view and global cookie stores.

    First, can you elaborate a bit more on how that's possible with Rails? This request is coming from a new client, so it shouldn't have any existing or shared session/cookies. Are they created fresh? If so, how?

    Second, am I wrong to assume that the access token request should be hitting an API-like controller? This will be a request without a CSRF token, so no protection can be added there. But if I work with, for example, ActionController::API, then I don't even have access to the cookies.

    I've spent a while on this and feel like I'm missing something obvious. But I'm kind of lost as to how this all lines up. Any help would be greatly appreciated! Thanks in advance.

    opened by joemasilotti 6
  • Option to disable logging

    Option to disable logging

    I've found Turbo logging to be quite verbose and get in the way of my application logs. This PR lets the developer disable Turbo logging by adding the disableTurboLog launch argument to their scheme.

    image

    This could have been an environment variable, but the double negative get's weird, and those are parsed as strings. If we wanted to add logging levels then environment variables are the better choice.

    opened by joemasilotti 6
  • Final location.pathname when accessing GET redirects

    Final location.pathname when accessing GET redirects

    In brief: After visiting a GET request that responded with a redirect, the location.pathname of a turbo-ios webview is the requested path rather than the redirect-target path. This is different from how it is handled in Chrome, Safari, and Mobile Safari where the resulting location.pathname is the ultimate redirect-target path.

    Please see: https://github.com/johnmaxwell/turbo-native-demo/pull/1/files

    This fork and patch to the hotwired/turbo-native-demo demo app adds a /get-redirect GET route that simply redirects (302) to /one and an index link for it.

    image image ☝️Tapping the "Go through a redirect" on a desktop browser (Chrome or Safari) takes the browser to /one as expected, showing both the page content and updating the browser URL.


    image image image ☝️ In the context of turbo-ios, the browser is shown the content of /one as expected, but the location.pathname remains on /get-redirect


    Is this expected behavior or a known issue?

    image ☝️In our production Rails app, we have a notification tray that contains a variety of types of notifications, each resolving to a different target URL. In that tray, we link not to the target URL, but to a notification URL -- e.g. /notifications/123 -- where the user's interaction is logged before forwarding them to the final target -- e.g. /final-target

    In attempting to package our Rails app in a turbo-ios project, we have noticed that the app location.pathname hangs on /notification/123 . For subsequent link clicks, the referrer is then passed as /notification/123 instead of /final-target.

    opened by johnmaxwell 5
  • If a form submission redirects, the target page is rendered twice

    If a form submission redirects, the target page is rendered twice

    This gif demonstrates the bug:

    form

    To replicate, using https://github.com/hotwired/turbo-native-demo

    // index.ejs
    
      <form method="post" action="/redirect">
        <button type="submit">Submit Form and redirect to another page</button>
      </form>
    
    // server.js
    
    app.post("/redirect", (request, response) => {
      response.redirect("/one")
    })
    

    Or click here to see the changes: https://github.com/hotwired/turbo-native-demo/compare/main...ghiculescu:duplicate-page-replica (if you'd like redirect behavior in the demo, I can tidy up the UI and make a PR - but that's not the issue here :))

    When you click on the button:

    • The POST request is made and returns a 302
    • Turbo.js catches the 302 and makes a GET to /one
    • Turbo-native's didProposeVisit is called with /one as the desired URL. session.visit(/one) gets called. And another GET to /one is made.

    You can see this in the demo server logs:

    Sat Jan 09 2021 17:35:23 GMT-0700 (Mountain Standard Time) -- POST /redirect
    Sat Jan 09 2021 17:35:23 GMT-0700 (Mountain Standard Time) -- GET /one
    Sat Jan 09 2021 17:35:23 GMT-0700 (Mountain Standard Time) -- GET /one
    

    As well as the doubled up requests in Safari's inspector. I would expect only a single GET /one.

    This isn't too bad in this case, but where it gets really problematic is if the POST request sets a flash (in Rails). For example:

    def create
      redirect_to some_path, alert: "something happened"
    end
    

    In this case the flash will be consumed by the first GET request, and will not appear in the second one. So from the app's perspective no flash message is shown (but if you do exactly the same behavior through a web browser, you do see a flash).

    documentation 
    opened by ghiculescu 5
  • Add support `theme-color` for the website

    Add support `theme-color` for the website

    The proposed change adds support for a custom color (light/dark) from the HTML attribute, which can be helpful.

    <!doctype html>
    <html> 
    
    <head>
      <meta name="theme-color" content="#f5f7fa" media="(prefers-color-scheme: light)">
      <meta name="theme-color" content="#232730" media="(prefers-color-scheme: dark)">
    </head>
    
    ...
    

    This can be a convenient and intuitive way to style an element, especially for users unfamiliar with Swift. Also, this ensures that the colors will always be up-to-date and the same as in the PWA version.

    In the end, this will increase the usability for those who start cloning a Demo project.

    opened by tabuna 1
  • Update requirements

    Update requirements

    I noticed that the description of the home page requires the rails package. Although, it really only needs @hotwired/turbo. I took the formatting from the Turbo documentation

    opened by tabuna 1
  • How to display the initial boot screen

    How to display the initial boot screen

    Hey, i want to make an initial application loading screen (Beautiful picture).

    I've been to change the first screen and put its change after N seconds. But this seems like a very bad solution to me. Because after N seconds it will show the Turbo screen with page loading.

    I wanted very much that the display of the first image would be tied to the loading of the page on the network. Since I can't just add the image to the screen with a visual builder. I created a very simple controller:

    import UIKit
    
    class SplashController: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            navigationController?.setNavigationBarHidden(true, animated: true)
            
            
            let image = UIImage(named: "Splash")
            let imageView = UIImageView(image: image)
            
            imageView.frame = UIScreen.main.bounds
            self.view.addSubview(imageView)
        }
    
    }
    

    But where to return it correctly and link it to the content download in the demo? I would be grateful for any help

    opened by tabuna 1
  • CSS not loading sometimes for Android and iOS app

    CSS not loading sometimes for Android and iOS app

    No CSS loaded sometimes after a user returns to the App.

    image

    You guys have any clue where to search for this? It happens on iOS and on Android and it occur randomly as it seems.

    I have some functions on returning to the app, displayed here, maybe something is wrong here? image

    Is it maybe a package issue?

    Cheers, Chris

    opened by chriskroon 0
  • Downloading the file from the link

    Downloading the file from the link

    Hello, I have a calendar file that is downloading:

    <a href="https://example.com/user.ics" download="calendar">
        Download
    </a>
    

    When you click on it, the application opens the browser with a blank page and only then offers to add values ​​to the ios calendar.

    https://user-images.githubusercontent.com/5102591/193480123-eae5406b-6718-4657-ab27-ca43105d293e.MOV

    Can anyone suggest how to avoid opening the browser and staying in the application?

    opened by tabuna 4
Releases(7.0.0-rc.7)
  • 7.0.0-rc.7(Jun 30, 2022)

    What's Changed

    • Add missing sessionWebViewProcessDidTerminate to Demo by @ghiculescu in https://github.com/hotwired/turbo-ios/pull/69
    • Add sessionWebViewProcessDidTerminate to quick start by @ghiculescu in https://github.com/hotwired/turbo-ios/pull/72
    • Fixing Xcode warning: class-constrained protocol is deprecated by @ludagoo in https://github.com/hotwired/turbo-ios/pull/47
    • Improve logging by displaying more relevant details and additional arguments by @jayohms in https://github.com/hotwired/turbo-ios/pull/83
    • Fix typo in migration guide by @frederfred in https://github.com/hotwired/turbo-ios/pull/70
    • Fix CI by @svara in https://github.com/hotwired/turbo-ios/pull/85
    • Allow a Session's snapshot cache to be cleared manually when another Session has finished a form submission by @jayohms in https://github.com/hotwired/turbo-ios/pull/84
    • Expose a new TurboLog.debugLoggingEnabled API that apps can enable to see debug logs by @jayohms in https://github.com/hotwired/turbo-ios/pull/86

    New Contributors

    • @ludagoo made their first contribution in https://github.com/hotwired/turbo-ios/pull/47
    • @frederfred made their first contribution in https://github.com/hotwired/turbo-ios/pull/70
    • @svara made their first contribution in https://github.com/hotwired/turbo-ios/pull/85

    Full Changelog: https://github.com/hotwired/turbo-ios/compare/7.0.0-rc.6...7.0.0-rc.7

    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-rc.6(Jan 24, 2022)

    What's Changed

    • Delegate callback when web view process terminates by @joemasilotti in https://github.com/hotwired/turbo-ios/pull/56
    • Docs: Make it clearer that you should use the latest version by @ghiculescu in https://github.com/hotwired/turbo-ios/pull/60
    • Conform test class to protocol to fix broken test by @jayohms in https://github.com/hotwired/turbo-ios/pull/63

    Full Changelog: https://github.com/hotwired/turbo-ios/compare/7.0.0-rc.5...7.0.0-rc.6

    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-rc.5(Dec 14, 2021)

    What's Changed

    • Skip the same-page anchor scrolling behavior for visits initiated from the native side by @jayohms in https://github.com/hotwired/turbo-ios/pull/59

    Full Changelog: https://github.com/hotwired/turbo-ios/compare/7.0.0-rc.4...7.0.0-rc.5

    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-rc.4(Dec 8, 2021)

    What's Changed

    • Initialize path config from file synchronously by @joemasilotti in https://github.com/hotwired/turbo-ios/pull/27
    • Allow blob:http* urls to load from es-module-shims by @jayohms in https://github.com/hotwired/turbo-ios/pull/58

    New Contributors

    • @jayohms made their first contribution in https://github.com/hotwired/turbo-ios/pull/58

    Full Changelog: https://github.com/hotwired/turbo-ios/compare/7.0.0-rc.3...7.0.0-rc.4

    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-rc.3(Dec 7, 2021)

    What's Changed

    • Expose visit by @olivaresf in https://github.com/hotwired/turbo-ios/pull/53
    • Register the adapter only after turbo is loaded by @railsbob in https://github.com/hotwired/turbo-ios/pull/52

    New Contributors

    • @olivaresf made their first contribution in https://github.com/hotwired/turbo-ios/pull/53
    • @railsbob made their first contribution in https://github.com/hotwired/turbo-ios/pull/52

    Full Changelog: https://github.com/hotwired/turbo-ios/compare/7.0.0-rc.2...7.0.0-rc.3

    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-rc.2(Sep 30, 2021)

  • 7.0.0-rc.1(Sep 14, 2021)

    Changes:

    • Update the Hotwire subdomains
    • Allow VisitProposal construction outside of Turbo
    • fix directories for relative links
    • Fix link reference to code in Turbo library
    • remove extra word
    • Add instructions to select the correct app lifecycle type under XCode 12
    • Default arguments in record() test helper
    • Fix tense of error message
    • Update Migration.md
    • Update adapter to account for location -> URL

    Demo:

    • Always call session.visit with options
    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-beta.1(Dec 18, 2020)

Owner
Hotwire
Build modern web apps by sending HTML over the wire
Hotwire
A document-based SwiftUI application for viewing and editing EEG data, aimed at making software for viewing brain imaging data more accessible.

Trace A document-based SwiftUI application for viewing and editing EEG data, aimed at making software for viewing brain imaging data more accessible.

Tahmid Azam 3 Dec 15, 2022
Adventures-with-Swift - Building Native iOS Apps with UIKit and SiwftUI ο£Ώ

Adventures with Swift, UIKit, & SwiftUI As I have experience working with React Native and have dabbled a bit with Flutter, I've decided to dive in th

Daniel Stafford 4 Nov 17, 2022
A weather app developed in React Native. It is the React Native version of SwiftWeather.

ReactNativeWeather A weather app developed in React Native. It is the React Native version of SwiftWeather How to run the app Install react-native If

Jake Lin 22 Jun 7, 2022
Native iOS app using the exposure notification framework from Apple.

Corona Warn App - iOS Development β€’ Documentation β€’ Contribute β€’ Support β€’ Changelog β€’ Licensing The goal of this project is to develop the official C

Corona-Warn-App 1.7k Dec 18, 2022
This framework contains SBB (Swiss Federal Railways) UI elements for iOS SwiftUI Apps

Framework: Design System Mobile for iOS & SwiftUI This framework contains SBB (Swiss Federal Railways) UI elements for iOS SwiftUI Apps. It allows an

Swiss Federal Railways (SBB) 21 Nov 3, 2022
Seaglass is a truly native macOS client for Matrix. It is written in Swift and uses the Cocoa user interface framework.

Seaglass is a truly native macOS client for Matrix. It is written in Swift and uses the Cocoa user interface framework.

null 1 Jan 17, 2022
Super basic iOS app to browse open-source-ios-apps

Super basic iOS app to browse open-source-ios-apps

null 76 Nov 28, 2022
Native Jellyfin Client for iOS and tvOS

Swiftfin Swiftfin is a modern client for the Jellyfin media server. Redesigned in Swift to maximize direct play with the power of VLC and look native

Jellyfin 1.1k Jan 6, 2023
Native and encrypted password manager for iOS and macOS.

Open Sesame Native and encrypted password manager for iOS and macOS. What is it? OpenSesame is a free and powerful password manager that lets you mana

OpenSesame 431 Dec 28, 2022
πŸ“± Guideo - Native iOS App crafted with Swift and SwiftUI

Guideo An awesome iOS Native App ?? About Guideo App wire-framed and crafted from scratch by a team of 4. Our final project of the ο£Ώ Apple Foundation

Lyane Lamara 5 Oct 12, 2022
GitHub-User is an iOS native application, written in Swift programming language.

#GitHub-User GitHub-User is an iOS native application, written in Swift programming language. This project is an interview take home project. The arch

Zeljko Lucic 1 Mar 25, 2022
Native iOS app built in SwiftUI, displays a collection of user's books.

Native iOS app built in SwiftUI, displays a collection of user's books.

Matthew Eilar 1 May 23, 2022
Riddler is a riddle game built as a native iOS app in Swift using SwiftUI

Riddler is a riddle game built as a native iOS app in Swift using SwiftUI. It includes 50 challenging riddles with hints for when you get stuck. The game tracks your stats so you can compare your performance against your friends, and see who can answer all 50 riddles the quickest.

Oliver Stenning 3 Nov 23, 2022
Angela Yu iOS tutorials TODO apps practice

Todoey βœ“ Our Goal The objective of this tutorial is to understand how to save data in iOS. We'll look at various choices and learn to use UserDefaults

Ferdous 0 Jan 1, 2022
A curated list of Open Source example iOS apps developed in Swift

Example iOS Apps A curated list of Open Source example iOS apps developed in Swift. How to Use Example-iOS-Apps is an amazing list for people who are

null 1 Dec 15, 2021
CS193p---Assignments - Assignment Solutions for Stanford CS193p - Developing Apps for iOS

Assignment Solutions for Stanford CS193p - Developing Apps for iOS Note: This is ongoing work Task done Programming Assignment 1 x Programming Assignm

null 0 Jan 12, 2022
A simple star rating library for SwiftUI apps on macOS and iOS

DLDRating A simple star rating library for SwiftUI apps on macOS and iOS. Features Installation Usage Styling Credits DLDRating was made by Dionne Lie

null 1 Mar 6, 2022
Create a beautiful Onabording for your iOS/iPadOS apps in just a few minutes.

Create a beautiful Onabording for your iOS/iPadOS apps in just a few minutes.

Jem Alvarez 6 Sep 9, 2022
Demonstration of using UIWindowScene and SwiftUI to provide a native-looking Mac preferences window in Catalyst

CatalystPrefsWindow Ever wondered how to create a more Mac-like preferences window for Catalyst? Perhaps Settings Bundles are too limiting for the kin

Steven Troughton-Smith 148 Dec 27, 2022