Onesignal NotificationOpened doesn't work in ios app - ios

I try to work with Onesignal Notifications in my ios app, created in Visual Studio with Xamarin. I want to make application open ViewController when user tap on notification, but it doesn't work. When user tap notification, app opens ViewController which was active when user application go to background.
Hope somebody show, where I made mistake
Here the code:
public class AppDelegate : UIApplicationDelegate
{
public static UIStoryboard Storyboard = UIStoryboard.FromName("Main", null);
public override UIWindow Window {
get;
set;
}
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
OneSignal.Current.StartInit("mykey")
.InFocusDisplaying(OSInFocusDisplayOption.None)
.HandleNotificationOpened(HandleNotificationOpened)
.EndInit();
OneSignal.Current.IdsAvailable(IdsAvailable);
return true;
}
void IdsAvailable(string userID, string pushToken)
{
GlobalUserInfo._token = userID;
}
private static void HandleNotificationOpened(OSNotificationOpenedResult result)
{
OSNotificationPayload payload = result.notification.payload;
Dictionary<string, object> additionalData = payload.additionalData;
if (additionalData != null)
{
UINavigationController NavigationController = (UINavigationController)Storyboard.InstantiateInitialViewController();
NotificationsViewController notificationsController = Storyboard.InstantiateViewController("NotificationsViewController") as NotificationsViewController;
NavigationController.PushViewController(notificationsController, true);
}
}
}
NotificationsViewController is simple list of all notification user received.
UPDATED
As I noticed, function HandleNotificationOpened does not work in iOS.
UPDATED
Error was found. I forgot, that I use OneSignal Init in other place. When I delete duplicate, HandleNotificationOpened begins work correctly.

You should find current root ViewController instead of creating a new instance. So please modify like this:
if (additionalData != null)
{
UINavigationController NavigationController = (UINavigationController)Window.RootViewController;
NotificationsViewController notificationsController = Storyboard.InstantiateViewController("NotificationsViewController") as NotificationsViewController;
NavigationController.PushViewController(notificationsController, true);
}
Moreover, if the root ViewController is a UITabBarController, please present this view controller(NotificationsViewController).

Related

Login with Xamarin.Auth in IOS Project is not working

I'm using Xamarin.Auth to login to google and facebook, It works fine with Android Project but seem like presenter is not working with IOS, it can't opon the browser for login.
Here is my code to call Presenter in viewmodel
var authenticator = new OAuth2Authenticator(
clientId,
constants.FacebookScope,
new Uri(constants.FacebookAuthorizeUrl),
new Uri(constants.FacebookAccessTokenUrl),
null,
true
);
authenticator.Completed += OnAuthCompleted;
authenticator.Error += OnAuthError;
AuthenticationState.Authenticator = authenticator;
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(authenticator);
And Here is my AppDelegate in IOS project
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)
{
FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
Rg.Plugins.Popup.Popup.Init();
global::Xamarin.Forms.Forms.Init();
global::Xamarin.Auth.Presenters.XamarinIOS.AuthenticationConfiguration.Init();
//global::Xamarin.Auth.WebViewConfiguration.IOS.UserAgent = "moljac++";
XamEffects.iOS.Effects.Init();
FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
CachedImageRenderer.InitImageSourceHandler();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
public override bool OpenUrl
(
UIApplication application,
NSUrl url,
string sourceApplication,
NSObject annotation
)
{
// Convert iOS NSUrl to C#/netxf/BCL System.Uri - common API
Uri uri_netfx = new Uri(url.AbsoluteString);
// load redirect_url Page (send it back to Xamarin.Auth)
// for Url parsing and raising Complete or Error events
AuthenticationState.Authenticator.OnPageLoading(uri_netfx);
return true;
}
}

Xamarin IOS Facebook SDK LoginButton

