Application get stuck at SplashScreen after MvvmCross upgrade to 8.x - xamarin

I upgraded all MvvmCross libraries from 7.1.2 to 8.0.0. According to "Upgrade from 7 to 8" tutorial from mvvmcross documentation, I have implemented necessary changes (override CreateLogProvider and CreateLogFactory methods and added iocProvider as parameter to InitializeFirstChance, InitializeLastChance, InitializeNavigationService and CreateApp):
public class Setup : MvxAndroidSetup
{
protected override IMvxAndroidViewPresenter CreateViewPresenter() => new HistoryViewPresenter(ViewAssemblies);
protected override void InitializeFirstChance(IMvxIoCProvider iocProvider)
{
base.InitializeFirstChance(iocProvider);
//Things are done here
}
protected override void InitializeLastChance(IMvxIoCProvider iocProvider)
{
base.InitializeLastChance(iocProvider);
//Things are done here
}
protected override IMvxNavigationService InitializeNavigationService(IMvxIoCProvider iocProvider)
{
var loader = CreateViewModelLoader(iocProvider);
Mvx.IoCProvider.RegisterSingleton<IMvxViewModelLoader>(loader);
InitializeViewDispatcher(iocProvider);
var dispatcher = CreateViewDispatcher();
Mvx.IoCProvider.RegisterSingleton(dispatcher);
var navigationService = new DeepNavigationService(null, loader, dispatcher, iocProvider);
Mvx.IoCProvider.RegisterSingleton<IMvxNavigationService>(navigationService);
return navigationService;
}
//Another things are done here
protected override IMvxApplication CreateApp(IMvxIoCProvider iocProvider)
{
return new App();
}
protected override ILoggerProvider CreateLogProvider()
{
return new SerilogLoggerProvider();
}
protected override ILoggerFactory CreateLogFactory()
{
Serilog.Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.CreateLogger();
return new SerilogLoggerFactory();
}
}
I have also created custom app start in Core project and set this class RegisterCustomAppStart:
public class AppStart : MvxAppStart
{
public AppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService) { }
protected override Task NavigateToFirstViewModel(object hint = null)
{
return NavigationService.Navigate<StartupViewModel>();
}
}
public class App : MvxApplication
{
public override void Initialize()
{
//Services and Singletons are registered here.
RegisterCustomAppStart<AppStart>();
}
}
And here is the issue- when I run app, my app get stuck in SplashScreen. Did anyone face similar issue? On the previous version, everything was fine. I would be grateful for some advice how to fix that

I resolved this issue - I changed line:
var dispatcher = CreateViewDispatcher();
For:
var dispatcher = Mvx.IoCProvider.Resolve<IMvxViewDispatcher>();
And it works.

Related

Register and Resolving Dependencies on Request Based on Param (Autofac WEB API)

