Convert Figma logo to code with AI

DeclarativeHub logoBond

A Swift binding framework

4,233
359
4,233
52

Top Related Projects

24,320

Reactive Programming in Swift

Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.

15,085

Network abstraction layer written in Swift.

A Swift Reactive Programming Kit

Quick Overview

Bond is a cross-platform framework for working with schematized data. It supports efficient serialization and deserialization, and is designed to be fast, compact, and versatile. Bond is particularly useful for scenarios involving cross-language serialization or communication between heterogeneous systems.

Pros

  • High performance and efficient serialization
  • Cross-platform and multi-language support (C++, C#, Java, Python)
  • Flexible schema evolution, allowing for backward and forward compatibility
  • Compact binary format, reducing data size and network traffic

Cons

  • Steeper learning curve compared to simpler serialization libraries
  • Limited community support and resources compared to more popular alternatives
  • Requires additional tooling for schema compilation
  • May be overkill for simple serialization needs

Code Examples

  1. Defining a Bond schema:
namespace example

struct Person
{
    0: string name;
    1: uint16 age;
    2: vector<string> hobbies;
}
  1. Serializing data in C++:
#include <bond/core/bond.h>
#include <bond/stream/output_buffer.h>

using namespace example;

Person person;
person.name = "Alice";
person.age = 30;
person.hobbies.push_back("reading");
person.hobbies.push_back("hiking");

bond::OutputBuffer output;
bond::CompactBinaryWriter<bond::OutputBuffer> writer(output);
bond::Serialize(person, writer);
  1. Deserializing data in C#:
using Bond;
using Bond.Protocols;
using Bond.IO.Safe;

var input = new InputBuffer(serializedData);
var reader = new CompactBinaryReader<InputBuffer>(input);
var person = Bond.Deserialize<Person>.From(reader);

Console.WriteLine($"Name: {person.name}, Age: {person.age}");

Getting Started

  1. Install Bond:

    • For C++: Use vcpkg or build from source
    • For C#: Install via NuGet: Install-Package Bond.CSharp
    • For Python: pip install bond
  2. Define your schema in a .bond file

  3. Compile the schema:

    gbc c++ --output=. my_schema.bond
    
  4. Include generated files in your project and start using Bond for serialization and deserialization

Competitor Comparisons

24,320

Reactive Programming in Swift

Pros of RxSwift

  • More comprehensive and feature-rich, offering a wider range of operators and functionalities
  • Larger community and ecosystem, providing better support and resources
  • Cross-platform compatibility with other Rx implementations

Cons of RxSwift

  • Steeper learning curve due to its complexity and extensive API
  • Potentially higher memory footprint and performance overhead
  • Can lead to overuse of reactive paradigms, making code harder to understand

Code Comparison

RxSwift:

Observable.from([1, 2, 3, 4, 5])
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }
    .subscribe(onNext: { print($0) })

Bond:

let numbers = Observable<[Int]>([1, 2, 3, 4, 5])
numbers
    .map { $0.filter { $0 % 2 == 0 }.map { $0 * 2 } }
    .observeNext { print($0) }

Both libraries provide reactive programming capabilities for Swift, but RxSwift offers a more extensive set of features and better cross-platform compatibility. Bond, on the other hand, focuses on simplicity and ease of use, making it a good choice for smaller projects or developers new to reactive programming. The code comparison shows that both libraries have similar syntax for basic operations, but RxSwift's approach is more granular and chainable.

Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.

Pros of ReactiveCocoa

  • More mature and widely adopted in the iOS community
  • Supports a broader range of reactive programming concepts
  • Offers more advanced features like schedulers and error handling

Cons of ReactiveCocoa

  • Steeper learning curve due to its complexity
  • Heavier framework with more overhead
  • Less focused on Swift-specific features and syntax

Code Comparison

ReactiveCocoa:

let disposable = textField.reactive.continuousTextValues
    .map { text in text.uppercased() }
    .bind(to: label.reactive.text)

Bond:

let disposable = textField.reactive.text
    .map { $0.uppercased() }
    .bind(to: label.reactive.text)

Summary

ReactiveCocoa is a more comprehensive reactive programming framework with a longer history in the iOS ecosystem. It offers a wider range of features and concepts, making it suitable for complex reactive programming scenarios. However, this comes at the cost of increased complexity and a steeper learning curve.

Bond, on the other hand, is more focused on Swift-specific reactive programming, offering a simpler and more lightweight approach. It may be easier to adopt for developers new to reactive programming or those working on smaller projects that don't require the full feature set of ReactiveCocoa.

Both frameworks provide similar basic functionality, as seen in the code comparison, but ReactiveCocoa's API tends to be more verbose and feature-rich.

15,085

Network abstraction layer written in Swift.

