Search code examples
delphifiremonkeyspring4d

DI container can't resolve FMX controls


I have following project:

MyForm unit(just empty form):

unit uMyForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs;

type
  TMyForm = class(TForm)
  end;

implementation

{$R *.fmx}

end.

App Unit:

unit App;

interface

uses
    uMyForm,
    Spring.Container;

type
    TApp = class
    private
        _myForm: TMyForm;
    public
        [Inject]
        constructor Create(myForm: TMyForm);
    end;

implementation

uses
  System.SysUtils;

{ TApp }

constructor TApp.Create(myForm: TMyForm);
begin
    _myForm := myForm;
end;

end.

And build code:

procedure BuildProject;
begin
    GlobalContainer.RegisterType<TApp>;
    GlobalContainer.RegisterType<TMyForm>;
    GlobalContainer.Build;

    _app := GlobalContainer.Resolve<TApp>;
end;

Run BuildProject() causes Error: "Cannot resolve type: TMyForm". I was testing same configuration on VCL platform and there everything is ok. Do you have any idea what is wrong here?

Edit1: I had to change problem description because I was wrong thinking that the problem occurs on both(VCL and FMX) platforms. @RudyVelthuis 's comment showed me that problem is only on FMX platform.


Solution

  • During registration there are two different kinds of types:

    • component type - meaning the underlying type that is being constructed (typically a class)
    • service type - a type that can be resolved - this can also be the component type

    When using RegisterType<T> you only specify the component type.

    If you do not explicitly specify a service type for the component type the container will make a best guess.

    During the Build it inspects the component type for any interfaces and registers those as service types (the only exception being IComponentReference implemented by TComponent). If it does not find any it will register the class type itself as service type.

    That usually works with VCL (because of the exclusion of IComponentReference) but not FMX because there all the classes implement many different interfaces.

    Using the RegisterType overload with two generic parameters (the first one is the service type, second the component type) or adding one or more Implements calls will explicitly register one or more service type(s) for the component type.