Bond is a Swift binding framework that takes binding concepts to a whole new level.

Related tags

Event Bond
Overview

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.

Comments
  • Bond v4

    Bond v4

    Hi All,

    I've just pushed my work on the next major version of Bond to the bond-4 branch. It's not finished yet, but I'd like to get some feedback while it's still in progress.

    Version 4 changes everything from the inside while trying to keep things, at least conceptually, from the outside similar to previous versions.

    The major difference is that now everything revolves around base Observable type. Functional aspects are implemented even on a higher lever - on ObservableType protocol. Anything that implements that protocol can be mapped, filtered, observed, reduced, bound, etc.

    Dynamic has been renamed to Scalar. It's a stateful Observable whose events are just new values that have been set.

    DynamicArray has been renamed to Vector. It's also a stateful Observable, but it's events are operations applied to the Vector (i.e. to the underlying array), like Insert, Delete, Update. No more bunch of will/did* closures. A Vector can perform Batch updates.

    The Bond class has been deprecated. Any Observable can now be observed by a simple closure (a sink).

    "Unbinding" is now managed by the Disposables, but in most cases it will happen automatically without the need for user intervention.

    A new class, the Promise, has been introduced that simplifies asynchronous tasks. It's also an Observable.

    As people have been using Bond for FRP, it's now much better supported through the new stateless Observable class that behaves like a hot signal. UIControl events, notifications, etc. are now Observables as they do not store any state.

    I'll be writing new Readme in following days which should explain everything much better. In the meantime, if anyone is having some spare time, check out bond-v4 branch.

    There are few things that are still in the TODO: filter function for the Vector, KVO, more unit tests.

    Regarding the operator ->>, I'm in favor of replacing it with |>.

    v4 is written in Swift 2. I'm aiming to have Bond ready and stable before Xcode 7 GM is out, which is I guess in a month.

    Thanks!

    @tonyarnold @ivanmoskalev @sync @tonyxiao @TadeasKriz

    opened by srdanrasic 46
  • Use Diff.swift to calculate diffs and patches

    Use Diff.swift to calculate diffs and patches

    This PR removes the existing internal diff calculation code in favour of Diff.swift which is faster, and provides the same functionality.

    I've updated the code to sent through individual patches, so that TableView/OutlineView changes should animate "correctly" with regards to moves.

    Remaining Tasks:

    • [ ] ~~Write tests appropriate to the new discrete change signals~~ Unnecessary, as we're using the same diff event syntax - the same tests should (and do) pass as-is
    • [ ] ~~Update the README~~ Unnecessary, as the diff event syntax has not changed.
    • [x] Finalise the Package.swift file. Diff.swift wasn't using proper SemVer - @wokalski is on it, though: https://github.com/wokalski/Diff.swift/issues/29
    Feature 
    opened by tonyarnold 22
  • Issues compiling under Xcode 9 beta 1

    Issues compiling under Xcode 9 beta 1

    The Swift 4 compiler appears to have a bug that we're hitting in Bond. I've filed an issue on Swift's bug tracker, so hopefully it will be fixed in a future beta.

    https://bugs.swift.org/browse/SR-5095

    Bug 
    opened by tonyarnold 19
  • Stop fatal error on non convertible value in 'RKKeyValueSignal'

    Stop fatal error on non convertible value in 'RKKeyValueSignal'

    I don't believe it is proper behavior here for Bond to cause my application to crash when an RKKeyValueSignal gets a change that is not convertible to the signal's type. I would instead like to see the signal send me an error in this case. The user should be able to handle this error as they see fit, rather than having an application crash.

    I have found this causing many crashes in my application when using NSManagedObjects and deallocating multiple view controllers, (i.e. when popping to the root view controller of a navigation stack). This is because I am deallocating the managed object's context simultaneously to the deallocation of the view controllers. The context is set to nil for the objects and a KVO event is fired with a value of nil. The signals, which will be shortly disposed of, but have not been yet, cannot convert the nil value to the expected type and cause a fatal error.

    Thoughts?

    opened by AnthonyMDev 17
  • Support iOS 7.0 with cocoapods.

    Support iOS 7.0 with cocoapods.

    Hi guys,

    I really like your work. But I had a problem, my project still supports iOS 7.0. I hope you can down version of Bond in Cocoapods from 8.0 to 7.0.

    Huy.

    opened by huyphams 17
  • Pod does not compile: Value of optional type 'Selector?' not unwrapped in file

    Pod does not compile: Value of optional type 'Selector?' not unwrapped in file "NSObject+KVO.swift"

    Xcode 9.0 Bond (6.4.2)

    /Pods/Bond/Sources/Bond/Shared/NSObject+KVO.swift:335:47: Value of optional type 'Selector?' not unwrapped; did you mean to use '!' or '?'?

    line that is breaking: unsafeBitCast(imp, to: _IMP.self)(me, selector)

    opened by tadasz 16
  • Crash on AtomicObserver in ReactiveKit

    Crash on AtomicObserver in ReactiveKit

    Hi,

    After updating pods, my app gets crashing randomly on AtomicObserver.dispose() in ReactiveKit. I think this problem was resolved in version 3.15.3 of ReactiveKit but when I update the pod for Bond, CocoaPods downloads the version 3.15.2 of ReactiveKit. How can I force CocoaPods to download latest version of ReactiveKit? I've tried putting both Bond and ReactiveKit in Podfile without success.

    And here is the crash log:

    Simultaneous accesses to 0x283da9e10, but modification requires exclusive access.
    Previous access (a modification) started at ReactiveKit AtomicObserver.dispose() + 152 (0x10d0943bc).
    Current access (a read) started at:
    0    libswiftCore.dylib                 0x00000001965bbbe0 swift_beginAccess + 560
    1    ReactiveKit                        0x000000010d093a58 AtomicObserver.on(_:) + 288
    2    ReactiveKit                        0x000000010d0946b8 protocol witness for ObserverProtocol.on(_:) in conformance AtomicObserver<A, B> + 20
    3    ReactiveKit                        0x000000010d094964 ObserverProtocol.receive(completion:) + 612
    4    ReactiveKit                        0x000000010d0cd6cc closure #2 in closure #1 in SignalProtocol.prefix<A>(untilOutputFrom:) + 748
    5    ReactiveKit                        0x000000010d093a58 AtomicObserver.on(_:) + 484
    6    ReactiveKit                        0x000000010d0946b8 protocol witness for ObserverProtocol.on(_:) in conformance AtomicObserver<A, B> + 20
    7    ReactiveKit                        0x000000010d094964 ObserverProtocol.receive(completion:) + 612
    8    ReactiveKit                        0x000000010d0cd4c4 closure #1 in closure #1 in SignalProtocol.prefix<A>(untilOutputFrom:) + 328
    9    ReactiveKit                        0x000000010d093a58 AtomicObserver.on(_:) + 484
    10   ReactiveKit                        0x000000010d0946b8 protocol witness for ObserverProtocol.on(_:) in conformance AtomicObserver<A, B> + 20
    11   ReactiveKit                        0x000000010d0959b8 partial apply + 132
    12   ReactiveKit                        0x000000010d093524 thunk for @escaping @callee_guaranteed (@in_guaranteed Signal<A, B>.Event) -> () + 20
    13   ReactiveKit                        0x000000010d0ef668 thunk for @escaping @callee_guaranteed (@in_guaranteed Signal<A, B>.Event) -> (@out ()) + 36
    14   ReactiveKit                        0x000000010d0ee89c Subject.on(_:) + 2524
    15   ReactiveKit                        0x000000010d0f2278 ReplayOneSubject.on(_:) + 1048
    16   ReactiveKit                        0x000000010d0f03c0 protocol witness for ObserverProtocol.on(_:) in conformance Subject<A, B> + 28
    17   ReactiveKit                        0x000000010d0ed37c SubjectProtocol.send(completion:) + 636
    18   ReactiveKit                        0x000000010d07cd70 DisposeBag.deinit + 252
    19   ReactiveKit                        0x000000010d07cf68 DisposeBag.__deallocating_deinit + 60
    20   libswiftCore.dylib                 0x00000001965bd4f0 <redacted> + 28
    21   libobjc.A.dylib                    0x0000000188e0b2ac <redacted> + 352
    22   libobjc.A.dylib                    0x0000000188e07d24 objc_destructInstance + 100
    23   libobjc.A.dylib                    0x0000000188e0ed80 _objc_rootDealloc + 48
    24   UIKitCore                          0x000000018d162d60 <redacted> + 152
    25   UIKitCore                          0x000000018d59d278 <redacted> + 872
    26   UIKitCore                          0x000000018cb6dfd4 <redacted> + 68
    27   UIKitCore                          0x000000018cb5fc3c <redacted> + 96
    
    opened by madiguzel 15
  • [WIP] ObservableCollection implementation

    [WIP] ObservableCollection implementation

    This PR adds a new ObservableCollection type that was proposed in #493.

    To do:

    • [x] Refactor ObservableCollectionEvent to better handle diff/patch updates
    • [ ] Cover this PR with unit tests
    • [ ] Flesh out some documentation on how to extend ObservableCollection
    • [ ] Deprecate/typealias ObservableArray to this new class
    • [ ] Provide Observable2DArray alternative by leveraging TreeNode (#524).
    opened by srdanrasic 15
  • Issue when upgrading to Swift 3, Bond 6.2.8

    Issue when upgrading to Swift 3, Bond 6.2.8

    I'm upgrading from Swift 2.3 to 3.0 (Xcode 8.2.1), so I've updated the podfile (to no longer restrict the versions). This way my project now uses the 6.2.8 version of Bond, but fails to compile as seen in the attached screenshot.

    Any ideas on how to fix this? Thanks in advance!

    screen shot 2017-07-31 at 16 31 39

    opened by DarkByte 14
  • Sort MutableObservableArray<Item>

    Sort MutableObservableArray

    In ReactiveKit 2 there was the rather magical ability to sort a CollectionProperty<[Item]>'s events in-flight, as in:

    
    let collection: CollectionProperty<[Item]>
    
    let sorted: Stream<CollectionChangeset<[Item]>> = collection.sort { (a: Item, b:Item) -> Bool in 
        return a.whatever > b.whatever
    }
    

    Similar to #335, could it be possible to add this to the roadmap for Bond (6?), to port this functionality over to the new MutableObservableArray type?

    Cheers!

    Feature 
    opened by iandundas 13
  • asObservableFor causes a crash if the property value is nil

    asObservableFor causes a crash if the property value is nil

    I have a class Person with a property commonName. In some cases the value of this property is nil.

    If I would like to watch this property, I do the following:

    // Suppose that p is of type Person var commonNameDynamic = Dynamic.asObservableFor(p, keyPath: "commonName")

    This causes a crash if the value of commonName is nil.

    Bug 
    opened by serieuxchat 13
  • Changed `ViewControllerLifecycle` implementation using swizzling

    Changed `ViewControllerLifecycle` implementation using swizzling

    Hello! This PR changes ViewControllerLifecycle implementation to a swizzling based approach because I noticed some issues with the existing child view controller approach:

    • If you have a UINavigationController and you start observing the events on it, the rootViewController of the navigation controller will be swapped with a WrapperViewController resulting in an empty view. TabBarController is potentially open to same type of issue.
    • If you are implementing a container view controller and need to also observe its lifecycle via reactive the children array could become hard to manage.
    • Some crash detection libraries inspects the view hierarchy when a crash happens and if there is a view controller containing the WrapperViewController as child, the stack trace messages become hard to understand because of these WrapperViewController noise.

    I don't like that this solution is based on swizzling but I believe there's no easy way to address this without creating a base UIViewController subclass that all the other VCs have to subclass. This piece of code was also untested and I added some tests on top of it now, let me know if you see any issue!

    cc @ibrahimkteish

    opened by fabfelici 0
  • Unable to run in simulator on M1 Macs

    Unable to run in simulator on M1 Macs

    I am unable to build and run my projects which use Bond since I have switched to a new M1 Mac. I get the following error:

    Could not find module 'Bond' for target 'x86_64-apple-ios-simulator'; found: arm64, arm64-apple-ios-simulator
    

    I have google'd this error and none of the workarounds seem to help. Is this something one of you have run into? I am only getting this error in my one project that uses ReactiveKit and Bond.

    opened by kevinrenskers 5
  • Swift Package Manager Dynamic Libraries

    Swift Package Manager Dynamic Libraries

    I want to propose that you add a dynamic library product to the Package.swift file.

    The issue I'm having is we have a Xcode workspace with a project to build a framework with reusable custom UI widgets along with the project for the application. Both have dependencies on Bond and ReactiveKit, so we added package dependencies in both projects for them. The application builds and runs from Xcode. However, Xcode seems to be deciding to automatically create dynamic frameworks for Bond, ReactiveKit, and Differ (it makes the static libraries too), and then embeds these frameworks into frameworks which can't be uploaded to App Store Connect.

    I did try removing the package dependencies from the UI framework project, and it does act like it picks up the static libraries and module map files from the build products directory, but the import of Bond shows an error "missing required module 'BNDProtocolProxyBase'" which has a .o file but no module map files.

    The other thing I tried was to add a SPM library that has a dynamic product to our framework project, and it doesn't embed the framework into the framework. Plus, Xcode allows selecting if the library is embedded. This is why I proposed to add a dynamic library product to the Package.swift to Bond, ReactiveKit, and Differ as it probably is the easiest way to keep Xcode from creating and embedding dynamic libraries.

    opened by brianmakenoise 0
  • Incorrect MutableObservableArray difference

    Incorrect MutableObservableArray difference

    Hello.

    I have an issue with diff and patch calculating. I receive nothing in these properties when have changes in the array. To check this, I have added zipPrevious and it shows that I have real changes, but not diff and patch.

    viewModel.bnd_items
      .zipPrevious()
      .observeNext { (old, items) in
        print(items.diff, items.patch, old?.collection == items.collection) 
        // Inserts: [], Deletes: [], Updates: [], Moves: [] [] false
      }
      .dispose(in: disposeBag)
    
    • ReactiveKit (3.17.4)
    • Bond (7.6.6):
      • Differ (~> 1.4)
      • ReactiveKit (~> 3.14)

    MacOS 11.2.3 (BigSur) Xcode 12.4 iOS 12.4-14.4

    opened by krotoff 0
  • Installation problems Xcode Version 12.4 (12D4e)

    Installation problems Xcode Version 12.4 (12D4e)

    Hi,

    I can't install bond with latest Xcode:

    *** Building scheme "Bond-macOS" in Bond.xcworkspace Build Failed Task failed with exit code 65: /usr/bin/xcrun xcodebuild -workspace /Users/me/Desktop/testi/Carthage/Checkouts/Bond/Bond.xcworkspace -scheme Bond-macOS -configuration Release -derivedDataPath /Users/ivo/Library/Caches/org.carthage.CarthageKit/DerivedData/12.4_12D4e/Bond/7.8.1 ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES archive VALIDATE_WORKSPACE=NO -archivePath /var/folders/38/yp11zkds4rgf49qr3mn23_ww0000gn/T/Bond SKIP_INSTALL=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=NO CLANG_ENABLE_CODE_COVERAGE=NO STRIP_INSTALLED_PRODUCT=NO (launched in /Users/me/Desktop/testi/Carthage/Checkouts/Bond).

    Cocoapod-Installation ist possible, but there are issues with differ etc., can't compile and run my project with bond (Swift 5).

    opened by fiveyears 0
  • Control Event not triggered when setting data inside viewModel to textField

    Control Event not triggered when setting data inside viewModel to textField

    I used bidirectionalBind to connect textfield.reactive.text with defined property inside vm. When I get data from presented VC via delegate inside vm, setting the textfieldText.value = "text" does not trigger any control event in viewController. Even if I observe textField.reactive.text.observeNext... it's not working. Any help please how to notify textChange? I can pass via delegate from vc to vm textField.sendActions(.editingChanged) when value is assigned but though might be better solution inside ReactiveKit/Bond. Thank you. @srdanrasic

    opened by HarisA92 0
Releases(7.8.1)
  • 7.8.1(Oct 31, 2020)

  • 7.8.0(Aug 5, 2020)

  • 7.7.1(Apr 21, 2020)

  • 7.7.0(Apr 20, 2020)

    Thanks to @ibrahimkteish's awesome contribution, Bond 7.7 introduces the ability to observe view controller lifecycle events! 🎉 🎈

    Example usage:

    let viewController = UIViewController()
    
    viewController.reactive.lifecycleEvents.observeNext { (event) in
        print(event)
    }
    
    viewController.reactive.lifecycleEvent(.viewDidLoad).observeNext {
        print("view did load")
    }
    
    viewController.reactive.lifecycleEvent(.viewWillAppear).observeNext {
        print("view will appear")
    }
    
    viewController.reactive.lifecycleEvent(.viewDidAppear).observeNext {
        print("view did appear")
    }
    
    viewController.reactive.lifecycleEvent(.viewWillDisappear).observeNext {
        print("view will disappear")
    }
    
    viewController.reactive.lifecycleEvent(.viewDidDisappear).observeNext {
        print("view did disappear")
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 7.6.6(Dec 17, 2019)

    • Make collectionView(_: numberOfItemsInSection:) open on CollectionViewBinderDataSource. Thanks @AnthonyMDev!
    • Fix KVO on iOS 10. Thanks @Sephiroth87.
    Source code(tar.gz)
    Source code(zip)
  • 7.6.5(Dec 15, 2019)

  • 7.6.4(Dec 13, 2019)

  • 7.6.3(Dec 12, 2019)

  • 7.6.2(Dec 12, 2019)

  • 7.6.1(Nov 13, 2019)

  • 6.10.5(Jul 8, 2019)

  • 7.6.0(Jun 30, 2019)

  • 7.5.0(Apr 27, 2019)

  • 7.4.3(Apr 18, 2019)

  • 7.4.2(Apr 12, 2019)

  • 7.4.1(Apr 2, 2019)

  • 7.4.0(Mar 31, 2019)

    🎊 Official Swift 5 support 🎉

    • Update code syntax to Swift 5
    • Update ReactiveKit dependency to 3.10

    Do not update if you are still using Swift 4.2 or lower.

    Source code(tar.gz)
    Source code(zip)
  • 6.10.4(Mar 31, 2019)

  • 7.3.3(Mar 26, 2019)

    • Add method replaceItems(ofSectionAt:with:performDiff:) to MutableObservableArray2D that can replace items of a section at the given index with new items and calculate change diff. For example:
    data.replaceItems(ofSectionAt: 1, with: [1, 100, 20], performDiff: true)
    
    Source code(tar.gz)
    Source code(zip)
  • 7.3.2(Mar 26, 2019)

  • 6.10.3(Mar 26, 2019)

  • 7.3.1(Mar 25, 2019)

  • 7.3.0(Mar 16, 2019)

    Bond v7.3 improves ergonomics around Tree types.

    Simpler conformance

    Trees no long need to be index-based. Conforming to TreeProtocol (renamed from TreeNodeProtocol) now requires only that the children property is provided.

    /// A protocol that provides abstraction over a tree type.
    /// A tree can be any containter type that encapsulates objects or values that are also trees.
    public protocol TreeProtocol {
    
        /// A collection of child nodes that are trees and whose children are also trees
        associatedtype Children: Collection where
            Children.Element: TreeProtocol,
            Children.Element.Children == Children,
            Children.Index == Int
    
        /// Child nodes of the current tree node.
        var children: Children { get }
    }
    

    Not that you'll ever need to conform UIView, bet let's use it as an example:

    extension UIView: TreeProtocol {
    
        public var children: [UIView] {
            return subviews
        }
    }
    

    This will automatically provide you index path subscripting capability:

    let pickerView = UIPickerView()
    
    let secondSubviewOfFirstSubview = pickerView[childAt: [0, 1]]
    

    Tree Views

    Consuming trees is now done through "tree views". Bond provides two views out of the box: DFS and BFS views thorough .depthFirst and .breadthFirst properties on any TreeProtocol.

    For example, the snippet

    for descendent in pickerView. depthFirst {
        print(descendent)
    }
    

    will print all descendent views of the pickerView in depth-first search order. Note that descendent implies not only children, but also children of the children and so on.

    Tree view is a flat collection of tree nodes. It's of type Collection whose indices are of type IndexPath. That means that we can do all the stuff on trees that we can do with collections, like:

    let firstImageViewDescendent = pickerView.breadthFirst.first(where: { $0 is UIImageView })
    
    let allVisible = pickerView.breadthFirst.allSatisfy { !$0.isHidden }
    
    let totalWidth = pickerView.breadthFirst.reduce(0, { $0 + $1.frame.size.width })
    

    Mutable Trees

    To add support for various mutating methods on the tree, conform to RangeReplaceableTreeProtocol protocol. All that is needed is to add setter to children property:

    extension UIView: RangeReplaceableTreeProtocol {
    
        public var children: [UIView] {
            get {
                return subviews
            }
            set {
                subviews.forEach { $0.removeFromSuperview() }
                newValue.forEach { addSubview($0) }
            }
        }
    }
    

    We can then do stuff like inserting, deleting or moving children:

    // Moves child at index path [0, 1] to index path [2, 3]
    pickerView.move(from: IndexPath(indexes: [0, 1]), to: IndexPath(indexes: [2, 3]))
    

    (Mutable) Observable Trees

    Now that our tree type conforms to TreeProtocol (or RangeReplaceableTreeProtocol) we can wrap it onto MutableObservableTree and use the wrapper to enable observation of the tree changes:

    let tree = MutableObservableTree(pickerView)
    
    tree.observeNext { changeset in
        print(changeset.collection, changeset.diff, changeset.patch)
    }
    
    tree.insert(UIImageView(), at: [0, 0])
    

    Simpler Array2D

    Array2D is refactored into a simple struct. Changes should be backward compatible if Array2D typealias has been used.

    public struct Array2D<SectionMetadata, Item>: Array2DProtocol {
    
        /// Represents a single section of Array2D.
        public struct Section {
    
            /// Section metadata, e.g. section title.
            public var metadata: SectionMetadata
    
            /// Items contained in the section.
            public var items: [Item]
        }
    
        /// All sections of Array2D.
        public var sections: [Section]
    
        /// Create a new Array2D with the given sections.
        public init(sections: [Section] = []) {
            self.sections = sections
        }
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 7.2.1(Mar 3, 2019)

    • Support for Swift 5 compiler (in Swift 4.2 compatibility mode).

    Note that this does not update the project to use Swift 5 syntax, it only makes the project compilable with Swift 5 compiler by reworking parts that are affected by Swift 5 breaking changes. You can keep using Swift 4.2 (Xcode 10/10.1).

    Source code(tar.gz)
    Source code(zip)
  • 7.2.0(Feb 17, 2019)

    • Add firstIndex method to trees (#587)
    • Fix broken removeAllItems method on trees / 2D arrays (#584)
    • Add support for binding of collection signals to changeset containers like MutableObservableArray (#580)
    • Add data source binding extensions to UIPickerView - thanks @jonathanfoster! (#577)
    Source code(tar.gz)
    Source code(zip)
  • 7.1.0(Feb 9, 2019)

    • Support for Tree diffing. Also implies Array2D diffing.

    You can now calculate diffs between trees. For example, a mutable tree can be updated like this:

    aMutableTree.replace(with: newTree, performDiff: true)
    
    Source code(tar.gz)
    Source code(zip)
  • 7.0.0(Dec 16, 2018)

    Bond 7 brings refactored observable collections that are much more powerful and makes it easy to customize binders and create your own variants of observable collections. Anything that conforms to Swift.Collection can now be made observable. Bond supports observable trees now! Check out observable collections documentation, new playgrounds in the project workspace and the migration guide.

    Bond 7 updates only observable collections APIs. All other APIs remain unchanged.

    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-beta.2(Nov 22, 2018)

    • Expose convenience collection methods on immutable changeset container.
    • Add removeSubrange() method to mutable observable array.
    • Make UIButton image and backgroundImage bonds of optional type.
    • Make table and collection view properties of binder data source open.
    Source code(tar.gz)
    Source code(zip)
  • 7.0.0-beta.1(Nov 11, 2018)

    Bond 7 brings refactored observable collections that are much more powerful and makes it easy to customize binders and create your own variants of observable collections. Anything that conforms to Swift.Collection can now be made observable. Bond also supports observable trees now! Check out observable collections documentation and new playgrounds in the project workspace.

    Bond 7 updates only observable collections APIs. All other APIs remain unchanged. APIs for use cases like creating, mutating and binding collections remain mostly unchanged, however there are breaking changes in the collection binders and the observable collection event type. Make sure to check out playgrounds in the project workspace to learn about new stuff.

    Source code(tar.gz)
    Source code(zip)
  • 6.10.2(Oct 13, 2018)

Owner
Declarative Hub
Declarative Hub
Swift Apps in a Swoosh! A modern framework for creating iOS apps, inspired by Redux.

Katana is a modern Swift framework for writing iOS applications' business logic that are testable and easy to reason about. Katana is strongly inspire

Bending Spoons 2.2k Jan 1, 2023
🐌 snail - An observables framework for Swift

?? snail A lightweight observables framework, also available in Kotlin Installation Carthage You can install Carthage with Homebrew using the followin

Compass 179 Nov 21, 2022
EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

Ali Samaiee 4 Oct 5, 2022
UI event handling using Apple's combine framework.

Description Combinative is a library for UI event handling using Apple's combine framework. It doesn't need many dependencies because it is written ma

noppefoxwolf 106 Jan 29, 2022
Open source implementation of Apple's Combine framework for processing values over time.

OpenCombine Open-source implementation of Apple's Combine framework for processing values over time. The main goal of this project is to provide a com

OpenCombine 2.4k Dec 26, 2022
RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way.

About Architecture concerns RxReduce Installation The key principles How to use RxReduce Tools and dependencies Travis CI Frameworks Platform Licence

RxSwift Community 125 Jan 29, 2022
SwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms

SwiftUI-compatible framework for building browser apps with WebAssembly At the moment Tokamak implements a very basic subset of SwiftUI. Its DOM rende

TokamakUI 2k Dec 30, 2022
SwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms

SwiftUI-compatible framework for building browser apps with WebAssembly At the moment Tokamak implements a very basic subset of SwiftUI. Its DOM rende

TokamakUI 2k Dec 29, 2022
Write great asynchronous code in Swift using futures and promises

BrightFutures How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven fu

Thomas Visser 1.9k Dec 20, 2022
Easy Swift Futures & Promises.

❗️ Archived now ❗️ Since Apple released Combine framework, I decide to archive this repo. You still can use this repo as an example of Future/Promise

Dmytro Mishchenko 40 Sep 23, 2022
Type-safe event handling for Swift

emitter-kit v5.2.2 A replacement for NSNotificationCenter#addObserver and NSObject#addObserver that is type-safe and not verbose. import EmitterKit /

Alec Larson 570 Nov 25, 2022
A Swift based Future/Promises Library for IOS and OS X.

FutureKit for Swift A Swift based Future/Promises Library for IOS and OS X. Note - The latest FutureKit is works 3.0 For Swift 2.x compatibility use v

null 759 Dec 2, 2022
📡 Helping you own NotificationCenter in Swift!

Notificationz ?? Helping you own NotificationCenter Highlights Keep Your Naming Conventions: This library gives you convenient access to NotificationC

Kitz 77 Feb 18, 2022
Observable is the easiest way to observe values in Swift.

Observable is the easiest way to observe values in Swift. How to Create an Observable and MutableObservable Using MutableObservable you can create and

Robert-Hein Hooijmans 368 Nov 9, 2022
Modern thread-safe and type-safe key-value observing for Swift and Objective-C

Now Archived and Forked PMKVObserver will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork o

Postmates Inc. 708 Jun 29, 2022
A library for reactive and unidirectional Swift applications

ReactorKit is a framework for a reactive and unidirectional Swift application architecture. This repository introduces the basic concept of ReactorKit

ReactorKit 2.5k Dec 28, 2022
ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift.

ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift. ReSwift helps you to separate three important concerns of your app's components.

null 7.3k Jan 9, 2023
Lightweight Promises for Swift & Obj-C

Tomorrowland Tomorrowland is an implementation of Promises for Swift and Objective-C. A Promise is a wrapper around an asynchronous task that provides

Lily Ballard 115 Nov 23, 2022
VueFlux is the architecture to manage state with unidirectional data flow 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