How to integrate two apps in xamarin iOS? - xamarin.ios

I have two apps. One called SincApp that take a pic and should return it for caller with a status. So, a image and a string.
This app was be developed in swift.
Other called ZipZag that call SincApp and wait for your response that should show the image taked and status string in a label, developed in Xamarin.
I was configured Info.plist like this:
Info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sincapp-scan</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>br.com.xxx.SincApp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>sincapp-scan-back</string>
</array>
</dict>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UIRequiresFullScreen</key>
<true/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>SincApp</string>
<key>LSItemContentTypes</key>
<array>
<string>br.com.xxx.sincapp</string>
</array>
</dict>
</array>
Entitlement.plist like this:
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.xxx.sincapp</string>
</array>
ScamCameraViewControler.cs
[Register("UniversalView")]
public class UniversalView : UIView
{
public UniversalView()
{
Initialize();
}
public UniversalView(RectangleF bounds) : base(bounds)
{
Initialize();
}
void Initialize()
{
BackgroundColor = UIColor.Red;
}
}
[Register("ScamCameraViewController")]
public class ScamCameraViewController : UIViewController
{
public ScamCameraViewController()
{
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
}
public override void ViewDidLoad()
{
View = new UniversalView();
base.ViewDidLoad();
this.UIObserver();
this.CallSincApp();
}
public async Task UIObserver()
{
NSNotificationCenter
.DefaultCenter
.AddObserver(
this,
new ObjCRuntime.Selector("handleEventNotification:"),
new NSString("EventNotification"),
null);
}
[Export("handleEventNotification:")]
public void handleEventNotification(NSNotification obj)
{
// var readValue = NSUserDefaults.StandardUserDefaults.StringForKey("CFBundleURLName");
NSUserDefaults userDefaults = new NSUserDefaults("group.br.com.xxx.sincapp", NSUserDefaultsType.SuiteName);
var isSincAppProcessSuccess = userDefaults.BoolForKey("KEY_STATUS");
var isSincAppProcessSuccess2 = userDefaults.StringForKey(new NSString ("KEY_STATUS"));
}
public async Task CallSincApp()
{
var application = UIApplication.SharedApplication;
var SDK_KEY = "99999999999999999999999999999999";
var urlString = "sincapp-scan://?canoaKey=/" + SDK_KEY + "&scanbarcode=/";
Foundation.NSUrl urlApp = Foundation.NSUrl.FromString(urlString);
if (application.CanOpenUrl(urlApp))
{
application.OpenUrl(urlApp);
}
}
}
AppDelegate.cs
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(new iOSInitializer()));
return base.FinishedLaunching(app, options);
}
public override bool HandleOpenURL(UIApplication application, NSUrl url)
{
BindValue(url);
return true;
}
private void BindValue(NSUrl url)
{
if (url.Scheme == "sincapp-scan-back")
{
NSNotificationCenter
.DefaultCenter
.PostNotificationName("EventNotification", null, null);
}
}
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
// We need to handle URLs by passing them to their own OpenUrl in order to make the SSO authentication works.
BindValue(url);
return true;
}
}
public class iOSInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
// Register any platform specific implementations
}
}
}
ScanCameraiOS
private async Task callMainViewController()
{
ScamCameraViewController scvc = new ScamCameraViewController();
scvc.ViewDidLoad();
}
But when I degub it, var isSincAppProcessSuccess is returning always false and isSincAppProcessSuccess2 null.
The same app developed in Swift is working.
Someone could help me, please?

Related

CarPlay parking app crashed when launching from Xcode 12 CarPlay simulator