I have an Application with autofac dependency injection and I wanted to use a specific dll extension based on the parameter I have on the request.
Here's my global.asax where I initialize autofac.
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());
containerBuilder.RegisterModule<ExModule>();
var container = containerBuilder.Build();
container.Resolve<IArtigoErp>();
Here's the autofac module where I load register my DLL's
public class ExModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
//Load DLL1 from folder and register it
RegistaDepedencias<IArtigoErp>(builder, "DLL1");
//Load DLL2 from folder and register it
RegistaDepedencias<IArtigoErp>(builder, "DLL2");
}
private void RegistaDepedencias<T>(ContainerBuilder builder, string NomeDll)
{
RegisterDep<T>(GetEnumerableTypes<T>(NomeDll), builder);
}
private void RegisterDep<T>(IEnumerable<Type> types, ContainerBuilder builder)
{
foreach (var t in types)
{
builder.RegisterType(t).As<T>();
}
}
private IEnumerable<Type> GetEnumerableTypes<T>(string NomeDll)
{
return Directory.EnumerateFiles(Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "Engine"))
.Where(x => x.Contains(NomeDll) && x.EndsWith(NomeDll +".dll"))
.Select(x => Assembly.LoadFrom(x))
.SelectMany(x => x.GetTypes()
.Where(t => typeof(T).IsAssignableFrom(t) && t.IsClass));
}
}
Both my DLL's have a class that extend from IArtigoErp.
So the ideia is, based on the parameter I get on my request, I want to run the method in either DLL1 or DLL2.
Example:
if(param == 1)
_artigoErp.GetLista(); // In DLL1
if(param == 2)
_artigoErp.GetLista(); // In DLL2
EDIT 1:
The parameter comes from the post method as it follows (Guid IdLoja)
public class ArtigoController : ApiController
{
private readonly IArtigoErp _artigoErp;
private readonly IArtigoLoja _artigoLoja;
public ArtigoController(IArtigoErp artigoErp, IArtigoLoja artigoLoja)
{
_artigoErp = artigoErp;
_artigoLoja = artigoLoja;
}
[Route("PostArtigos")]
public CallResponse PostArtigos([FromBody] Guid IdLoja)
{
}
}
I guess we can also process this in the begin_request method in global.asax
Thanks in advance.

Xamarin Android Share Link/Text via social media from custom renderer

I wan't to share a link via social media from custom renderer
public class CustomActions : ICustomActions
{
Context context = Android.App.Application.Context;
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
context.StartActivity(Intent.CreateChooser(sharingInt, "Share via"));
}
}
This error occur
Android.Util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
even when I added the below code I still get same error
sharingInt.AddFlags(ActivityFlags.NewTask);
The problem is that Intent.CreateChooser creates yet another Intent. What you want to do is to set the flag on this new intent:
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
var intent = Intent.CreateChooser(sharingInt, "Share via");
intent.AddFlags(ActivityFlags.NewTask);
context.StartActivity(intent);
}
Alternatively to avoid the need to do this, you could cache the MainActivity instance Xamarin.Forms uses:
public MainActivity
{
public static MainActivity Instance {get;private set;}
protected override void OnCreate(Bundle bundle)
{
Instance = this;
...
}
}
And then use the Instance as the Context in your code instead of the Application.Context

MVVMCross 5.3.2 UWP: Where to Get IMvxWindowsFrame for MvxFormsUwpViewPresenter