Pros of Moya

  • Focused specifically on network abstraction, providing a clean and robust API for handling network requests
  • Extensive documentation and community support, making it easier for developers to get started and troubleshoot issues
  • Built-in support for stubbing network requests, facilitating easier testing and development

Cons of Moya

  • Limited to networking tasks, whereas Bond offers a broader range of reactive programming features
  • May introduce additional complexity for simple networking tasks that don't require such a comprehensive framework
  • Steeper learning curve for developers new to reactive programming concepts

Code Comparison

Moya example:

let provider = MoyaProvider<MyAPI>()
provider.request(.userProfile) { result in
    switch result {
    case let .success(response):
        let data = response.data
        // Handle the response
    case let .failure(error):
        // Handle the error
    }
}

Bond example:

let observable = Observable<String>("Initial value")
observable.bind(to: label.bnd_text)
observable.value = "New value"

While Moya focuses on network requests, Bond provides a more general-purpose reactive programming framework. Moya excels in API abstraction and network-related tasks, while Bond offers a wider range of reactive programming capabilities for various aspects of iOS development.

A Swift Reactive Programming Kit

Pros of ReactiveKit

  • More lightweight and focused on core reactive programming concepts
  • Provides a more flexible and extensible architecture
  • Better suited for advanced reactive programming scenarios

Cons of ReactiveKit

  • Steeper learning curve for beginners
  • Less comprehensive UI binding capabilities out of the box
  • Requires more manual setup for common UI interactions

Code Comparison

ReactiveKit:

let signal = Signal<Int, Never>()
signal.observeNext { value in
    print("Received value: \(value)")
}
signal.send(value: 42)

Bond:

let observable = Observable<Int>(0)
observable.bind(to: label.bnd_text)
observable.value = 42

ReactiveKit focuses on core reactive programming concepts, offering a more flexible architecture for advanced scenarios. However, it has a steeper learning curve and requires more manual setup for UI interactions. Bond, on the other hand, provides more comprehensive UI binding capabilities out of the box, making it easier for beginners to get started with reactive programming in iOS development.

The code comparison demonstrates the difference in approach between the two libraries. ReactiveKit uses signals and observers, while Bond utilizes observables and bindings, showcasing their distinct philosophies in implementing reactive programming concepts.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Bond, Swift Bond

Platform CI Status Twitter


Update: Bond 7 has been released! Check out the migration guide to learn more about the update.

Bond is a Swift binding framework that takes binding concepts to a whole new level. It's simple, powerful, type-safe and multi-paradigm - just like Swift.

Bond is built on top of ReactiveKit and bridges the gap between the reactive and imperative paradigms. You can use it as a standalone framework to simplify your state changes with bindings and reactive data sources, but you can also use it with ReactiveKit to complement your reactive data flows with bindings, reactive delegates and reactive data sources.

Bond is a backbone of the Binder Architecture - a preferred architecture to be used with the framework.

Why use Bond?

Say that you would like to do something when text of a text field changes. Well, you could setup the target-action mechanism between your objects and go through all that target-action selector registration pain, or you could simply use Bond and do this:

textField.reactive.text.observeNext { text in
    print(text)
}

Now, instead of printing what the user has typed, you can bind it to a label:

textField.reactive.text.bind(to: label.reactive.text)

Because binding to a label text property is so common, you can even do:

textField.reactive.text.bind(to: label)

That one line establishes a binding between the text field's text property and label's text property. In effect, whenever user makes a change to the text field, that change will automatically be propagated to the label.

More often than not, direct binding is not enough. Usually you need to transform input is some way, like prepending a greeting to a name. As Bond is backed by ReactiveKit it has full confidence in functional paradigm.

textField.reactive.text
  .map { "Hi " + $0 }
  .bind(to: label)

Whenever a change occurs in the text field, new value will be transformed by the closure and propagated to the label.

Notice how we have used reactive.text property of the text field. It is an observable representation of the text property provided by Bond framework. There are many other extensions like that one for various UIKit components. They are all placed within the .reactive proxy.

For example, to observe button events do:

button.reactive.controlEvents(.touchUpInside)
  .observeNext { e in
    print("Button tapped.")
  }

Handling touchUpInside event is used so frequently that Bond comes with the extension just for that event:

button.reactive.tap
  .observeNext {
    print("Button tapped.")
  }  

You can use any ReactiveKit operators to transform or combine signals. Following snippet depicts how values of two text fields can be reduced to a boolean value and applied to button's enabled property.

combineLatest(emailField.reactive.text, passField.reactive.text) { email, pass in
    return email.length > 0 && pass.length > 0
  }
  .bind(to: button.reactive.isEnabled)

Whenever user types something into any of these text fields, expression will be evaluated and button state updated.

Bond's power is not, however, in coupling various UI components, but in the binding of the business logic layer (i.e. Service or View Model) to the View layer and vice-versa. Here is how one could bind user's number of followers property of the model to the label.