We are extending our Xamarin.iOS app (by Xamarin.Forms) for CarPlay. When open the CarPlay simulator, the app is showing on the screen of CarPlay, but crashed when launching from CarPlay simulator.
Below is the Info.plist Scene configuration:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UISceneConfigurations</key>
<dict>
<key>CPTemplateApplicationSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>CPTemplateApplicationScene</string>
<key>UISceneConfigurationName</key>
<string>ParkingPlus-Car</string>
<key>UISceneDelegateClassName</key>
<string>ParkingPlus.AppSceneDelegateImp</string>
</dict>
</array>
</dict>
</dict>
"ParkingPlus" is the app bundle name!
The class AppSceneDelegateImp (under the root folder of the iOS project
public class AppSceneDelegateImp : UIResponder, ICPTemplateApplicationSceneDelegate
{
private CPInterfaceController _interfaceController;
public async void DidConnect(CPTemplateApplicationScene templateApplicationScene, CPInterfaceController interfaceController)
{
_interfaceController = interfaceController;
.....
}
public void DidDisconnect(CPTemplateApplicationScene templateApplicationScene, CPInterfaceController interfaceController)
{
_interfaceController.Dispose();
_interfaceController = null;
}
}
When I override the AppDelegate.GetConfiguration as below
public override UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
{
...
}
When tapping the app icon on CarPlay, the method will be called. But when I inspected the connectingSceneSession, I found there are some exceptions inside the variable members.
"CPTemplateApplicationSceneSessionRoleApplication has no associated enum value on this platform".
Screenshot for connectingSceneSession inspection
If continue, then the app will throw an exception which seems advise that the SceneDelegate is not being loaded properly:
Exception
My envorinment:
Visual studio for mac Version 8.7.8
Xamarin.iOS 14.0.0
Xcode 12.0
It seems like Xamarin.ios 14 missing something when binding the iOS library. Anyone has the similar issue. Did I do anything wrong or Is there any other way I can implement the CarPlay part feature by Xcode/swift while keeping the mobile app on Xamarin.Forms/Xamarin.iOS?
Appreciate any comments or help.
With the help from Xamarin.ios team, below is the full solution to this issue:
https://github.com/xamarin/xamarin-macios/issues/9749
The AppDelegate to create the scene configuration for two cases (CarPlay and phone)
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public extern static IntPtr IntPtr_objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);
public static UISceneConfiguration Create(string? name, NSString sessionRole)
{
global::UIKit.UIApplication.EnsureUIThread();
var nsname = NSString.CreateNative(name);
UISceneConfiguration sceneConfig;
sceneConfig = Runtime.GetNSObject<UISceneConfiguration>(IntPtr_objc_msgSend_IntPtr_IntPtr(Class.GetHandle("UISceneConfiguration"), Selector.GetHandle("configurationWithName:sessionRole:"), nsname, sessionRole.Handle));
NSString.ReleaseNative(nsname);
//Because only the CarPlay scene will be here to create a scene configuration
//We need manually assign the CarPlay scene delegate here!
sceneConfig.DelegateType = typeof(AppCarSceneDelegateImp);
return sceneConfig!;
}
[Export("application:configurationForConnectingSceneSession:options:")]
public UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
{
UIWindowSceneSessionRole sessionRole;
bool isCarPlaySceneSession = false;
try
{
//When the connecting scene is a CarPlay scene, an expected exception will be thrown
//Under this moment from Xamarin.iOS.
sessionRole = connectingSceneSession.Role;
}
catch (NotSupportedException ex)
{
if (!string.IsNullOrEmpty(ex.Message) &&
ex.Message.Contains("CPTemplateApplicationSceneSessionRoleApplication"))
{
isCarPlaySceneSession = true;
}
}
if (isCarPlaySceneSession && UIDevice.CurrentDevice.CheckSystemVersion(14,0))
{
return Create("Car", CarPlay.CPTemplateApplicationScene.SessionRoleApplication);
}
else
{
//If it is phone scene, we need the regular UIWindow scene
UISceneConfiguration phoneScene = new UISceneConfiguration("Phone", UIWindowSceneSessionRole.Application);
//And assign the scene delegate here.
phoneScene.DelegateType = typeof(AppWindowSceneDelegateImp);
return phoneScene;
}
}
Create a UIWindowScene delegate to handle the regular mobile scene window
public class AppWindowSceneDelegateImp : UISceneDelegate
{
public override void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
var windowScene = scene as UIWindowScene;
if (windowScene != null)
{
//Assign the Xamarin.iOS app window to this scene
UIApplication.SharedApplication.KeyWindow.WindowScene = windowScene;
UIApplication.SharedApplication.KeyWindow.MakeKeyAndVisible();
}
}
}

