A Swift event bus for UIWebView/WKWebView and JS.

Overview

Caravel Logo

CocoaPods

Join the chat at https://gitter.im/coshx/caravel

An event bus for sending messages between UIWebView/WKWebView and embedded JS. Made with pure Swift.

Features

  • Easy, fast and reliable event bus system
  • Multiple bus support
  • Multithreading support
  • WKWebView support
  • iOS ~> JavaScript supported types:
    • Bool
    • Int
    • Float
    • Double
    • String
    • Any array (using types in this list, including dictionaries)
    • Any dictionary (using types in this list, including arrays)
  • JavaScript ~> iOS supported types:
    • Boolean
    • Int
    • Float (available as a Double)
    • String
    • Array (available as a NSArray)
    • Object (available as a NSDictionary)

Installation

Using CocoaPods

Add this line to your Podfile:

pod 'Caravel'

And run pod install. Once installed, open the Pods folder. Navigate to caravel/caravel/js. In this folder, drag and drop caravel.min.js into your Xcode project. You should load this JS script in any webpage you use Caravel.

Using Carthage

Add this line to your Cartfile:

github "coshx/caravel"

And run carthage update. Once installed, open the Carthage/Checkouts folder. Navigate to caravel/caravel/js. In this folder, drag and drop caravel.min.js into your Xcode project. You should load this JS script in any webpage you use Caravel.

Using as a submodule

Clone this repo and add Caravel.xcodeproj into your workspace.

Might be useful

Get started

Caravel allows developers to communicate between their UIWebView and the embedded JS. You can send any kind of message between these two folks.

Have a glance at this super simple sample. Let's start with the iOS part:

class MyController: UIViewController {
    @IBOutlet weak var webView: UIWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Prepare your bus before loading your web view's content
        Caravel.getDefault(self, webView: webView, whenReady: { bus in
            // In this scope, the JS endpoint is ready to handle any event.
            // Register and post your events here
            bus.post("MyEvent", data: [1, 2, 3])
            
            self.bus = bus // You can save your bus for firing events later
        })
        
        // ... Load web view's content there
    }
}

And now, in your JS:

var bus = Caravel.getDefault();

bus.register("AnEventWithAString", function(name, data) {
    alert('I received this string: ' + data);
    bus.post("AnEventForiOS");
});

And voilร !

WKWebView

Caravel 1.1.0 supports WKWebView. Keep in mind this component is still in beta and might not work as expected. We won't ship you a ๐Ÿ• if your app does not work.

Anyway. If you're this kind of guy who likes being involved in some risky business, here is an example about how to use Caravel with it. Be careful, it is a 2-step process.

class MyController: UIViewController {
    private var wkWebView: WKWebView?

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()
        // First generate a draft using your custom configuration
        let draft = Caravel.getDraft(config)

        // Build your WKWebView as usual then
        self.wkWebView = WKWebView(frame: self.view.bounds, configuration: config)

        // Finally initiate Caravel
        Caravel.getDefault(self, wkWebView: self.wkWebView!, draft: draft, whenReady: {
            // Do whatever you've got to do here
        })

        // ... Load content into your WKWebView there
    }
}

Porting your app from Drekkar to Caravel

Super duper easy. Just use the same codebase and use the JS script from Caravel. Finally, add this after having loaded the Caravel script:

var Drekkar = Caravel;

Troubleshooting

๐Ÿ˜• Sometimes the bus is not working?!

Firstly, ensure you are using the bus correctly. Check if you are unregistering the bus when exiting the controller owning your web component (either a UIWebView or a WKWebView). Use the unregister method for this.

Caravel automatically cleans up any unused bus when you create a new one. However, this operation is run in the background to avoid any delay on your side. So, a thread collision might happen if you have not unsubscribed your bus properly.

However, if you think everything is good with your codebase, feel free to open a ticket.

I want to use my custom UIWebViewDelegate. What should I do?

To raise iOS events, Caravel must be the delegate of the provided UIWebView. However, if there is any existing delegate, Caravel saves it before setting its own. So, if you would like to use your custom one, simply set it before any call to Caravel.

