<div dir="ltr">Hi Ian,<div><br></div><div>Thanks for your insightful feedback. First, apologies for the broken link in the README. I just updated it (I accidentally linked to the wrong branch). (the actual demo can be found here: <a href="https://github.com/square/Cleanse/tree/master/Examples/CleanseGithubBrowser">https://github.com/square/Cleanse/tree/master/Examples/CleanseGithubBrowser</a>)</div><div><br></div><div>Anyways, you bring up some good points. I think Cleanse may actually solve a lot of your concerns, but it may be a symptom of my slapdash README. I&#39;m going to take a good documentation pass in the coming week and incorporate your feedback and others.</div><div><br></div><div>Going to try to address some of your concerns.</div><div class="gmail_extra"><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div>My first point of confusion is that these libraries seem to be less about dependency injection and more about service location. For instance, in your GitHub example in the playground, we see the following:</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">struct GithubListMembersServiceImpl : GithubListMembersService {<br>    let githubURL: TaggedProvider&lt;GithubBaseURL&gt;</font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif"> <br></font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">    func listMembers(organizationName: String, handler: [String] -&gt; ()) {<br>        let url = githubURL<br>            .get()<br>            .URLByAppendingPathComponent(&quot;orgs/\(organizationName)/public_members&quot;)<br>        let dataTask = urlSession.dataTaskWithURL(url) ...</font></blockquote><div><br></div><div>To my mind it doesn&#39;t look like githubURL was injected. It looks like it was looked up in a service locator. In fact, it appears that the Binder is a service locator, and you can simply set up multiple service locators for multiple environments (with the added flexibility of multiple types via tags). I call this solving the problem of inflexible global state (usually singletons) by making it a flexible global state (a global service locator).</div></div></blockquote><div><br></div><div>I&#39;m not very familiar with the service locator pattern to be honest, however, I can assure you that the base URL is being injected. I think there may be some confusion on either how the <span style="font-family:tahoma,sans-serif">GithubListMembersServiceImpl is being constructed, or tagged providers in general.</span></div><div><span style="font-family:tahoma,sans-serif"><br></span></div><div><span style="font-family:tahoma,sans-serif">For the former, there was feedback regarding the constructors being implicitly made for structs in this <a href="https://www.reddit.com/r/swift/comments/4o2lno/squarecleanse_swift_dependency_injection/d49pu6c">reddit thread</a>.</span></div><div><span style="font-family:tahoma,sans-serif"><br></span></div><div><font face="tahoma, sans-serif">As far as the TaggedProvider part, they&#39;re essentially a workaround for not being able to have <a href="https://github.com/google/guice/wiki/BindingAnnotations">Binding/Qualifier annotations</a> in swift</font></div><div><font face="tahoma, sans-serif"><br></font></div><div><font face="tahoma, sans-serif">In java/Guice </font><span style="font-family:tahoma,sans-serif">githubURL</span><font face="tahoma, sans-serif">, would be probably be request as either &quot;@GithubBaseURL URL&quot; or </font><span style="font-family:tahoma,sans-serif">&quot;@GithubBaseURL Provider&lt;URL&gt;&quot;.</span></div><div><span style="font-family:tahoma,sans-serif"><br></span></div><div><span style="font-family:tahoma,sans-serif">In the playground example, one could easily change the service implementation to be</span></div><div><span style="font-family:tahoma,sans-serif"><br></span></div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">struct GithubListMembersServiceImpl : GithubListMembersService {<br>    let githubURL: <b>NSURL</b></font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif"> <br></font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">    func listMembers(organizationName: String, handler: [String] -&gt; ()) {<br>        let url = githubURL<br>            .URLByAppendingPathComponent(&quot;orgs/\(organizationName)/public_members&quot;)<br>        let dataTask = urlSession.dataTaskWithURL(url) ...</font></blockquote></div><div><font face="tahoma, sans-serif"><br></font></div><div>and then change the binding statement to be</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">      binder<br>       .bind()<br>       .to(value: NSURL(string: &quot;<a href="https://api.github.com">https://api.github.com</a>&quot;)!)</blockquote><div><div><br></div><div>The downside of this in an actual application is that you may want to inject other URLs. These &quot;<a href="file:///Users/lewis/Development/appointments-cleansed/Frameworks/Cleanse/Documentation/_build/html/README.html#type-tags">Type Tags</a>&quot;  TaggedProviders are essentially just a way of wrapping a type (e.g. NSURL) and giving it a unique key. A lot of the necessity for TaggedProviders should go away once Cleanse has a concept of <a href="http://google.github.io/dagger/subcomponents.html">Subcomponents</a> and Scopes like in Dagger 2.</div><div> <br></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div>I&#39;ve always thought of dependency injection as being a tool for avoiding global state. But in this case it seems to me like the service locator is globally managed. The language of service locators is even present in Guice&#39;s <a href="https://github.com/google/guice/wiki/Motivation" target="_blank">excellent motivation page</a> (my emphasis):</div><div><br></div></div></blockquote><div><br class="">The only &quot;global&quot; state are things that are declared singletons. (which are still only singletons in the scope of each time one calls build). They have very similar semantics to <a href="https://github.com/google/guice/wiki/Scopes">singletons in Guice</a> (however no custom scopes yet as mentioned above).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Guice will inspect the annotated constructor, and <b><i>lookup</i></b> values for each parameter.<br></blockquote></div></blockquote><div> <br></div><div>Cleanse actually achieves the same thing, but without reflection. I attempted to explain it in this part of the <a href="https://github.com/square/Cleanse/blob/master/README.rst#terminating-methods-top1factory-p1---e-1st-arity">README</a>, but I&#39;ll be honest, I struggled while attempting to make it both simple and detailed enough to convey what&#39;s going on.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div>Another point I&#39;ve heard people talk about is that these libraries help as dependencies grow. I hope this isn&#39;t a straw man, as I interpret the following line in your README in that way.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">As the functionality of this app grows, one may add arguments to RootViewController and its dependencies as well as more modules to satisfy them.<br></blockquote><div><br></div><div>The argument seems to go that this is easy to maintain:</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">init(a: A)</font></blockquote><div><br></div><div>But this is difficult:</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">init(a: A, b: B, c: C, d: D, e: E, ...)</font></blockquote><div><br></div><div>The argument goes further by saying that it becomes especially difficult as you may need to pass the dependencies through the some nodes of the object graph that seem not to need them. For instance, in the object graph A -&gt; B -&gt; C, if C has a dependency on Foo but B does not, why should B know about Foo?</div><div><br></div><div>But I find that argument unconvincing on two grounds. First, I believe an object&#39;s dependencies are the union of its and its children&#39;s dependencies. In the example above, B has an implicit dependency on Foo. &quot;Skipping&quot; passing them around or instantiating them in the owning class is actually only hiding an object&#39;s true dependencies by using global state. Second, as a single object&#39;s dependencies become more unwieldy it seems in practice to indicate an architectural problem where objects are doing too much. These problems are related, as the architectural problem may not be as visible when the true dependencies are hidden!</div></div></blockquote><div><br></div><div>So this is an interesting point. I&#39;m a strong believer that DI solves this issue really well.</div><div><br></div><div>Let&#39;s take this example (I think its similar to the straw man you are referring to):</div><div><br></div><div><a href="https://gist.github.com/mikelikespie/c54a017677265322df7eb785d9f36345">https://gist.github.com/mikelikespie/c54a017677265322df7eb785d9f36345</a><br></div><div><br></div><div>Basically, VC_A, needs serviceA, and a VC_B to do its job, however, for it to create VC_B it needs VC_B&#39;s dependencies + its transitive dependencies which happen to be serviceA, serviceB, serviceC.</div><div><br></div><div>So I agree with the arguments made in the straw man, e.g. don&#39;t need to change the constructor hierarchy all the way down when VC_D needs a new service or needs to start depending on something. It takes a pretty large example to start to see these benefits, but its kind of like how you don&#39;t really see a benefit from a quicksort over a bubble sort until you have a lot of items to sort.</div><div><br></div><div>So if we refactor as is (without turning anything into protocols) to use Cleanse, we come up with something like:</div><div><br></div><div><a href="https://gist.github.com/mikelikespie/3abe371bf7f7ab67f71d6cfa22c0145d">https://gist.github.com/mikelikespie/3abe371bf7f7ab67f71d6cfa22c0145d</a><br></div><div><br></div><div>Now, say serviceD incurred a new dependency, one would just add it to its constructor and make sure the dependency is configured... don&#39;t have to change any of the other view controllers in the hierarchy.</div><div><br></div><div>This gets me to the next point, testing. VC_A&#39;s purpose is to call serviceA and present a view controller. To properly unit test its functionality, it may not be necessary to provide a concrete implementation of the VC it wants to present. In general if one can test a component and its functionality correctly with less dependencies that&#39;s better.</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><br></div><div>So to me — and with great respect for the work you&#39;ve done! I know a lot of people (even some of my teammates with Dagger) value this approach immensely — I don&#39;t personally see how the approach adds value over manual injection, and I think the complexity is a negative.</div></div></blockquote><div><br></div><div>I appreciate your feedback too! Hopefully will be able to reduce complexity with iterations and improve documentation.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div>I do think there is value in aiding with property injection for the cases where we can&#39;t use initializer injection (we do often use storyboards at my company, although we try to keep them small and unwieldy!), but I think those cases are solved with a few clever extensions. See post-script if you&#39;re curious.</div></div></blockquote><div><br></div><div>Cleanse <a href="https://github.com/square/Cleanse#property-injection">does support property injection</a>. My examples in the readme only really demonstrate using it for the AppDelegate, but it also makes sense for storyboard injection. It is very explicit though (less magic than other DI frameworks). I also think there would be room for a cleanse extension that specifically deals with storyboards. (I kinda like what RxSwift did with RxCocoa).</div><div><br></div><div>Here&#39;s what the previous example could look like using Storyboards + segues:</div><div><br></div><div><a href="https://gist.github.com/mikelikespie/db480360fe9b261cd3441c18a14bcbc9">https://gist.github.com/mikelikespie/db480360fe9b261cd3441c18a14bcbc9</a><br></div><div><br></div><div>Its definitely not terse and magical, but I think it is pretty explicit.</div><div><br></div><div>I&#39;ll check out post-script to see if there&#39;s anything to be learned though. I also plan on including an example app using storyboard in the future.</div><div><br></div><div><br></div><div>Thanks for your other examples too! I think we may be on the page on some of the property injection stuff, but it definitely shows some weakness in my documentation.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><br></div><div>So: I&#39;d love to know what sorts of use cases you find where this sort of library is very helpful.</div><div><br></div><div>Thanks,</div><div>Ian</div><div><br></div><div>For contrast, I&#39;ve been handling property injection on iOS with a few helpers, depending on how I want to do it.</div><div><br></div><div>For segues identified by identifier (at my company we do tend to use storyboards with medium frequency; although we keep them separated into small collections of scenes rather than large unwieldy ones), extensions help create code like this:<br></div><div><br></div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">let dependency: Dependency       // itself injected, or</font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">// var dependency: Dependency! // if it could not be injected at instantiation</font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif"> </font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {<br>    switch segueFromStoryboardSegue(segue) {<br>    case .GoToCustomViewController(let custom):<br>        custom.inject(dependency: dependency)<br>    }<br>}</font></blockquote></div><div><br></div><div>That&#39;s taken from some experiments I did at <a href="https://github.com/willowtreeapps/segue_handler" target="_blank">https://github.com/willowtreeapps/segue_handler</a><br></div><div><br></div><div>Or in simple cases where I mostly care about type, I can do things like this:</div><div><br></div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><font face="tahoma, sans-serif">override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {<br>    if let custom: CustomViewController = segue.destinationViewController.injectable() {<br>        custom.inject(dependency: dependency)<br>    }<br>}</font></blockquote></div><div><br></div><div>With the extension here: <a href="https://gist.github.com/ianterrell/5aa139ab4b835b85cfee8e0f4430b863" target="_blank">https://gist.github.com/ianterrell/5aa139ab4b835b85cfee8e0f4430b863</a></div><div><br></div><div>Finally, for that style of property injection a precondition in viewDidLoad or before usage in other types can ensure that the inject method was called.</div><div><br></div><div>In my mind the AppDelegate represents the root of my graph, and is generally responsible for setting up all the dependencies it needs to care about. For integration tests or XCUITests (such as I&#39;ve done, which is limited, which might be skewing my opinion), I use environment variables or launch options.</div><div><br></div><div><br></div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote"><div><div class="h5">On Tue, Jun 14, 2016 at 7:55 PM, Mike Lewis via swift-users <span dir="ltr">&lt;<a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a>&gt;</span> wrote:<br></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div><div class="h5"><div dir="ltr">Hi,<div><br></div><div>I&#39;ve recently open sourced a dependency injection framework for Swift called Cleanse. <a href="https://github.com/square/cleanse" target="_blank">https://github.com/square/cleanse</a></div><div><br></div><div>It does a couple things I&#39;d consider novel with the type system to make wiring up functions easy without having to use reflection or other runtime or compile-time hacks (I tried elaborating on it in this part of the README here <a href="https://github.com/square/Cleanse#dependency-requesting-terminating-methods" target="_blank">https://github.com/square/Cleanse#dependency-requesting-terminating-methods</a>)</div><div><br></div><div>Anyways, I&#39;m looking for feedback on the design and use of this library, and maybe strike up a discussion on language features that may make writing a library such as this be able to be &quot;cleaner&quot; in the future. Couple things that come to mind are custom annotations for qualifying types (instead of what we call &quot;type tags&quot;), variadic generic arguments (which we work around by code generating the various arities), or even a plugin architecture to achieve things similar to what can be done with Java annotation processors.</div><div><br></div><div>I&#39;d also be interested in feedback on some more of the implementation details. e.g. is using this to key objects by type a good thing or a terrible thing? <a href="https://github.com/square/Cleanse/blob/master/Cleanse/TypeKeyProtocol.swift" target="_blank">https://github.com/square/Cleanse/blob/master/Cleanse/TypeKeyProtocol.swift</a></div><div><br></div><div>Since Swift generics don&#39;t seem to be completely understood by the general population, was hoping I could get some concrete feedback here. Even would be stoked with a response to the tune of &quot;You don&#39;t need DI in Swift for reasons X, Y, and Z.&quot;</div><div><br></div><div>Thanks!</div><span><font color="#888888"><div>Mike Lewis</div></font></span></div>
<br></div></div><span class="">_______________________________________________<br>
swift-users mailing list<br>
<a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-users" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-users</a><br>
<br></span></blockquote></div><br></div>
</blockquote></div><br></div></div>