Observer won't run update in JavaFX GUI

I read much about the JavaFX GUI Model, Plattform->RunLater and Threads, but I still do not figure out how to get this right. I had a JavaFX GUI which on a button click executed a process and updated a Progress Bar and Label. This was running well with Threading and Platform, but I had to Change this to an Observer Model.
I invoke a Progress Tracker in a Singleton Model, which gets updated by the class executing the process and is Observable. I implemented an Observer as well which should update the two UI Elements.
GUI Controller with Button Event
private void createKeyPressed(ActionEvent event) {
// Make Progressbar visible
pbKeyProgress.visibleProperty().set(true);
if (!Check.keyFileExistant() || cbKeyOverwrite.selectedProperty().get()) {
ProgressTracker.getTracker().addObserver(new ProgressObserver(pbKeyProgress, lblKeyProgress));
Creator.createKey(cbKeyLength.getValue());
} else {
}
}
Progress Observer
public class ProgressObserver implements Observer {
private final ProgressBar progressBar;
private final Label statusLabel;
public ProgressObserver(ProgressBar progressBar, Label statusLabel) {
this.progressBar = progressBar;
this.statusLabel = statusLabel;
}
#Override
public void update(Observable o, Object o1) {
Platform.runLater(() -> {
System.out.println("Tracker set to "+ProgressTracker.getProgress() + " " + ProgressTracker.getStatus());
progressBar.setProgress(ProgressTracker.getProgress());
statusLabel.setText(ProgressTracker.getStatus());
});
}
}
Progress Tracker
public synchronized void setTracker(int currentStep, String currentStatus) {
checkInstance();
instance.step = currentStep;
instance.status = currentStatus;
instance.notifyObservers();
System.out.println(instance.countObservers());
}
Creator
public static void createKey(String length) {
Task<Void> task;
task = new Task<Void>() {
#Override
public Void call() throws Exception {
initTracker(0,"Start");
doStuff();
ProgressTracker.getTracker().setTracker(1,"First");
doStuff();
ProgressTracker.getTracker().setTracker(2,"Second");
// and so on
return null;
}
};
new Thread(task)
.start();
}
The Print within the ProgressTracker gets executed. However, if I add a print within the update of the Observer nothing will be printed. If I check within the Progresstracker, the Observer Count is 1.
Why does the Observer not get notified or execute anything, even if the Notify is called? Did I get the Threading and Execution Modell wrong?
The Progress Bar and the Label will also stay on their initial values.
Don't reinvent the wheel. The JavaFX Properties Pattern is a ready-made implementation of the Observable pattern: there is no need to implement it yourself. Additionally, Task already defines methods for updating various properties, which can be called from any thread but will schedule the actual updates on the FX Application Thread. See updateProgress() and updateMessage(), for example.
So you can do, for example:
public static Task<Void> createKey(String length) {
Task<Void> task;
task = new Task<Void>() {
final int totalSteps = ... ;
#Override
public Void call() throws Exception {
updateProgress(0, totalSteps);
updateMessage("Start");
doStuff();
updateProgress(1, totalSteps);
updateMessage("First");
doStuff();
updateProgress(2, totalSteps);
updateMessage("Second");
// and so on
return null;
}
};
new Thread(task)
.start();
return task ;
}
and
private void createKeyPressed(ActionEvent event) {
// Make Progressbar visible
pbKeyProgress.visibleProperty().set(true);
if (!Check.keyFileExistant() || cbKeyOverwrite.selectedProperty().get()) {
Task<Void> task = Creator.createKey(cbKeyLength.getValue());
pbKeyProgress.progressProperty().bind(task.progressProperty());
lblKeyProgress.textProperty().bind(task.messageProperty());
} else {
}
}

ServiceBusTrigger POCO Deserialization