viewModel.numberOfFollowers
  .map { "\($0)" }
  .bind(to: label)

Point here is not in the simplicity of a value assignment to the text property of a label, but in the creation of a binding which automatically updates label text property whenever the number of followers change.

Bond also supports two way bindings. Here is an example of how you could keep username text field and username property of your View Model in sync (whenever any of them change, other one will be updated too):

viewModel.username
  .bidirectionalBind(to: usernameTextField.reactive.text)

Bond is also great for observing various different events and asynchronous tasks. For example, you could observe a notification like this:

NotificationCenter.default.reactive.notification("MyNotification")
  .observeNext { notification in
    print("Got \(notification)")
  }
  .dispose(in: bag)

Let me give you one last example. Say you have an array of repositories you would like to display in a collection view. For each repository you have a name and its owner's profile photo. Of course, photo is not immediately available as it has to be downloaded, but once you get it, you want it to appear in collection view's cell. Additionally, when user does 'pull down to refresh' and your array gets new repositories, you want those in collection view too.

So how do you proceed? Well, instead of implementing a data source object, observing photo downloads with KVO and manually updating the collection view with new items, with Bond you can do all that in just few lines:

repositories.bind(to: collectionView) { array, indexPath, collectionView in
  let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! RepositoryCell
  let repository = array[indexPath.item]

  repository.name
    .bind(to: cell.nameLabel)
    .dispose(in: cell.onReuseBag)

  repository.photo
    .bind(to: cell.avatarImageView)
    .dispose(in: cell.onReuseBag)

  return cell
}

Yes, that's right!

Reactive Extensions

Bond is all about bindings and other reactive extensions. To learn more about how bindings work and how to create your own bindings check out the documentation on bindings.

If you are interested in what bindings and extensions are supported, just start typing .reactive. on any UIKit or AppKit object and you will get the list of available extensions. You can also skim over the source files to get an overview.

Observable Collections

When working with arrays usually we need to know how exactly did an array change. New elements could have been inserted into the array and old ones deleted or updated. Bond provides mechanisms for observing such fine-grained changes.

For example, Bond provides you with a (Mutable)ObservableArray type that can be used to generate and observe fine-grained changes.

let names = MutableObservableArray(["Steve", "Tim"])

...

names.observeNext { e in
  print("array: \(e.collection), diff: \(e.diff), patch: \(e.patch)")
}

You work with the observable array like you would work with the array it encapsulates.

names.append("John") // prints: array: ["Steve", "Tim", "John"], diff: Inserts: [2], patch: [I(John, at: 2)]
names.removeLast()   // prints: array: ["Steve", "Tim"], diff: Deletes: [2], patch: [D(at: 2)]
names[1] = "Mark"    // prints: array: ["Steve", "Mark"], diff: Updates: [1], patch: [U(at: 1, newElement: Mark)]

Peek into observable collections documentation to learn more about observable collections.

Data Source Signals

Observable collections and other data source signals enable us to build powerful UI bindings. For example, an observable array can be bound to a collection view just like this:

names.bind(to: collectionView, cellType: UserCell.self) { (cell, name) in
    cell.titleLabel.text = name
}

No need to implement data source objects and do everything manually. Check out documentation on the data source signals to learn more about them and about table or collection view bindings.

Protocol Proxies

Bond provides NSObject extensions that make it easy to convert delegate method calls into signal. The extensions are built on top of ObjC runtime and enable you to intercept delegate method invocations and convert them into signal events.

Bond uses protocol proxies to implement table and collection view bindings and to provide signals like tableView.reactive.selectedRowIndexPath. Check out the protocol proxies documentation to learn more.

Community Extensions

Make sure to check out Extensions directory. It contains extensions that make Bond easy to use with other frameworks and libraries, like Realm.

If you have an extensions that makes your favourite framework work with Bond and you'd like to share it with everyone, we'd be more than happy to accept your PR.

Requirements

  • iOS 8.0+ / macOS 10.11+ / tvOS 9.0+
  • Swift 4.2

Communication

  • If you'd like to ask a question, open an issue.
  • If you found a bug, open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request (include unit tests).

Installation

Carthage

  1. Add the following to your Cartfile:

    github "DeclarativeHub/Bond"
    
  2. Run carthage update

  3. Add the framework as described in Carthage Readme

Accio

  1. Add the following to your Package.swift:

    .package(url: "https://github.com/DeclarativeHub/Bond.git", .upToNextMajor(from: "7.4.1")),
    
  2. Next, add Bond to your App targets dependencies like so:

    .target(
        name: "App",
        dependencies: [
            "Bond",
        ]
    ),
    
  3. Then run accio update.

CocoaPods

  1. Add the following to your Podfile:

    pod 'Bond'
    
  2. Run pod install.

License

The MIT License (MIT)

Copyright (c) 2015-2019 Srdan Rasic (@srdanrasic)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.