I'm working out of the Xamarin Forms for MVVMCross 5 Solution Template and updated the packages to the latest version (5.3.2 for MVVMCross). Doing that changes some namespaces around particularly in the UWP project.
It seems that I need to resolve IMvxViewPresenter as MvxFormsUwpViewPresenter which takes a IMvxWindowsFrame as an argument. In the setup file method of Setup.cs there's a XamlControls.Frame rootFrame passed as an argument but I'm not sure if that's suppose to be cast somehow as IMvxWindowsFrame.
Where can you pull the object that implements IMvxWindowsFrame from or is there another way to turn the rootFrame into an IMvxWindowsFrame legitimately.
public class Setup : MvxFormsWindowsSetup
{
private readonly LaunchActivatedEventArgs _launchActivatedEventArgs;
public Setup(XamlControls.Frame rootFrame, LaunchActivatedEventArgs e) : base(rootFrame, e)
{
_launchActivatedEventArgs = e;
// Mvx.RegisterSingleton<IMvxWindowsFrame>(rootFrame);
}
protected override void InitializeFirstChance()
{
base.InitializeFirstChance();
Mvx.RegisterSingleton<Core.Services.ILocalizeService>(new Services.LocalizeService());
Mvx.RegisterSingleton<ISettings>(CrossSettings.Current);
Mvx.RegisterType<IMvxViewPresenter, MvxFormsUwpViewPresenter>();
}
protected override MvxFormsApplication CreateFormsApplication()
{
return new Core.FormsApp();
}
protected override IMvxApplication CreateApp()
{
return new Core.MvxApp();
}
protected override IMvxTrace CreateDebugTrace()
{
return new Core.DebugTrace();
}
}
public sealed partial class MainPage : WindowsPage
{
public MainPage()
{
this.InitializeComponent();
var start = Mvx.Resolve<IMvxAppStart>();
start.Start();
var presenter = Mvx.Resolve<IMvxViewPresenter>() as MvxFormsUwpViewPresenter;
LoadApplication(presenter.FormsApplication);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
}
EDIT: I've been looking more into the class MvxFormsWindowsSetup in the source code at https://github.com/MvvmCross/MvvmCross/blob/develop/MvvmCross-Forms/MvvmCross.Forms.Uwp/Platform/MvxFormsWindowsSetup.cs. It appears that in the method CreateViewPresenter that the IMvxViewPresenter is registered as a singleton with the MvxWrappedFrame already inside but by default the code does not resolve when calling var presenter = Mvx.Resolve() as MvxFormsUwpViewPresenter; in the windows page. Possible bug? Trying to see if I can resolve it myself.
Looks like it fails to resolve even if I put the code right after when Mvx is suppose to register the type / singleton
protected override IMvxWindowsViewPresenter CreateViewPresenter(IMvxWindowsFrame rootFrame)
{
var presenter = new MvxFormsUwpViewPresenter(rootFrame, FormsApplication);
Mvx.RegisterSingleton<IMvxFormsViewPresenter>(presenter);
var presenter2 = Mvx.GetSingleton<IMvxViewPresenter>() as MvxFormsUwpViewPresenter;
return presenter;
}
When updating to MvvmCross 5.3.2 for UWP, the presenter needs to resolve as IMvxFormsViewPresenter rather than IMvxViewPresenter. Change the interface type and it should load properly.
public MainPage()
{
this.InitializeComponent();
var start = Mvx.Resolve<IMvxAppStart>();
start.Start();
var presenter = Mvx.Resolve<IMvxFormsViewPresenter>() as MvxFormsUwpViewPresenter;
LoadApplication(presenter.FormsApplication);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}

odata ApiController.User == NULL after upgrade to web api 5.0.0-rc1

I'm using Windows Auth and it was working fine on this odata controller. But after I got the latest NuGet package (prerelease 5.0.0-rc1) something changed and ApiController.User is null. It's not passing the Windows Auth anymore. Any ideas? I tried adding the [Authorize] attribute but that didn't work - maybe that needs more config somewhere else.
public class ProductsController : EntitySetController<Product, int>
{
protected ProjectContextUnitOfWork UoW;
protected UserRepository UserRepo;
protected ProductRepository ProductRepo;
protected Project.Models.User CurrentUser;
// odata/Products/
public ProductsController()
{
if (!User.Identity.IsAuthenticated)
{
HttpResponseMessage msg = Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User not authenticated.");
throw new HttpResponseException(msg);
}
ProjectUserPrincipal LoggedInUser = this.User as ProjectUserPrincipal;
// - closed in Dispose()
UoW = new ProjectContextUnitOfWork(false); //without lazy loading
UserRepo = new UserRepository(UoW);
ProductRepo = new ProductRepository(UoW);
CurrentUser = UserRepo.Get(LoggedInUser.Username, LoggedInUser.Domain);
}
protected override Product GetEntityByKey(int id)
{
var x = from b in ProductRepo.GetAvailableProductsWithNumbers(CurrentUser)
where b.Id == id
select b;
return x.FirstOrDefault();
}
...
}
Other details:
.NET 4.5
Web Forms
Also, when I reverted back to 5.0.0.beta2, without any other changes, it works again. So it's definitely a change in Microsoft.AspNet.WebApi. I'm ok with making code changes, I just need some tips. Thanks!
It's because you are using the ApiController.User in controller constructor. At that time, the property has not been initialized. You should:
Add [Authorize] attribute on your controller
Move the initialization code in Initialize method
So the code looks like:
[Authorize]
public class ProductsController : EntitySetController<Product, int>
{
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
ProjectUserPrincipal LoggedInUser = this.User as ProjectUserPrincipal;
// - closed in Dispose()
UoW = new ProjectContextUnitOfWork(false); //without lazy loading
UserRepo = new UserRepository(UoW);
ProductRepo = new ProductRepository(UoW);
CurrentUser = UserRepo.Get(LoggedInUser.Username, LoggedInUser.Domain);
}
}