I would like to see if/how it would be possible to plug into the deserialization process for a parameter that's decorated with the ServiceBusTrigger?
Say I have a function that looks like:
public static void HandleMessage([ServiceBusTrigger("myqueue")] MyCustomType myCustomType) { }
How would I go about taking over the deserialization? I know that there is a notion of an IArgumentBindingProvider and IArgumentBinding but it does not look like ServiceBusTrigger supports these concepts.
I know I can use GetBody<Stream>() and deserialize that way but I'd like to know if I can plug into the ServiceBusTrigger's pipeline. By the looks at the SDK, the ServiceBusTrigger has a hard coded list of IQueueArgumentBindingProviders and so I can't add my own.
If you have a look at the Azure WebJobs SDK Extensions, there is an overview on how to create your own bindings :
Binding Extensions Overview
Otherwise the ServiceBusConfiguration exposes a MessagingProvider property that allows you to intercept the ServiceBusTrigger pipeline:
private static void Main()
{
var sbConfig = new ServiceBusConfiguration()
{
MessagingProvider = // you implemetation of the MessagingProvider class goes here !!!
};
var config = new JobHostConfiguration();
config.UseServiceBus(sbConfig);
new JobHost(config).RunAndBlock();
}
Here is a simple skeleton of a MessagingProvider implementation:
public sealed class MyMessagingProvider : MessagingProvider
{
private readonly ServiceBusConfiguration _config;
public MyMessagingProvider(ServiceBusConfiguration config)
: base(config)
{
_config = config;
}
public override MessageProcessor CreateMessageProcessor(string entityPath)
{
return new MyMessageProcessor(_config.MessageOptions);
}
private class MyMessageProcessor : MessageProcessor
{
public MyMessageProcessor(OnMessageOptions messageOptions)
: base(messageOptions)
{
}
public override Task<bool> BeginProcessingMessageAsync(BrokeredMessage message, CancellationToken cancellationToken)
{
// Intercept the message before the execution of the triggerred function
return base.BeginProcessingMessageAsync(message, cancellationToken);
}
public override Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken)
{
// Intercept the message after the execution of the triggerred function and before being completed
return base.CompleteProcessingMessageAsync(message, result, cancellationToken);
}
}
}
So you're main function now looks like that:
private static void Main()
{
var sbConfig = new ServiceBusConfiguration();
sbConfig.MessagingProvider = new MyMessagingProvider(sbConfig);
var config = new JobHostConfiguration();
config.UseServiceBus(sbConfig);
new JobHost(config).RunAndBlock();
}

Xamarin.iOS simple NavigationController memory leak

I had an issue in my project and attempted to create a sample project to reproduce it and I was able to.
https://bitbucket.org/theonlylawislove/xamarinnavigationcontrollermemoryleak
The issue is that, when I present a UINavigationController, the navigation controller or its root view controller never gets garbage collected. It DOES work in the iOS simulator though. Why does this memory leak only happen on the device? If you run the sample project on the device, you will never see the Console.WriteLine in the deconstructors called.
I am using XCode5 and Xamarin.iOS 7.0.4.171 (Business Edition)
Here is the AppDelegate I am using to demonstrate the leak.
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = new UINavigationController(new RootController ());
window.MakeKeyAndVisible ();
return true;
}
class RootController : UIViewController
{
public RootController ()
{
NavigationItem.RightBarButtonItem = new UIBarButtonItem("Present", UIBarButtonItemStyle.Bordered, (o,e) => {
PresentViewController(new NavigationController(), true, new NSAction(() => {}));
});
}
}
class NavigationController : UINavigationController
{
public NavigationController ()
:base(new TestController())
{
}
~NavigationController()
{
Console.WriteLine("~NavigationController");
}
class TestController : UIViewController
{
~TestController()
{
Console.WriteLine("~TestController");
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
Task.Factory.StartNew (() => {
Thread.Sleep(2000);
NSThread.MainThread.InvokeOnMainThread(new NSAction(() => {
DismissViewController(true, new NSAction(() => {
}));
}));
});
}
}
}
}
This is merely a side effect of the conservative collector, there might be some junk on the stack, but using your application will eliminate the junk and allow the object to be released.
If you use SGen, which uses a precise system, you will see the object vanish right away.

