Giter Club home page Giter Club logo

dip-ui's Introduction

No Maintenance Intended Version Carthage Compatible License Platform Swift Version

Dip-UI is a part of Dip since version 7.0.0. Thir repository will no longer be maintained.

Dip-UI

Dip-UI is an extension for Dip that provides support for dependency injection using Dip in applications that utilize storyboards and nib files.

Installation

You can install Dip-UI using your favorite dependency manager:

CocoaPods: pod "Dip-UI"

You need at least 1.1.0.rc.2 version of CocoaPods.

Carthage: github "AliSoftware/Dip-UI"

Usage

Dip-UI provides a unified and simple pattern to resolve dependencies of view controllers (or any other NSObject's) created by storyboards.

Let's say you want to use Dip to inject dependencies in MyViewController class defined like this:

class MyViewController: UIViewController {
  var logger: Logger?
  var tracker: Tracker?
  var router: Router?
  var presenter: MyViewControllerPresenter?
  var service: MyViewControllerService?
  
  /*...*/
}

Note 1: Though constructor injection is a preferred way to inject dependencies, in this case we can not use it - we can not make storyboards to use custom constructor. We could do it using subclass of UI(NS)Storyboard and method-swizzling, but you don't expect such things in a Swift framework.

Note 2: Implicitly unwrapped optionals are used here to indicate that these dependencies are required for this class. You don't have to follow this pattern and are free to use plain optionals if you prefer.

To inject dependencies in this view controller when it is instantiated from storyboard you need to follow next steps:

  • Register the dependencies in the DependencyContainer, as well as MyViewController:
import DipUI

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  let container = DependencyContainer { container in
    container.register(.singleton) { LoggerImp() as Logger }
    container.register(.singleton) { TrackerImp() as Tracker }
    container.register(.singleton) { RouterImp() as Router }
    container.register { MyViewControllerPresenterImp() as MyViewControllerPresenter }
    container.register { MyViewControllerServiceImp() as MyViewControllerService }
    
    container.register(storyboardType: MyViewController.self, tag: "myVC")
      .resolvingProperties { container, controller in
        controller.logger     = try container.resolve() as Logger
        controller.tracker    = try container.resolve() as Tracker
        controller.router     = try container.resolve() as Router
        controller.presenter  = try container.resolve() as MyViewControllerPresenter
        controller.service    = try container.resolve() as MyViewControllerService
      }
      
      DependencyContainer.uiContainers = [container]
  }
}

Note: All the depdencies are registered as implementations of abstractions (protocols). MyViewController is registered as concrete type. To register your view controller as a protocol read here.

  • Set the container as one that will be used to inject dependencies in objects created by storyboards. You do it by setting static uiContainers property of DependencyContainer class:
DependencyContainer.uiContainers = [container]
  • Make your view controller class conform to StoryboardInstantiatable protocol:
extension MyViewController: StoryboardInstantiatable { }

Tip: Do that in the Composition Root to avoid coupling your view controller's code with Dip.

  • In a storyboard (or in a nib file) set Dip Tag attribute on your view controller. This value will be used to lookup definition for view controller, so it should be the same value that you used to register view controller in the container.

img

Note: remember that DependencyContainer fallbacks to not-tagged definition if it does not find tagged definition, so you may register your view controller without tag, but you still need to set it in a storyboard. In this case you can use Nil attribute type instead of String.

Now when view controller will be loaded from a storyboard Dip-UI will intercept the setter of dipTag property and will ask DependencyContainer.uiContainer to resolve its dependencies.

StoryboardInstantiatable

StoryboardInstantiatable protocol defines single method didInstantiateFromStoryboard(_:tag:) and provides its default implementation. In most cases you will not need to override it. But if you register your view controller as an impementation of some protocol instead of concrete type, or want to perform some pre/post actions, you will need to override it like this:

container.register { MyViewController() as MyScene }
extension MyViewController: StoryboardInstantiatable {
  func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag?) throws {
    try container.resolveDependenciesOf(self as MyScene, tag: tag)
  }
}

License

Dip-UI is available under the MIT license. See the LICENSE file for more info.

dip-ui's People

Contributors

ilyapuchka avatar lightsprint09 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

Watchers

 avatar  avatar  avatar  avatar  avatar

dip-ui's Issues

Make registering ViewControllers more save

Would it make sense to have an extension on DependencyContainer like the following:

extension DependencyContainer {
  public func registerViewController<T: StoryboardInstantiatable>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping () throws -> T) -> Dip.Definition<T, ()> {
    return register(scope, type: type, tag: tag, factory: factory)
  }
}

This would help to not forget to conform to StoryboardInstantiatable .

Can't inject into NSSplitViewController