I want to use my custom WKUserContentController. What should I do?

To raise iOS events, Caravel adds a custom WKScriptMessageHandler to the current content controller. If you would like to use your custom one, simply set it before any call to Caravel.

What object should I use as a subscriber?

A subscriber could be any object except the watched target (either the UIWebView or the WKWebView). We recommend to use the controller as a subscriber (it is a common pattern).

Reserved names

CaravelInit is an internal event, sent by the JS part for triggering the whenReady method.

Also, the default bus is named default. If you use this name for a custom bus, Caravel will automatically switch to the default one.

Finally, when using a WKWebView, Caravel names its script message handler caravel.

Keep in mind event and bus names are case-sensitive.

You might also like...
KVO for Swift - Value Observing and Events

Value Observing and Events for Swift Swift lacks the powerful Key Value Observing (KVO) from Objective-C. But thanks to closures, generics and propert

A sample modulated project to show my knowledge about Swift and Software Development process
A sample modulated project to show my knowledge about Swift and Software Development process

A sample modulated project to show my knowledge about Swift and Software Development process

Redux for Swift - a predictable state container for Swift apps

Merge / deprecation announcement: ReduxKit and Swift-Flow have joined forces! The result is ReSwift. The nitty gritty: We decided to deprecate ReduxKi

Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.

Reactive extensions to Cocoa frameworks, built on top of ReactiveSwift. โš ๏ธ Looking for the Objective-C API? ๐ŸŽ‰ Migrating from RAC 4.x? ๐Ÿš„ Release Road

๐Ÿค– RxSwift + State Machine, inspired by Redux and Elm.
๐Ÿค– RxSwift + State Machine, inspired by Redux and Elm.