ViewModel property sort of fatal with VMDisconnectedException

EDIT 2: If you're looking for an answer to a similar problem, check Stuart's answer and my comments on it.
EDIT: I am actually getting a Mono.Debugger.Soft.VMDisconnectedException. I also recently installed Windows 8.1 and Resharper (though Resharper is suspended now).
When I access a very simple list property of my view model in my MVVMCross Xamarin iOS application, the program fails. It doesn't quit most of the time: it acts like it's running. The simulator has a black screen and there is no exception. If I breakpoint on if (messagesViewModel != null) source.ItemsSource = messagesViewModel.Messages; and then type messagesViewModel.Messages into the Immediate Window, everything stops, so I can tell it is failing at this line. If instead I "step over", it never moves to the next line.
I was having similar behavior when I was toggling this code in the MvxTableViewSource:
public override int RowsInSection(UITableView tableview, int section)
{
return 1;
}
My view model looks like this:
public class MessagesViewModel : MvxViewModel
{
private List<BaseMessage> _messages = null;
public List<BaseMessage> Messages
{
get
{
return _messages; //yes, I know I'm returning null
//I wasn't at first.
}
}
public MessagesViewModel()
{
}
}
This is my ViewDIdLoad on the MvxTableViewController:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var source = new MessagesTableViewSource(TableView);
//was binding here, removed it for debug purposes
//failure on second line here
var messagesViewModel = ViewModel as MessagesViewModel;
if (messagesViewModel != null) source.ItemsSource = messagesViewModel.Messages;
TableView.Source = source;
TableView.ReloadData();
}
Some initialization code:
public class App : MvxApplication
{
public App()
{
var appStart = new MvxAppStart<MessagesViewModel>();
Mvx.RegisterSingleton<IMvxAppStart>(appStart);
}
}
public partial class AppDelegate : MvxApplicationDelegate
{
//empty functions removed.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var presenter = new MvxTouchViewPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
return true;
}
}
I suspect whatever the error is, it isn't in any of the code you have posted.
I just created a simple ViewModel:
public class FirstViewModel
: MvxViewModel
{
private List<string> _items = new List<string>() { "One", "Two", "Three"};
public List<string> Items
{
get { return _items; }
set { _items = value; RaisePropertyChanged(() => Items); }
}
}
And a simple View:
[Register("FirstView")]
public class FirstView : MvxTableViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var firstViewModel = ViewModel as FirstViewModel;
var source = new MessagesTableViewSource(TableView);
source.ItemsSource = firstViewModel.Items;
TableView.Source = source;
}
public class MessagesTableViewSource : MvxTableViewSource
{
public MessagesTableViewSource(UITableView tableView) : base(tableView)
{
tableView.RegisterClassForCellReuse(typeof(MessagesCell), new NSString("MessagesCell"));
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
return tableView.DequeueReusableCell("MessagesCell");
}
}
public class MessagesCell : MvxTableViewCell
{
public MessagesCell(IntPtr handle)
: base(handle)
{
var txt = new UILabel(new RectangleF(0, 0, 320, 44));
Add(txt);
this.DelayBind(() =>
{
this.CreateBinding(txt).Apply();
});
}
}
}
And this code runs fine...
I wouldn't completely trust the integration of Xamarin.iOS with the Immediate window - it is better now than it used to be, but I've seen several problems with it before.
Some things to possibly check:
does the above code work for you?
if it does, then what's in your BaseMessage and MessagesTableViewSource classes - perhaps they are causing the problem?
can you use Mvx.Trace("The list is {0}", messagesViewModel.Messages ?? "-null") to view the list? Can you use trace within the ViewModel property get - is it being called? Can you use trace within the ViewModel constructor?
are all your assemblies building against the same versions of things? Are all your assemblies definitely rebuilt? (Check "Build|Configuration Manager")- what version of Xamarin.iOS are you running in VS and in the Mac?

Resources