Autofac, ASP.NET MVC 3 httpRequest scope and AutoMapper: No scope with a Tag matching 'httpRequest' is visible

When I use a web type registered with autofac from an automapper mapping, I get this error:
No scope with a Tag matching 'httpRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being reqested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
When another type is resolved in the mapping it works.
When a web type is resolved from the controller it works.
Why doesnt web (or any other httprequest scoped?) types get successfully resolved in my mapping?
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AssignableTo<Profile>()
.As<Profile>()
;
builder.Register(c => Mapper.Engine)
.As<IMappingEngine>();
builder.RegisterType<AnotherType>()
.As<IAnotherType>();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
var profiles = container.Resolve<IEnumerable<Profile>>();
Mapper.Initialize(c => profiles.ToList().ForEach(c.AddProfile));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
public class HomeController : Controller
{
private readonly IMappingEngine _mapper;
private readonly Func<HttpContextBase> _httpContext;
public HomeController(IMappingEngine mapper, Func<HttpContextBase> httpContext)
{
_mapper = mapper;
_httpContext = httpContext;
}
public ActionResult Index()
{
var test = _httpContext.Invoke();
return View(_mapper.Map<Model, ViewModel>(new Model()));
}
}
public class MyProfile : Profile
{
private readonly Func<HttpContextBase> _httpContext;
private readonly Func<IAnotherType> _anotherType;
public MyProfile(Func<HttpContextBase> httpContext, Func<IAnotherType> anotherType)
{
_httpContext = httpContext;
_anotherType = anotherType;
}
protected override void Configure()
{
CreateMap<Model, ViewModel>()
.ForMember(d => d.Url, o => o.ResolveUsing(s =>
{
var test = _anotherType.Invoke().GetAValue();
return _httpContext.Invoke().Request.Url;
}))
;
}
}
public interface IAnotherType
{
string GetAValue();
}
public class AnotherType : IAnotherType
{
public string GetAValue() { return "a value"; }
}
public class ViewModel
{
public string Url { get; set; }
}
public class Model
{
}
EDIT: Its easy to create an empty MVC project, paste the code and try it out and see for yourself.
EDIT: Removed the ConstructServicesUsing call because its not required by the example. No services are resolved through AutoMapper in the example.
#rene_r above is on the right track; adapting his answer:
c.ConstructServicesUsing(t => DependencyResolver.Current.GetService(t))
Still might not compile but should get you close.
The requirement is that the call to DependencyResolver.Current is deferred until the service is requested (not kept as the value returned by Current when the mapper was initialised.)
I think you should use DependencyResolver.Current.Resolve instead of container.Resolve in
Mapper.Initialize(c =>
{
c.ConstructServicesUsing(DependencyResolver.Current);
profiles.ToList().ForEach(c.AddProfile);
});
I recently had a similar problem and it turned out to be a bad setup in my bootstrapper function. The following autofac setup did it for me.
builder.Register(c => new ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers))
.AsImplementedInterfaces()
.SingleInstance();
builder.Register(c => Mapper.Engine)
.As<IMappingEngine>()
.SingleInstance();
builder.RegisterType<TypeMapFactory>()
.As<ITypeMapFactory>()
.SingleInstance();
I did not have to specify resolver in the Mapper.Initialize() function. Just called
Mapper.Initialize(x =>
{
x.AddProfile<DomainToDTOMappingProfile>();
});
after the bootstrapped and it works fine for me.

Resources