Coder Social home page Coder Social logo

bond's Introduction

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.

bond's People

Contributors

adamstener avatar adlai-holler avatar akbsteam avatar anthonymillersf avatar dhardiman avatar iandundas avatar ibrahimkteish avatar ivanmoskalev avatar jberkel avatar jechol avatar jeehut avatar jonathanfoster avatar leeroyding avatar manas-chaudhari avatar marciok avatar mattthousand avatar maurovc avatar mblsha avatar modnovolyk avatar morizotter avatar murrayds avatar npvisual avatar olyve avatar phiberjenz avatar srdanrasic avatar tomaz avatar tonyarnold avatar tonyxiao avatar xai3 avatar yanamura avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bond's Issues

CocoaPods support

I'd really, really love to use this in...every project I've ever worked on.

Any plans to add a podspec?

Chaining functions (map, filter and etc.)

Probably I'm missing something while trying to chain filter and map functions. It's just not working as expected. If I do something like this: someDyn.filter { $0 > 0 } ->| someBond it works fine. But when I add map after filter than map closure calls independently from filter:

someDyn
    .filter { $0 > 0 } 
    .map { println("I'm called every time.") } 
->| someBond

NSFetchedResultsController Support

It'd be great to be able to create a proxy DynamicArray<DynamicArray<T: NSManagedObject>> or similar out of an NSFetchedResultsController.

I'm working on building this but I've hit a roadblock. Namely, Core Data only gives us change details after the change has been made. didChangeObject didChangeSection. Is this a deal-breaker? Can anyone think of a way to make the dynamic array in spite of that?

I'm thinking the best way is to make a shadow array that tracks the FRC array, and change that. We won't be able to support updated objects (since my references to the managed objects will already be updated by the time I find out about it), but I think that's probably alright. I don't want to hurt performance too much, but as long as I don't read attributes from the NSManagedObjects, they shouldn't get unfaulted and waste much memory, right?

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.

class TextFieldDynamicHelper must allow textChanged to be public!

class TextFieldDynamicHelper: NSObject {

weak var control: NSTextField?
var listener: (String -> Void)?

init(control: NSTextField) {
    self.control = control
    super.init()
    control.target = self
    control.action = Selector("textChanged:")
}

//private JDH corrected
func textChanged(sender: AnyObject?) {
    self.listener?(control?.stringValue ?? "")
}

}

The method textChanged must be accessible from outside this file or else you get "unknown selector textChanged: sent to TextFieldDynamicHelper

Juan Dent

Set nil value on relation field of NSManagedObject throw NSInvalidArgumentException