RxAutomaton RxSwift port of ReactiveAutomaton (State Machine). Terminology Whenever the word "signal" or "(signal) producer" appears (derived from Rea

A configurable api client based on Alamofire4 and RxSwift4 for iOS

SimpleApiClient A configurable api client based on Alamofire4 and RxSwift4 for iOS Requirements iOS 8.0+ Swift 4 Table of Contents Basic Usage Unwrap

STDevRxExt contains some extension functions for RxSwift and RxCocoa which makes our live easy.

STDevRxExt Example To run the Example.playground, clone the repo, and run pod install from the Example directory first. Requirements iOS 9.0+ tvOS 9.0

RxAlamoRecord combines the power of the AlamoRecord and RxSwift libraries to create a networking layer that makes interacting with API's easier than ever reactively.
RxAlamoRecord combines the power of the AlamoRecord and RxSwift libraries to create a networking layer that makes interacting with API's easier than ever reactively.

Written in Swift 5 RxAlamoRecord combines the power of the AlamoRecord and RxSwift libraries to create a networking layer that makes interacting with

๐ŸŸฃ Verge is a very tunable state-management engine on iOS App (UIKit / SwiftUI) and built-in ORM.
๐ŸŸฃ Verge is a very tunable state-management engine on iOS App (UIKit / SwiftUI) and built-in ORM.

Verge.swift ๐Ÿ“ An effective state management architecture for iOS - UIKit and also SwiftUI ๐Ÿ“ _ An easier way to get unidirectional data flow _ _ Supp

Comments
  • only works on initial web view.

    only works on initial web view.

    Hi, I'm building a tool that allows editing a document in a webview with a native toolbar. Everything works fine the first time around, but if I dismiss a view controller, then reload it again (creating a new post), none of the events work anymore.

    I have this

    override func viewDidLoad() {
      self.wv!.frame = CGRectMake(0, 65, self.view.bounds.width, self.view.bounds.height - 50)
      self.wv = UIWebView()
      self.view.addSubview(self.wv!)
    
      Caravel.get("editor", webView: self.wv!).whenReady() { bus in
        bus.register("highlight") { name, data in
          // all is good here!
        }
      }
    }
    
    editor.on('highlight', function () {
      Caravel.get("editor").post('highlight')
    })
    

    As mentioned, it all works great until I attempt to load it the second time around. Any ideas?

    opened by ded 4
  • bus.post(

    bus.post("ev", optionalVar)

    Heyo. Me again.

    I had an issue when that I post an optional var, it posts the Optional() wrapper around it to the webview.

    Eg:

    bus.post("html", aString: self.html!)
    
    MyListener.register('html', function (name, html) {
      alert(html) // verbatim: "Optional('<p>hello world</p>')"
    })
    

    it's a little bogus โ€”ย any thoughts aside from unwrapping the "Optional()" text around the string on the JS side?

    opened by ded 2
  • Drekkar parallel dev

    Drekkar parallel dev

    https://github.com/coshx/caravel/blob/zealous-hornet/caravel/Caravel.swift#L52 > Could be factorised

    https://github.com/coshx/caravel/blob/zealous-hornet/caravel/Caravel.swift#L94 > Hazardous. Should collect ids first

    In data test: bad => wrong

    • Add ready event for registration

    Remove receive float test as not supported

    Lock subscribers in EventBus

    Add timer for benchmarks

    Remove useless ViewController

    opened by acadet 0
  • Add a Gitter chat badge to README.md

    Add a Gitter chat badge to README.md

    coshx/caravel now has a Chat Room on Gitter

    @acadet has just created a chat room. You can visit it here: https://gitter.im/coshx/caravel.

    This pull-request adds this badge to your README.md:

    Gitter

    If my aim is a little off, please let me know.

    Happy chatting.

    PS: Click here if you would prefer not to receive automatic pull-requests from Gitter in future.

    opened by gitter-badger 0
Releases(v1.1.1)
iOS app for open event

CircleCI Code Quality Chat Open Event iOS iOS app for Open Event Introduction This is an iOS app developed for FOSSASIA in mind. The Open Event Projec

FOSSASIA 1.6k Jan 5, 2023
Open Event Orga iOS App

Open Event Organizer iOS App Event management app for organizers using Open Event Platform Roadmap Make the app functionality and UI/UX similar to the

FOSSASIA 1.5k Dec 10, 2022
A lightweight, event-driven architectural framework

Trellis Trellis features a declarative DSL that simplifies service bootstrapping: let cluster = try await Bootstrap { Group { Store(model:

Valentin Radu 25 Aug 16, 2022
MacOS Serial solution (Observable & Event-Driven) to make integration of Serial peripherals trivial

SerialSwift SerialSwift makes communicating with your Serial Peripherals on MacOS trivial. Better still, SerialSwift is designed to be fundamnetally O

Flowduino 2 Sep 9, 2022
Unidirectional flow implemented using the latest Swift Generics and Swift Concurrency features.

swift-unidirectional-flow Unidirectional flow implemented using the latest Swift Generics and Swift Concurrency features. struct SearchState: Equatabl

Majid Jabrayilov 104 Dec 26, 2022
Dynamic and type-safe framework for building linear and non-linear flows.

FlowKit FlowKit is a dynamic flow framework capable of building a flow, based on conditions and ordered according to a logic of next steps. By using F

N26 55 Dec 20, 2022
RxSwift extentions for Swift optionals and "Occupiable" types

RxOptional RxSwift extentions for Swift optionals and "Occupiable" types. Usage All operators are available on Driver as well unless otherwise marked.

Thane Gill 8 Jun 28, 2020
Simple and lightweight Functional Reactive Coding in Swift for the rest of us

The simplest Observable<T> implementation for Functional Reactive Programming you will ever find. This library does not use the term FRP (Functional R

Jens Ravens 1.1k Jan 3, 2023
Lightweight observations and bindings in Swift

What is Hanson? Hanson is a simple, lightweight library to observe and bind values in Swift. It's been developed to support the MVVM architecture in o

Blendle 526 Oct 18, 2022
Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux

Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux Introduction VueFlux is the architecture to manage state with unidi

Ryo Aoyama 324 Dec 17, 2022