I have followed the exact steps as I use with NSViewController subclasses but the dependencies never get injected into NSSplitViewController subclasses.

I have tested whether the setter for a property (that I called "blah") on NSObject is called when I set a runtime attribute (just like "dipTag"), and it appears that it never gets called and I suspect this is why the dependencies are not injected.

Do you have a workaround for this that you know of? At the moment I'm considering using NSViewController subclasses with a child NSSplitView to get it to work, but that makes it harder to have view controllers associated with the child views.

I know I can access the container in the app delegate as well, but I like keeping the wiring out of the view component if possible

How to resolve UIViewController with runtime arguments?

Hi,

I need to resolve a UIViewController with runtime arguments to do something like this following:

container.register(.unique) { (id: String, object: MyProtocol?) in try MyViewModel(repository: container.resolve(), id: id, object: object) as MyViewModelProtocol }

container.register(storyboardType: MyUIViewController.self, tag: "MyTag") .resolvingProperties { container, vc in vc.viewModel = try container.resolve(arguments: id, object) as MyViewModelProtocol }

Is it possible?

Thanks.

Superfluous error messages using multiple UI Containers

Hello! There are couple of modules in my VIPER-designed app. Each of them has separate DependencyContainer and each of them contains UIViewController that is instantiated from Storyboard.

func configureContainers(container rootContainer: DependencyContainer) {
    Dip.logLevel = .Errors
    DependencyContainer.uiContainers = [rootModule,
                                        homeModule,
                                        dockModule,
                                        settingsModule,
                                        listModule,
                                        previewModule]
    ...
}
let rootModule = DependencyContainer { container in
    let rootController = container.register(.shared, tag: "Root") { RootViewController() }
    ....
}

When these dependencies are being resolved during app execution, there are a lot of error messages in the console log. Something like:

No definition registered for type: SomeViewController, arguments: (), tag: String("Some").
Check the tag, type you try to resolve, number, order and types of runtime arguments passed to `resolve()` and match them with registered factories for type SomeViewController.

This messages are emitted by these lines in NSObject extension in dipTag setter:

for container in DependencyContainer.uiContainers {
        do {
          try instantiatable.didInstantiateFromStoryboard(container, tag: tag)
          return
        } catch { }
      }

The reason is in how Dip "finds" proper container for instantiating storyboard object. And it definitely doesn't look too pretty.
It looks like these error messages can be safely ignored, but in my opinion it would be better to get rid of them. They give a wrong idea to the users, who will think that they do something wrong.

Or maybe it's me who does something wrong :)

Спасибо за полезный фреймворк, желаю удачи :)

Swift 3 problem

Hello,

I saw thread "Swift 3 issues" in Issues page of Dip library and I am wondering whether problems mentioned there applies also to DipUI library. Since I updated library to version 1.0.0, I am experiencing problem when instantiating controller from storyboard with DipUI.
Here is sample code for container controller registration:

container.register(tag: "ViewController") {
            ViewController()
            } .resolvingProperties { (container, entity) in
            entity.viewModel = try! container.resolve() as ViewModel
        }

I have storyboard with viewController with dipTag "ViewController". When I try to instantiate VC, following error occurs:
fatal error: attempt to bridge an implicitly unwrapped optional containing nil
in line 46 in AutoInjection.swift in Dip library.

Has any one found solution for this? Or should we wait for Swift 3.1? This method worked with Swift 2.3 and DipUI 0.2.2

Does not build with Xcode 9 b4

When building via carthage 0.24.0 and Xcode 9 beta4, we get an error.

When using master all works fine. Maybe a new release or a swift3.2/4.0 would be good.

DipUI doesn't build from Carthage on XCode 8

I've tried in Swift 3.0 project.
"Use Legacy Swift Language version" set to NO

Cartfile:
github "AliSoftware/Dip-UI"

Build log:
/usr/bin/xcrun xcodebuild -project /Users/Stan/tmp/DipUUUI/Carthage/Checkouts/Dip-UI/DipUI/DipUI.xcodeproj -scheme DipUI-iOS -configuration Release -sdk iphoneos ONLY_ACTIVE_ARCH=NO BITCODE_GENERATION_MODE=bitcode CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES clean buildBuild settings from command line:
BITCODE_GENERATION_MODE = bitcode
CARTHAGE = YES
CODE_SIGN_IDENTITY =
CODE_SIGNING_REQUIRED = NO
ONLY_ACTIVE_ARCH = NO
SDKROOT = iphoneos10.0

=== CLEAN TARGET DipUI-iOS OF PROJECT DipUI WITH CONFIGURATION Release ===

Check dependencies
“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.
“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

=== BUILD TARGET DipUI-iOS OF PROJECT DipUI WITH CONFIGURATION Release ===