dynamicObservableFor<T>(object: NSObject, #keyPath: String, #from: AnyObject? -> T, #to: T -> AnyObject?)

When using KVO binding I'm not able to set nil on Dynamic object.
The keyPath is pointing on relation property on NSManagedObject.
I'm getting:

ERROR:NSInvalidArgumentException -[NSNull managedObjectContext]: unrecognized selector sent to instance

The reason is when we want to set nil value, then NSNull object is set. CoreData check that relation is != nil and then try to check NSManagedContextObject on NSNull

The bug is here

object.setValue(to(value) ?? NSNull(), forKey: keyPath)

Clear weak Bonds

Dynamic should clear weak Bonds from the array. That should probably happen in didSet property observer when encountering a weak Bond.

WatchKit

Thanks for this great framework!
It would be nice to add support for the WatchKit UI elements...

Discussion about naming

Please feel free to close this issue if you disagree, but I don't think the current names of some of the classes in this library reflect their purpose as well as they could.

For many Cocoa developers, words like "bind", "bindings" and "observer" are familiar due to the long-standing Cocoa Bindings and KVO that this library replaces.

I think it might make things a little easier to follow for long time Cocoa developers if the class names were closer to their equivalents in Cocoa:

  • Dynamic could become Observable
  • InternalDynamic could become RetainingObservable (or some variant thereof — maybe StrongObservable)
  • Bond becomes Binding (this one is debatable given that "bond" isn't inaccurate)

These are just suggestions, but I'm hoping others have thoughts and input on this.

/cc @raven

Map proxy - caching needed!

Without that caching, each time I access the map proxy element, it is recreated and that causes me real pain sinceI have some status saved in my viewModels.

Scenario:
I have DynamicArray of Models retreived from api. That dynamic array I have mapped to DynamicArray of ViewModels that have some "status" Dynamic and this status is lost when I access the ViewModel another time :(

Listener only called once

Using Bond 2.0.1, in an empty project
When I write this basic code :

var foo = Dynamic<Int>(0)

foo ->> { foo in
    println("foo changed: \(foo)")
}

foo.value = 1
foo.value = 2
foo.value = 3
foo.value = 4

It logs :

foo changed: 0

but I think it should do this :

foo changed: 0
foo changed: 1
foo changed: 2
foo changed: 3
foo changed: 4

Read-only `Dynamic<T>`

Is there some kind of read only Dynamic<T>, that couldn't be changed from the outside?

filter, map functions

        var ints = [Int]()

        for var i = 0; i < 100; i++ {
            ints.append(i)
        }

        let arr = DynamicArray<Int>(ints)
        let filtered = arr.filter { i -> Bool in i % 3 == 0 }
        let mapped = filtered.map { i -> String in "\(i*i)" }

        NSLog("arr: \(arr.value): \(arr.count)")
        NSLog("filtered: \(filtered.value): \(filtered.count)")
        NSLog("mapped: \(mapped.value): \(mapped.count)")

        for i in mapped {
            NSLog("mapped item: \(i)")
        }

        arr.removeAll(false)

doesnt log any mapped elements, also count is 0 on both filtered and mapped, value is empty too.

Using Bond beyond MVVM

Is it possible to use Bond beyond view <-> viewModel binding. For example api response mapped to the User which is mapped to the label. Some pseudocode:

Api:

class Api {
   public class fun login(username: "username", password: "password") -> ??? {
      Request("") { response in
          ???
      }

      return ???
   }
}

ViewModel:

let user = Dynamic<User?>(nil)

Api.login(username: "username", password: "password") ->> dynamicUser 

ViewController:

let user = viewModel.user
user.map { $0.firstName } ->> label

Is it possible to do something like this and how would you do this?

Crash on DynamicArray.removeAll(true/false)

It is happening on one instance of an array.
Bond+Arrays.swift:499

ma array had 13 elements, the for loop got to idx 7 and crashed for obvious reason: there are only 6 elements left.

Issue with bidirectional bindings in NSTextField

I was trying to set a bidirectional binding on a NSTextField and I think I have spotted a bug (I am using Xcode 6.3. / Swift 1.2)

This is what I have:

@IBOutlet weak var textField: NSTextField!
let backedValue = Dynamic<String>("Moose")

func applicationDidFinishLaunching(aNotification: NSNotification) {
   backedValue <->> textField.dynText
}

When the textfield is changed, Bond attempts to trigger -[Bond.TextFieldDynamicHelper textChanged:] but it fails with "unrecognized selector sent to instance". After looking at Bond+NSTextField.swift I noticed that textChanged is declared as private:

Declarations marked private are not exposed to the Objective-C runtime if not otherwise annotated. IB outlets, IB actions, and Core Data managed properties remain exposed to Objective-C whatever their access level. If you need a private method or property to be callable from Objective-C (such as for an older API that uses a selector-based callback), add the @objc attribute to the declaration explicitly.

Perhaps adding @objc would be a good idea?

Commands support

It would be great if Bond library have commands (like in Xamarin Forms). I made some basic stuff for my own use, but if your are interested in it and if it's possible to add commands right into Bond library, feel free to take my example here.

For example we have ViewModel, and it contains some command:

let notBusy: Dynamic<Bool>
lazy var pushNextPage: Command<()> = Command(target: self, enabled: self.notBusy) {
    [unowned self] value, sender in
    self.doSomethingAsync()
}

And it binds to UIButton like so:

button ->> viewModel.pushNextPage

When notBusy changes it value, UIButton will also become enabled or disabled. And command executes on button's touchUpInside:.

Add Dynamic for DynamicArray's count

It would be great to add Dynamic for DynamicArray's count, so we could write something like:
viewModel.categories.dynCount.map { "($0)" } ->> cell.countLabel

"reduce" function semantics different to swift standard library and FRP

Thank you for creating this marvelous library.
I like it very much for it's simplicity and clarity.
The current FRP libraries for swift (RxSwift and ReactiveCocoa 3.0)
are maybe more powerful but by far not as consistent as SwiftBond, I think. (maybe because of backward compatibility reasons (to RAC 2.0 / reactive extensions)).

One little thing though that bothered me is the reduce function.
In other contexts (like the swift standard library, other functional languages and FRP APIs) it has different semantics:
e.g. in the swift standard library:

// Return the result of repeatedly calling 'combine' with an
// accumulated value initialized to 'initial' and each element of
// 'sequence', in turn.
func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: @noescape (U, S.Generator.Element) -> U) -> U

On the other hand what you mean, the combination of different Dynamics to one, when one dynamic changes is expressed in Rx and RAC with combineLatest.

This is confusing I think.

I'd like to propose

  1. a "real" reduce function which combines the value of the previous reduction with the new provided value, maybe to provide a Dynamic for an average value or a velocity computation from a sequence of screen coordinates.
func reduce<T, U>(dynamic: Dynamic<T>, initial: U, combine: T -> U) -> Dynamic<U>

A naming alternative would be "fold" or "foldl", but as the library is young, using "reduce" with the new meaning wouldn't hurt so much and would provide a consistency with the swift standard library.
2. to rename the original "reduce" function to "combine" or something like that

Swift Bond for UiLabel

I thank you for this wonderful program! It has helped me to solve other programming issues I have been grappling with for three weeks!

I am attempting a procedure much like the code below, intended to continuously change the title of a button to that of a Dynamic String variable. I can println(myButton.dynTitle.value) to the logs, but it will not change the actual title of myButton in the simulator.

I am new to programming, and would greatly appreciate your help.

@IBOutlet weak var myButton: UIButton!
var abc = Dynamic<String>("Original Button Title")

abc ->> myButton.dynTitle
abc.value = "Button Title Changed"

Many Thanks, Dan

Inline bond declarations not firing

When creating a Bond inline (i.e. without a let declaration), the Bond is not fired on updates. Example

let numberOfFollowers = Dynamic("Initial")
let myBond = Bond<String>() { value in
    println("Pre-defined bond. value: \(value).")
}
numberOfFollowers ->> myBond
numberOfFollowers ->> Bond<String>(){ value in
    println("Inline bond. value: \(value).")
}
numberOfFollowers.value = "Some Followers"

When this is run, the update to Some Followers is only fired on the pre-defined Bond, i.e. the log output is:

Pre-defined bond. value: Initial.
Inline bond. value: Initial.
Pre-defined bond. value: Some Followers.

Dynamic property not firing

I want to add a new Dynamic property to UIView

private var FrameDynamicHandleUIView: UInt8 = 0;

extension UIView {


    public var dynFrame: Dynamic<CGRect> {
        if let d: AnyObject = objc_getAssociatedObject(self, &FrameDynamicHandleUIView) {
            return (d as? Dynamic<CGRect>)!
        } else {
            let d = InternalDynamic<CGRect>(self.frame ?? CGRectZero)
            let bond = Bond<CGRect>() { [weak self] v in if let s = self { s.frame = v } }
            d.bindTo(bond, fire: false, strongly: false)
            d.retain(bond)
            objc_setAssociatedObject(self, &FrameDynamicHandleUIView, d, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
            return d
        }
    } 
}

and in my view

class customView:UIView{

...

   lazy var frameListener:Bond<CGRect> = Bond<CGRect>(){ [unowned self] frame in


        println("not firing")

    }

    func bind(){


        self.dynFrame ->| frameListener

    }
}

but frameListener is not firing when I change the View's .frame ( I had no success on either .origin, .origin.y etc..), Am I doing something wrong ?

UITextField shortcut expansion issue

Today I came across the following issue: when typing a shortcut into UITextField (can be defined at Settings.app → General → Keyboard → Shortcuts) and after pressing Return button on keyboard shortcut expands, text is replaced but nothing happens from the corresponding binding's perspective. That's because UIControlEventEditingChanged is not sent in this case. Just FYI. I don't think there's an easy way to fix this without resorting to some ugly hacks. RAC also seems to suffer from the same issue.

R: Add UICollectionView bond

Bond is really amazing. What would be awesome to have is bond for UICollectionView as there is one for UITableView

Setting DynamicArray.value should trigger change notifications

It could be either "remove all" -> "insert all new", or it could be some special "set" operation (which would correspond to UITableView.reloadData). I think the set operation makes the most sense, to avoid wasting performance when changing 10000 items to 10000 different items.

Using a simple `Bond<T>` to propagate side effects

I'm having some trouble figuring out how to use a bond for things that are not necessarily value transforms.

For instance, if I want to change the height of my label based on the updated text, I thought I could do something like this:

textLabel.dynText ->> Bond<String>() { _ in
    textLabel.sizeToFit()
}

However, that seems to only fire once. Changing the textLabel (even via another bond) does not cause the function to fire again.

Am I missing something?

Question about eventDynamic

In the code below the startSprint (and stopSprint) gets triggered as soon as I open the screen.

Am I doing something wrong?

    override func viewDidLoad() {
        super.viewDidLoad()

        startClick = startButton.eventDynamic().filter { $0 == UIControlEvents.TouchUpInside }  ->> {
            [unowned self] event in
            self.project!.startSprint()
        }

        stopClick = stopButton.eventDynamic().filter { $0 == UIControlEvents.TouchUpInside } ->> {
            [unowned self] event in
            self.project!.stopSprint()
        }
    }

Thanks.

Extending UIView to add dynWidth property

You've done a wonderful job with the Bond library! I've used it in two of my projects and it works great!
Now I would like extend it a little bit in my new project. Mainly, I want to add dynWidth property to UIView. I tried to replicate your code from Bond+UIView.swift. But I am stumbled at d.retain(bond). I cannot do it from my ViewController, because your method is marked internal. Am I doing something wrong, or you library is not designed to be extended the way I want it to be?

private var widthDynamicHandleUIView: UInt8 = 0;

extension UIView {

    public var dynWidth: Dynamic<CGFloat> {
        if let d: AnyObject = objc_getAssociatedObject(self, &widthDynamicHandleUIView) {
            return (d as? Dynamic<CGFloat>)!
        } else {
            let d = InternalDynamic<CGFloat>(self.frame.width, faulty:false)
            let bond = Bond<CGFloat>() { [weak self] v in if let s = self {
                var viewFrame = s.frame
                viewFrame.size.width = v
                s.frame = viewFrame
                }
            }
            d.bindTo(bond, fire: false, strongly: false)
            //d.retain(bond)
            objc_setAssociatedObject(self, &widthDynamicHandleUIView, d, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
            return d
        }
    }

}

Thank you!

Map function - use created Dynamic or bind it to other Dynamic?

I am doing this:

let dynDate = Dynamic<Double>.asObservableFor(model, keyPath: "validTo", defaultValue: 0).map { seconds -> NSDate in NSDate(timeIntervalSince1970: seconds / 1000) }

Now should I do this:

let relativeTime = Dynamic<String>("")
dynDate.map { d -> String in BETTimeFormatter.timeRelative(d) } ->> relativeTime

or

let relativeTime = dynDate.map { d -> String in BETTimeFormatter.timeRelative(d) }

Same with reduce, etc..

Common Protocol for all Dynamic<T> classes?

Hi there,

is there a possibility that you can introduce an empty protocol that all the Dynamic types implement, similar to this?

protocol DynamicProtocol {}

public class Dynamic<T> : DynamicProtocol {
}

We would like to declare an array or set that can store the dynamic instances, but can't do that if they don't all have a common extension.

We have this kind of thing in our code base:

    private description: String = ""
    private var dynDescription = Dynamic(description)
    private var dynNetAmount = Dynamic(Money())
    private var dynVatAmount = Dynamic(Money())
   ...

and it would be really cool if we can declare a container like this that store any Dynamic instance:

var dynamicValues : [ ValueType: DynamicProtocol ]
dynamicValues[.description] = dynDescription
dynamicValues[.vatAmount] = dynVatAmount
...

We cannot do this at the moment because of the generic Dynamic type, but it will be possible all the Dynamic classes implement a single protocol without methods.

Thanks,

R

NS?

Hi — I see integration with a bunch of UI* objects. What's the situation with NS*?

asObservableFor

Amazing helper.

I have just question about mapping it. Can I simply do

let title = Dynamic<String>.asObservableFor(model, keyPath: "competition.name", defaultValue: "").map { t -> String in t.uppercaseString }

without retaining the first Dynamic object returned by the asObservableFor?

Or is:

let dynName = Dynamic<String>.asObservableFor(model, keyPath: "competition.name", defaultValue: "")
let title = Dynamic<String>("")
dynName.map { t -> String in t.uppercaseString } ->> title

required?

UITableViewDataSourceBond should update in main thread

Currently UITableViewDataSourceBond is doing UITableView updates using the current thread where the Dynamic Array was updated. Since this is not always the main thread it will cause bugs and crashes.
Of course we could change the data model only using the main thread but it's a pain and it's not elegant. I think if would be much better to dispatch all UITableViewDataSourceBond UI updates to the main thread. (Same for collection views I guess!).

Potential KVO bindings issue

I'm not sure and didn't test yet, but it seems to be an issue, that removeObserver is called only from deinit of KVO Helper. It may be a situation, when observable NSObject can be deallocated before Dynamic object who observers an KVO object. In this case an app will crash on iOS 8.
So I suggest to give an option for user so that he could call removeObserver when it is really necessary.

Compiler issues with Swift 1.2 / Xcode 6.3 beta

With today's release of Xcode 6.3 beta 1, including Swift 1.2, the new as! operator is required to force a downcast. There are several uses that need to be updated in Bond, for example:

Bond/Bond/Bond+Foundation.swift:62:67: error: 'AnyObject?' is not convertible to 'T'; did you mean to use 'as!' to force downcast?
    let dynamic = DynamicExtended(object.valueForKeyPath(keyPath) as T)
                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
                                                                  as!

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.

UICollectionViewDataSourceBond crash on removeAll()

Well, the title says it. It works fine as long as you are scrolled at the first item of the collection view, The moment you are scrolled at some other cell than the first one it crashed.

#3  0x00000001000fa798 in Betty.DynamicArray.subscript.getter (Swift.Int) -> A at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:161
#4  0x00000001000fdef0 in Betty.(DynamicArrayMapProxy in _6B65D22929435F0224F3247BF6E10FE5).subscript.getter (Swift.Int) -> B at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:342
#5  0x0000000100203508 in Betty.CollectionViewDynamicArrayDataSource.collectionView (Betty.CollectionViewDynamicArrayDataSource)(ObjectiveC.UICollectionView, cellForItemAtIndexPath : ObjectiveC.NSIndexPath) -> ObjectiveC.UICollectionViewCell at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+UICollectionView.swift:29
#6  0x0000000100203610 in @objc Betty.CollectionViewDynamicArrayDataSource.collectionView (Betty.CollectionViewDynamicArrayDataSource)(ObjectiveC.UICollectionView, cellForItemAtIndexPath : ObjectiveC.NSIndexPath) -> ObjectiveC.UICollectionViewCell ()
#7  0x000000018ae5877c in -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] ()
#8  0x000000018ae56754 in -[UICollectionView _updateVisibleCellsNow:] ()
#9  0x000000018ae52004 in -[UICollectionView layoutSubviews] ()
#10 0x000000018adf1760 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
#11 0x000000018a739e1c in -[CALayer layoutSublayers] ()
#12 0x000000018a734884 in CA::Layer::layout_if_needed(CA::Transaction*) ()
#13 0x000000018ae05f94 in -[UIView(Hierarchy) layoutBelowIfNeeded] ()
#14 0x000000018b484a58 in -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:] ()
#15 0x0000000100209184 in Betty.(UICollectionViewDataSourceSectionBond in _F54095F5E72ACDD38E8D6BDF23A22212).(init <A>(Betty.(UICollectionViewDataSourceSectionBond in _F54095F5E72ACDD38E8D6BDF23A22212)<A>.Type) -> (collectionView : Swift.Optional<ObjectiveC.UICollectionView>, section : Swift.Int) -> Betty.(UICollectionViewDataSourceSectionBond in _F54095F5E72ACDD38E8D6BDF23A22212)<A>).(closure #2) at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+UICollectionView.swift:64
#16 0x0000000100208e30 in reabstraction thunk helper <T_0_0> from @callee_owned (@owned Betty.DynamicArray<ObjectiveC.UICollectionViewCell>, @owned Swift.Array<Swift.Int>) -> (@unowned ()) to @callee_owned (@in (Betty.DynamicArray<ObjectiveC.UICollectionViewCell>, Swift.Array<Swift.Int>)) -> (@out ()) ()
#17 0x0000000100102390 in reabstraction thunk helper <T_0_0> from @callee_owned (@in (Betty.DynamicArray<T_0_0>, Swift.Array<Swift.Int>)) -> (@out ()) to @callee_owned (@owned Betty.DynamicArray<T_0_0>, @owned Swift.Array<Swift.Int>) -> (@unowned ()) ()
#18 0x00000001000fbd08 in Betty.DynamicArray.(dispatchDidRemove in _6B65D22929435F0224F3247BF6E10FE5) <A>(Betty.DynamicArray<A>)(Swift.Array<Swift.Int>) -> () at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:207
#19 0x0000000100102690 in Betty.(DynamicArrayMapProxy in _6B65D22929435F0224F3247BF6E10FE5).(init <A, B>(Betty.(DynamicArrayMapProxy in _6B65D22929435F0224F3247BF6E10FE5)<A, B>.Type) -> (sourceArray : Betty.DynamicArray<A>, mapf : (A, Swift.Int) -> B) -> Betty.(DynamicArrayMapProxy in _6B65D22929435F0224F3247BF6E10FE5)<A, B>).(closure #4) at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:272
#20 0x00000001001024e0 in reabstraction thunk helper <T_0_0, T_0_1> from @callee_owned (@owned Betty.DynamicArray<T_0_0>, @owned Swift.Array<Swift.Int>) -> (@unowned ()) to @callee_owned (@in (Betty.DynamicArray<T_0_0>, Swift.Array<Swift.Int>)) -> (@out ()) ()
#21 0x0000000100102390 in reabstraction thunk helper <T_0_0> from @callee_owned (@in (Betty.DynamicArray<T_0_0>, Swift.Array<Swift.Int>)) -> (@out ()) to @callee_owned (@owned Betty.DynamicArray<T_0_0>, @owned Swift.Array<Swift.Int>) -> (@unowned ()) ()
#22 0x00000001000fbd08 in Betty.DynamicArray.(dispatchDidRemove in _6B65D22929435F0224F3247BF6E10FE5) <A>(Betty.DynamicArray<A>)(Swift.Array<Swift.Int>) -> () at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:207
#23 0x00000001000fa714 in Betty.DynamicArray.removeAll <A>(Betty.DynamicArray<A>)(Swift.Bool) -> () at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:156
#24 0x00000001000a4e28 in Betty.EventsViewModel.(init (Betty.EventsViewModel.Type) -> (dynamicEvents : Betty.DynamicArray<ObjectiveC.BETEventModel>) -> Betty.EventsViewModel).(closure #3) at /Users/zdenektopic/betty_ios/Betty/Betty/EventsViewModel.swift:40
#25 0x00000001000a4c14 in reabstraction thunk helper from @callee_owned (@owned Betty.DynamicArray<ObjectiveC.BETEventModel>, @owned Swift.Array<Swift.Int>) -> (@unowned ()) to @callee_owned (@in (Betty.DynamicArray<ObjectiveC.BETEventModel>, Swift.Array<Swift.Int>)) -> (@out ()) ()
#26 0x0000000100102390 in reabstraction thunk helper <T_0_0> from @callee_owned (@in (Betty.DynamicArray<T_0_0>, Swift.Array<Swift.Int>)) -> (@out ()) to @callee_owned (@owned Betty.DynamicArray<T_0_0>, @owned Swift.Array<Swift.Int>) -> (@unowned ()) ()
#27 0x00000001000fbd08 in Betty.DynamicArray.(dispatchDidRemove in _6B65D22929435F0224F3247BF6E10FE5) <A>(Betty.DynamicArray<A>)(Swift.Array<Swift.Int>) -> () at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:207
#28 0x00000001000fa714 in Betty.DynamicArray.removeAll <A>(Betty.DynamicArray<A>)(Swift.Bool) -> () at /Users/zdenektopic/betty_ios/Betty/Vendor/Bond/Bond/Bond+Arrays.swift:156

For some reason even when the removeAll is called, the datasource cellForItemAtIndexPath is called at Bond+UICollectionView.swift:29

Clone a DynamicArray value

It would be nice if you could clone a dynamic value, the clone would still change if the elements on the original one changes and vice-versa but new values on the clone wouldn't affect the original.

My use case is: I have a tableview and a DynamicArray and I would like the first row to be a default value but without changing the my DynamicArray

        let projects: DynamicArray<Project> = userViewModel.projects

        projects.clone().insert(DefaultProject(), atIndex: 0).map { [unowned self] (project: Project) -> ProjectCellView in
            let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as ProjectCellView
            project.viewModel().name ->> cell.nameLabel
            return cell
        } ->> self.tableView

I would be happy with any other suggestion too :)

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.