Im new to Xamarin and IOS development and dont understand why I get this following error:
But first some Information:
Im using this SDK:
https://components.xamarin.com/view/facebookios
and creating my LoginButtin in my ViewController in ViewDidLoad like this:
loginButton = new LoginButton(new CGRect(48, 0, 218, 46))
{
LoginBehavior = LoginBehavior.Native,
ReadPermissions = readPermissions.ToArray()
};
View.AddSubview(button);
But in my Storyboard I get this error message:
Edit: my ViewController.cs
using System;
using System.Collections.Generic;
using UIKit;
namespace FacebookLogin
{
public partial class ViewController : UIViewController
{
List<string> readPermissions = new List<string> { "public_profile" };
LoginButton loginButton;
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
loginButton = new LoginButton(new CGRect(48, 0, 218, 46))
{
LoginBehavior = LoginBehavior.Native,
ReadPermissions = readPermissions.ToArray()
};
View.AddSubview(button);
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
}
}
Make sure to follow the instructions found on the getting started page. I see an empty component in your solution explorer but just in case, make sure that you installed Xamarin.Facebook.iOS 4.27.1 with nugget. Of course, you also need to set up your facebook app, login, and configure the iOS portion (like setting the BundleID).
Don't create the button in the controller. What you can do is use the storyBoard designer to drop in a regular button. Then, in the properties window click on Class and it should open a dropdown menu. In the selection you should see FBSDKLoginButton, select that class. Give it a name like btnFacebook.
In the codebehind for your controller it will look like this:
string[] readPermissions = { "public_profile" };
public override void ViewDidLoad()
{
base.ViewDidLoad();
btn.LoginBehavior = LoginBehavior.Native;
btnFacebook.ReadPermissions = readPermissions;
// Handle actions once the user is logged in
btnFacebook.Completed += LoginView_Completed;
// Handle actions once the user is logged out
btnFacebook.LoggedOut += LoginView_LoggedOut;
}
private void LoginView_Completed(object sender, LoginButtonCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
if (e.Result.IsCancelled)
{
return;
}
}
private void LoginView_LoggedOut(object sender, EventArgs e)
{
}
For good measure, clean solution and recompile. The login button won't appear as a facebook login button in your designer but on runtime it will.
As for your error, I don't see anything in your designer so it's curious that it's giving you an error like that. Open the Document Outline (View -> Other Windows -> Document Outline) and see if there's any invisible garbage (elements that aren't being rendered) that has to be deleted.
First Of All! You're on the right Way To solve your issue
and Secondly, I would Suggest you that you have created a controller in the above image you need to create a View Controller File and then add a Login Screen Like a Username named Textbox Then a Password named Textbox and A login Button and then View Controller will automatically created once you save the view And Then Finally, You Save the view Controller By Adding the Following Code
partial void login_TouchUpInside(UIButton sender)
{
var auth = new OAuth2Authenticator(clientId: "YOUR_CLIENT_ID", scope: "", authorizeUrl: new Uri("https://m.facebook.com/dialog/oauth/"), redirectUrl: new Uri("http://www.facebook.com/connect/login_success.html"));
auth.Completed += Auth_Completed;
var ui = auth.GetUI();
PresentViewController(ui, true, null);
}
private async void Auth_Completed(object sender, AuthenticatorCompletedEventArgs e)
{
if (e.IsAuthenticated)
{
var request = new OAuth2Request("POST", new Uri("YOUR Location Where You want to reach After Login"), null, e.Account);
//fb://profile/<id> For opening in Facebook App.
}
DismissViewController(true, null);
}
You See the Above Code And If you Want to Open the Facebook Link In Facebook Application Just Replace URL with
fb://profile/<id>

Warning: View is not in Window Hierarchy in Xamarin.Forms

I am doing a Xamarin project, Forms and I have integrated Xam.Plugins.Messaging to send SMS from my app. For this I have created a custom renderer in my iOS project with below code:
AppDelegate smsObj = new AppDelegate();
bool a= smsObj.ShowAndSendSMS(new string[] { "123" }, "Hi there");
And in my AppDelegate, I have the code as below:
public bool ShowAndSendSMS(string[] recipients, string body)
{
UIViewController sms = new UIViewController();
if (MFMessageComposeViewController.CanSendText)
{
MFMessageComposeViewController message = new MFMessageComposeViewController();
message.Finished += (sender, e) => {
message.DismissViewController(true, null);
};
message.Body = body;
message.Recipients = recipients;
sms.PresentModalViewController(message, false);
}
return true;
}
The problem I am facing is on my first-time app launch, the functionality to share SMS doesn't work and the debug log gives me warning like "Attempt to present on whose view is not in the window hierarchy!"
However, if I restart the app, the same functionality works like a charm. Any ideas from where i have made mistake?
I think the problem is with the fact that you're newing up an AppDelegate and calling the ShowAndSendSMS from there. iOS is going to new up that AppDelegate for you upon app startup, and you should always use that, as opposed to creating a new instance of AppDelegate (at least I've never seen a situation that called for a multi-AppDelegate-instance pattern). So, try this:
Create a helper class in your project like this (I don't really like the word "helper", but that's beside the point; name it something fitting for your project):
using Foundation;
using UIKit;
public class SmsHelper
{
public bool ShowAndSendSMS(string[] recipients, string body)
{
if (MFMessageComposeViewController.CanSendText)
{
UIViewController sms = new UIViewController();
MFMessageComposeViewController message = new MFMessageComposeViewController();
message.Finished += (sender, e) => {
message.DismissViewController(true, null);
};
message.Body = body;
message.Recipients = recipients;
sms.PresentModalViewController(message, false);
}
return true;
}
}
And then change your page renderer to consume it like this:
public class SMS_Ios: PageRenderer
{
private readonly TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
SmsHelper smsObj = new SmsHelper();
bool a = smsObj.ShowAndSendSMS(new string[] {"123"}, "Hi there");
}
}
And finally, remove ShowAndSendSMS from your AppDelegate.cs, since you'll be using your SMS helper going forward.
Let me know if that works for you.
If you have already installed the Xam.Plugins.Messaging package in the PCL and your platforms. You can just use the API from it in PCL to implement that without any special codes in your iOS platform.
You can just use the APIs of Xam.Plugins.Messaging in the PCL, like this:
// Send Sms
var smsMessenger = CrossMessaging.Current.SmsMessenger;
if (smsMessenger.CanSendSms)
smsMessenger.SendSms("+27213894839493", "Well hello there from Xam.Messaging.Plugin");
Reference: Messaging Plugin for Xamarin and Windows.