Check dependencies
“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.
“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

Command line build output:
carthage update --platform iOS
*** Fetching Dip-UI
*** Fetching Dip
*** Downloading Dip.framework binary at "4.6.1"
*** Checking out Dip-UI at "0.2.2"
*** xcodebuild output can be found in /var/folders/q1/6l276hx53m590df28r4rvpv00000gn/T/carthage-xcodebuild.dvMvGI.log
*** Building scheme "DipUI-iOS" in DipUI.xcodeproj
** CLEAN FAILED **

The following build commands failed:
Check dependencies
(1 failure)
** BUILD FAILED **

The following build commands failed:
Check dependencies
(1 failure)
A shell task (/usr/bin/xcrun xcodebuild -project /Users/Stan/tmp/DipUUUI/Carthage/Checkouts/Dip-UI/DipUI/DipUI.xcodeproj -scheme DipUI-iOS -configuration Release -sdk iphoneos ONLY_ACTIVE_ARCH=NO BITCODE_GENERATION_MODE=bitcode CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES clean build) failed with exit code 65:
** CLEAN FAILED **

The following build commands failed:
Check dependencies
(1 failure)
** BUILD FAILED **

The following build commands failed:
Check dependencies
(1 failure)

Setup travis integration

Travis lets setup only repos with administrative access, so I can not do that. I've added travis script, but you have to enable integration yourself, @AliSoftware

Cocoapods Dip version dependency resolving problems

In my Podfile I set Dip version to 7.0 and Dip-UI version was automatically redused to 0.2.2 version.
When I set Dip-UI version to 2.0, I got an error during pod update

[!] CocoaPods could not find compatible versions for pod "Dip":
  In snapshot (Podfile.lock):
    Dip (= 7.0.0, >= 4.1.0, ~> 7.0)

  In Podfile:
    Dip (~> 7.0)

    Dip-UI (~> 2.0) was resolved to 2.0, which depends on
      Dip (~> 6.0)

Is it possible to inject into NSWindowController instances?

Hi, I've been using Dip for a week or two with the UI extensions, and it's working great for my NSViewControllers, but I haven't been able to find a way to inject into NSWindowControllers so far.

It conforms to the StoryboardInstantiatable protocol, and there is a dipTag on the NSWindowController subclass but it's getting injected into. I also tried using the auto inject class to resolve the dependencies, and that was to no avail also.

I have a workaround in mind, by setting the isInitialController to false and creating the window in my app delegate in order to inject but it's not ideal.

I'm happy to concede that I have made a mistake somewhere, but I can't see it if I have.

Thanks in advance for any help you may be able to provide.

Connor

Edit: I can auto inject using the Injected class, it was a badly defined dependency, but would like to define the dependencies in the container initialisation.

Further edit: My above edit was incorrect, I still cannot get it to work.

P.S. I love how easy it was to implement, kudos on the framework, and I love the weakSingleton component scope, it makes sharing my view models really easy.

Strange behavior with several uiContainers

Hi! In our project we wanted to use different uiContainers for different userStories, but we stuck in some sort of issue.

what happened:
at first we were using one uiContainer and it works like a charm 👍

but then, as we added second container, some stage things start to appear

WE've added second container like
DependencyContainer.uiContainers.append(storyBoardContainer)

btw, first uiContainer was added just like this too.

And then, when controllers from new user story must be initialized, console wrote smth like

No definition registered for type: HotelSearchVC, arguments: (), tag: nil.
Check the tag, type you try to resolve, number, order and types of runtime arguments passed to resolve() and match them with registered factories for type HotelSearchVC.

BUT! though it wrote this in console, it also resolves injected properties like it was made to.

Then, we tried to use containers like in Readme
DependencyContainer.uiContainers = [storyBoardContainer]

and it starts working like it should.

So, the question is:

  • Is uiContainers can't work with several containers? Or is there something that we're doing wrong?

1.0.0 installation issue

Hello,

I've noticed that new version of DipUI was released - "1.0.0", unfortunately I was unable to install it through CocoaPods. Version 0.2.2 pops up as latest. Has anyone experienced it?

BTW Thanks for this nice library.

Resolve view controllers in code

Hi! I configured my view controller following a guide and I try to resolve an instance of view controller like this:

let viewController = try! container.resolve(tag: "myVC") as TestViewController

I get the instance of TestViewController, but it doesn’t have outlets and all the stuff that I set in the storyboard. Does DipUI work only when you create all view controllers with segues? Is there any way to resolve instances in code?

Can't inject dependency in UITabBarController

I have main UITabBarController with many view controllers. But method didInstantiateFromStoryboard doesn't call (for UITabBarController and view controllers in this tabbarcontroller ). dipTag and container register and added to uiContainers.

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.