How to pushasync from detailpage in masterdetailpage?

From the detailpage's view I try to push a new page on and I get this error: **
System.InvalidOperationException: Page must not already have a parent.
I keep trying different things but nothing works. Is there a way to push a page onto it, I mean, the detailpage is a navigationpage but it is a detailpage. Any and all help is much appreciated.
I am using xamarin forms labs ViewFactory.
//app.cs GetMainPage
var rootPage = ViewFactory.CreatePage<HomeVM>();
//in HomeView.xaml.cs, setting the detailpage to the list of messages
Detail = new NavigationPage(ViewFactory.CreatePage<MessagesVM>());
//This is in the MessagesView to show an individual message with a back button to the list of messages
Navigation.PushAsync(ViewFactory.CreatePage<MessageDetailVM>());
If you already have a NavigationPage, do not create another one to wrap your Detail instance in.
Detail = iewFactory.CreatePage<MessagesVM>();
Navigation.PushAsync(ViewFactory.CreatePage<MessageDetailVM>());
On my part, I also having the same error using MessagingCenter, but also solve it by unsubscribing/disposing after page closing/OnDisappearing.
Hope it helps.
public partial class MainPage : MasterDetailPage
{
public MainPage()
{
InitializeComponent();
MasterBehavior = MasterBehavior.Popover;
MessagingCenter.Subscribe<NavigationPage>(this, "Navigate", (pageItem) =>
{
Detail = pageItem;
IsPresented = false;
});
MessagingCenter.Subscribe<string>(this, "Logout", (s) =>
{
Application.Current.MainPage = new LoginPage("", "");
});
}
protected override void OnDisappearing()
{
MessagingCenter.Unsubscribe<NavigationPage>(this, "Navigate");
MessagingCenter.Unsubscribe<string>(this, "Logout");
base.OnDisappearing();
}
}
Unfortunately, I encountered this error again, and I solve it by using this setting the NavigationPage Parent property to null.
MessagingCenter.Subscribe<NavigationPage>(this, "Navigate", (pageItem) =>
{
pageItem.Parent = null; //solution
Detail = pageItem;
IsPresented = false;
});
Both answers before mine are pointing to the right direction, so this is just an addition. The key is in fact to not create the NavigationPage/NavigationView again.
In my project, I am using static objects for the MasterDetailPage, the NavigationView and the corresponding ViewModels in Xamarin Forms App class;
I am only creating an instance if their Value is null, which is likely to happen only if the app was closed before (no matter if closed by the user or the OS). If the app is still running (resumed), I am just using the already existing objects to restore the state.
This solved all these problems for me, and I hope it is helpful for someone else.
Try using the Navigation property of the Detail object like this:
Detail.Navigation.PushAsync(page);
To push a new page on my Detail I use the code below:
Note there I'm using a ListView with page options in MasterDetailPage
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MainMenuItem;
if (item == null)
return;
var page = (Page)Activator.CreateInstance(item.TargetType);
//Detail = new NavigationPage(page);
Detail.Navigation.PushAsync(page);
IsPresented = false;
MasterPage.ListView.SelectedItem = null;
}

UIDocumentInteractionController change title of presented view

I am using the UIDocumentInteractionController to display PDF files. My files are stored in the filesystem using encoded filenames that are not user-friendly. I do have access to a more friendly name for the file, but the file is not stored with this name (for uniqueness reasons).
When I load the document into the UIDocumentInteractionController then the view displays the unfriendly 'real' filename in the title bar.
Is there any way to change the displayed title as presented by the UIDocumentInteractionController?
UIDocumentInteractionController has a name property which you can set to your user-friendly name. That name will be used in the title bar of the presented document.
This is Xamarin (C#) code, but I hope it helps anyways
public class PreviewFile : IPreviewFile
{
public PreviewFile()
{
}
public void Preview(string file, string title)
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
bool isUrl = file.StartsWith("file:", StringComparison.OrdinalIgnoreCase);
var url = isUrl ? new NSUrl(file) : NSUrl.FromFilename(file);
var controller = UIDocumentInteractionController.FromUrl(url);
controller.Delegate = new MyInteractionDelegate(vc);
controller.Name = title;
controller.PresentPreview(true);
}
}
public class MyInteractionDelegate : UIDocumentInteractionControllerDelegate
{
UIViewController parent;
public MyInteractionDelegate(UIViewController controller)
{
parent = controller;
}
public override UIViewController ViewControllerForPreview(UIDocumentInteractionController controller)
{
return parent;
}
}

Resources