Cannot send email using xamarin ios implementation - xamarin

trying to send an email calling the method below but I keep getting
and the email ios form is not displayed.
" Attempt to present MFMailComposeViewController: 0x1560e1a00 on
Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x157b5c180
whose view is not in the window hierarchy!"
Any ideas of what I need to do to make it work?
public void SendEmail()
{
string to="jo#gmail.com";
string subject="Test";
string body="This is a test email";
if (MFMailComposeViewController.CanSendMail)
{
var mailComposeViewController = new MFMailComposeViewController();
mailComposeViewController.SetToRecipients(new[] { to });
mailComposeViewController.SetSubject(subject);
mailComposeViewController.SetMessageBody(body, false);
mailComposeViewController.Finished += (s, args) =>
{
Xamarin.Forms.Device.BeginInvokeOnMainThread
(
() => { args.Controller.DismissViewController(true, null); }
);
};
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(mailComposeViewController, true, null);
}
}

We should find the correct rootviewController on the top of the window.
Try this :
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
And modify your last line of code to
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);

Related

Initialize an xamarin view after an async method

Good evening everyone.
For some time now I have been to Xamarin. My first tests are rather conclusive. I decided to try to make a small application that retrieves information in a database via an API and then update this data via a ListView.
When I launch the application on my emulator everything works but as soon as I install the application on my phone it crashes. I thought this was because the API but I have an API that I use to check the Login / password that works correctly.
The API that returns the data reviews a lot of line about 3500/4000, can this be the reason?
So I passed the loading of the data in my viewModel in an async method but the problem now is that the view loads before the data is loaded correctly. Is there a way to get the view initialized after the data is loaded?
Below my code.
Initializing my viewModel
class ManageInventViewModel
{
public ObservableCollection<InventViewModel> InventProduct { get; set; }
public Command<InventViewModel> UpdateCommand
{
get
{
return new Command<InventViewModel>(invent =>
{
var index = invent.IndexLigneInventaire;
InventProduct.Remove(invent);
InventProduct.Insert(index, invent);
});
}
}
public Command<InventViewModel> ResetStock
{
get
{
return new Command<InventViewModel>(invent =>
{
var index = InventProduct.IndexOf(invent);
InventProduct.Remove(invent);
invent.RealStockProduct = 0;
InventProduct.Insert(index, invent);
});
}
}
public ManageInventViewModel()
{
LoadInventaire();
}
private async void LoadInventaire()
{
var listMvt = await Utils.Utils.GetListMouvementUntilDate();
var listStock = Utils.Utils.GetStockByProduct(listMvt).Take(20);
InventProduct = new ObservableCollection<InventViewModel>();
var indexLine = 0;
foreach (var stock in listStock)
{
var inventViewModel = new InventViewModel
{
LibelleProduit = stock.LibelleProduit,
PrCodeProduit = stock.PrCodeProduit,
UpCodeProduit = stock.UpCodeProduit,
RealStockProduct = stock.StockTheoProdct,
StockTheoProdct = stock.StockTheoProdct,
IndexLigneInventaire = indexLine
};
++indexLine;
InventProduct.Add(inventViewModel);
}
}
}
Initializinz my view
public partial class InventPage : ContentPage
{
public InventPage()
{
InitializeComponent();
TableInvent.ItemSelected += (sender, e) =>
{
if (TableInvent.SelectedItem != null)
{
if (TableInvent.SelectedItem is InventViewModel item)
{
PopupNavigation.Instance.PushAsync(new ChangeStockModal(item, this));
}
TableInvent.SelectedItem = null;
}
};
}
private void Reset_Stock(object sender, EventArgs e)
{
var input = sender as Button;
var inventViewModel = input?.BindingContext as InventViewModel;
var listViewModel = BindingContext as ManageInventViewModel;
listViewModel?.ResetStock.Execute(inventViewModel);
}
public void Update_Stock_List(InventViewModel dataStockUpdate)
{
var listViewModel = BindingContext as ManageInventViewModel;
listViewModel?.UpdateCommand.Execute(dataStockUpdate);
PopupNavigation.Instance.PopAsync();
}
}
Thanks
I managed to create the ActivityIndicator but I can not get my data loaded while I'm displaying the wait screen.
Regarding this issue, I don't see you useActivityIndicator from your code,maybe you didn't update your code, I think if you use useActivityIndicator , You can bind one property to ActivityIndicator IsRunning and IsVisible, then you can solve your issue.
Related use ActivityIndicator step, you can take a look:
ActivityIndicator

Using UIDocumentPickerViewController in Xamarin forms as a dependency service

I'm using Xamarin forms and writing a dependency service for the following objectives :
Open iOS files app. (UIDocumentPickerViewController )
Select any kind of a document.
Copy that document into my application Documents directory. (For app access)
Show that document into my application by storing its path into my SQLite DB.
What I am trying to do here is call the Files app from my application on an Entry click and the click event seems to be working well my dependency service calls perfectly but now when I try to use the UIDocumentPickerViewController I am unable to get View controller context in my dependency service to call the PresentViewController method. Now I know about the xamarin forms context but I don't know if it will work here and I don't even know if it would be a smart idea to use it as it has already been marked as obsolete and since I am not from the iOS background, I don't know what would be the right solution for it.
My code is as follows :
public class DocumentPickerRenderer : IDocumentPicker
{
public object PickFile()
{
var docPicker = new UIDocumentPickerViewController(new string[] { UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += (sender, wasCancelledArgs) =>
{
};
docPicker.DidPickDocumentAtUrls += (object sender, UIDocumentPickedAtUrlsEventArgs e) =>
{
Console.WriteLine("url = {0}", e.Urls[0].AbsoluteString);
//bool success = await MoveFileToApp(didPickDocArgs.Url);
var success = true;
string filename = e.Urls[0].LastPathComponent;
string msg = success ? string.Format("Successfully imported file '{0}'", filename) : string.Format("Failed to import file '{0}'", filename);
var alertController = UIAlertController.Create("import", msg, UIAlertControllerStyle.Alert);
var okButton = UIAlertAction.Create("OK", UIAlertActionStyle.Default, (obj) =>
{
alertController.DismissViewController(true, null);
});
alertController.AddAction(okButton);
PresentViewController(alertController, true, null);
};
PresentViewController(docPicker, true, null);
}
}
My questions:
Is my methodology correct for picking files?
what will be the object that I will be getting as a callback from a file selection and how will I get the callback?
Is there any other way or something available for xamarin forms, some guide or something that allows me to pick documents from my native file systems and gives a brief on how to handle it in both ios and android?
Hello Guys, You can use following code for picking any type of documents to mention in code using iOS Devices-
use follwing interface:
public interface IMedia
{
Task<string> OpenDocument();
}
public Task<string> OpenDocument()
{
var task = new TaskCompletionSource<string>();
try
{
OpenDoc(GetController(), (obj) =>
{
if (obj == null)
{
task.SetResult(null);
return;
}
var aa = obj.AbsoluteUrl;
task.SetResult(aa.Path);
});
}
catch (Exception ex)
{
task.SetException(ex);
}
return task.Task;
}
static Action<NSUrl> _callbackDoc;
public static void OpenDoc(UIViewController parent, Action<NSUrl> callback)
{
_callbackDoc = callback;
var version = UIDevice.CurrentDevice.SystemVersion;
int verNum = 0;
Int32.TryParse(version.Substring(0, 2), out verNum);
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.PNG,
UTType.Text,
UTType.PDF,
UTType.Image,
UTType.Spreadsheet,
"com.microsoft.word.doc",
"org.openxmlformats.wordprocessingml.document",
"com.microsoft.powerpoint.ppt",
"org.openxmlformats.spreadsheetml.sheet",
"org.openxmlformats.presentationml.presentation",
"com.microsoft.excel.xls",
};
// Display the picker
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Import);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
if (verNum < 11)
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Url);
pArgs.Url.StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Url.AbsoluteUrl);
};
}
else
{
args.DocumentPicker.DidPickDocumentAtUrls += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Urls[0]);
pArgs.Urls[0].StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Urls[0].AbsoluteUrl);
};
}
// Display the document picker
parent.PresentViewController(args.DocumentPicker, true, null);
};
pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;
parent.PresentViewController(pickerMenu, true, null);
UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;
if (presentationPopover != null)
{
presentationPopover.SourceView = parent.View;
presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
}
}
Now you need to call using following code:
var filePath = await DependencyService.Get<IMedia>().OpenDocument();
For pick document in Android, you can use following code
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{ if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
For pick document in Android, you can use following code:
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
Use below code to access the helper class: public class Media:
IMedia {
public Task<string> OpenDocument() {
var task = new TaskCompletionSource<string>();
try {
IntentHelper.OpenDocPicker((path) => { task.SetResult(path); });
} catch (Exception ex) {
task.SetResult(null);
}
return task.Task;
}
}
Since I was looking for UIDocumentPickerViewController and not UIDocumentMenuViewController the other answer was not what I was looking for :
So this is how I ended up doing it:
Calling the document picker:
var docPicker = new UIDocumentPickerViewController(new string[]
{ UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += DocPicker_WasCancelled;
docPicker.DidPickDocumentAtUrls += DocPicker_DidPickDocumentAtUrls;
docPicker.DidPickDocument += DocPicker_DidPickDocument;
var _currentViewController = GetCurrentUIController();
if (_currentViewController != null)
_currentViewController.PresentViewController(docPicker, true, null);
Where GetCurrentUIController is the function to get the current UI controller something like this :
public UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
return null;
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
For below iOS 11 i added the DidPickDocument event:
private void DocPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
{
try
{
NSUrl filePath = e.Url.AbsoluteUrl;
//This is the url for your document and you can use it as you please.
}
catch (Exception ex)
{
}
}
For above iOS 11 you use the DidPickDocumentUrls since multipick is supported there :
private void DocPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)
{
try
{
List<NSUrl> filePath = e.Urls.ToList().Select(y => y.AbsoluteUrl).ToList();
//returns the list of images selected
}
catch (Exception ex)
{
AppLogger.LogException(ex);
}
}

How to use Xamarin.Auth in Xamarin.Forms (Shared Project for iOS and Android)?

I am trying to use Xamarin.Auth for a Facebook signin. It's all set up, but I am missing the last part. The Facebook signin is not my MainActivity, one has to click on a button in order to sign in. I don't know how to start the page for the signin. I have trying to follow this approach, but as my MainActivity isn't the Facebook signin page, it won't work. I have binded (I am following the MVVM pattern) the signin button and have implemented an interface in order to use DependencyService. My problem is when I have to implement the code of the platforms.
This is what I have tried on Android:
class LoginFBImpl : Activity, ILoginFB, IFacebookAuthenticationDelegate
{
public void LoginFB() //the method in the interface
{
var auth = new FacebookAuthenticator(FacebookAuthenticator.ClientId, FacebookAuthenticator.Scope, this);
var authenticator = auth.GetAuthenticator();
var intent = authenticator.GetUI(this);
StartActivity(intent); //Problem occurs here
}
public async void OnAuthenticationCompletedAsync(UserModel token)
{
var facebookService = new FacebookService();
var name = await facebookService.GetNameAsync(token.AccessToken);
var id = await facebookService.GetIdAsync(token.AccessToken);
var picture = await facebookService.GetPictureAsync(token.AccessToken);
}
public void OnAuthenticationCancelled()
{
}
public void OnAuthenticationFailed(string message, Exception exception)
{
}
}
iOS:
public class LoginFBImpl : UIViewController, ILoginFB, IFacebookAuthenticationDelegate
{
public void LoginFB()
{
var auth = new FacebookAuthenticator(FacebookAuthenticator.ClientId, FacebookAuthenticator.Scope, this);
var authenticator = auth.GetAuthenticator();
var viewController = authenticator.GetUI();
PresentViewController(viewController, true, null);
}
public async void OnAuthenticationCompletedAsync(UserModel token)
{
DismissViewController(true, null);
var facebookService = new FacebookService();
var name = await facebookService.GetNameAsync(token.AccessToken);
var id = await facebookService.GetIdAsync(token.AccessToken);
var picture = await facebookService.GetPictureAsync(token.AccessToken);
}
public void OnAuthenticationFailed(string message, Exception exception)
{
DismissViewController(true, null);
var alertController = new UIAlertController
{
Title = message,
Message = exception?.ToString()
};
PresentViewController(alertController, true, null);
}
public void OnAuthenticationCancelled()
{
DismissViewController(true, null);
var alertController = new UIAlertController
{
Title = "Authentication cancelled",
Message = "You didn't complete the authentication process"
};
PresentViewController(alertController, true, null);
}
}
I think it has something to do with the Activity/ViewController, but I don't know how to do it properly. When I run this I get: java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference on Android, and I am expecting something similar on iOS - Haven't tested it yet as I am working on Windows.

Xamarin iOS Rich Push Notification Issue

I am trying to implement media/rich/enhanced notifications in ios. I have an iPhone 6, 6s and 7. The image I send in the payload appears in the rich notification on the 6 , but not on the 6s or 7. The code seems to just stop at the CreateDownloadTask (I have verified that I can change the Body text just before that line of code, but I can’t after). I have even had simpler version of this use NSData.FromUrl(url) but the code “breaks” at that line. The odd think is that it doesn’t truly break, it displays the text for the Body element that was originally pushed. Even a try catch doesn’t grab the error. FYI..category is there for the custom ui I am building. Can't figure out why it only works properly on iphone 6 (all the phone are on 10.2.x or above)
the payload is {"aps":{"alert":{"title":"title", "subtitle":"subtitle","body":"body"}, "category":"pushWithImage","mutable-content":1}, "pushImage":"https://ewcweb.azurewebsites.net/media/boldlythumb.png"}
I can’t send project but below is the service extension code:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Foundation;
using SDWebImage;
using UIKit;
using UserNotifications;
namespace notifications
{
[Register ("NotificationService")]
public class NotificationService : UNNotificationServiceExtension
{
UNMutableNotificationContent BestAttemptContent { get; set; }
public Action ContentHandler { get; set; }
const string ATTACHMENT_IMAGE_KEY = "pushImage";
const string ATTACHMENT_FILE_NAME = "-attachment-image.";
protected NotificationService (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public async Task<byte[]> LoadImage (string imageUrl)
{
var httpClient = new HttpClient ();
var contentsTask = await httpClient.GetByteArrayAsync (imageUrl);
return contentsTask;
}
public override void DidReceiveNotificationRequest (UNNotificationRequest request, Action<UNNotificationContent> contentHandler)
{
string imageURL = null;
ContentHandler = contentHandler;
BestAttemptContent = request.Content.MutableCopy () as UNMutableNotificationContent;
if (BestAttemptContent != null) {
if (BestAttemptContent.UserInfo.ContainsKey (new NSString (ATTACHMENT_IMAGE_KEY))) {
imageURL = BestAttemptContent.UserInfo.ValueForKey (new NSString (ATTACHMENT_IMAGE_KEY)).ToString ();
}
if (imageURL == null) {
ContentHandler (BestAttemptContent);
return;
}
var url = new NSUrl (imageURL.ToString ());
NSError err = null;
var task = NSUrlSession.SharedSession.CreateDownloadTask ( new NSMutableUrlRequest (url),(tempfile, response, error) => {
if (error != null)
{
ContentHandler (BestAttemptContent);
return;
}
if (tempfile == null)
{
ContentHandler (BestAttemptContent);
return;
}
var cache = NSSearchPath.GetDirectories (NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User, true);
var cachesFolder = cache [0];
var guid = NSProcessInfo.ProcessInfo.GloballyUniqueString;
var fileName = guid + ".png";
var cacheFile = cachesFolder + "/" + fileName;
var attachmentURL = NSUrl.CreateFileUrl (cacheFile, false, null);
NSFileManager.DefaultManager.Move(tempfile, attachmentURL, out err);
if (err != null)
{
ContentHandler (BestAttemptContent);
return;
}
// Create attachment;
var attachmentID = "image";
var options = new UNNotificationAttachmentOptions ();
var attachment = UNNotificationAttachment.FromIdentifier (attachmentID, attachmentURL, options, out err);
BestAttemptContent.Attachments = new UNNotificationAttachment [] { attachment };
BestAttemptContent.Title = BestAttemptContent.Title;
BestAttemptContent.Body = BestAttemptContent.Body;
BestAttemptContent.CategoryIdentifier = BestAttemptContent.CategoryIdentifier;
BestAttemptContent.Subtitle = BestAttemptContent.Subtitle;
//Display notification
ContentHandler (BestAttemptContent);
});
task.Resume ();
} else {
// Display notification
ContentHandler (BestAttemptContent);
}
}
public override void TimeWillExpire ()
{
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
ContentHandler (BestAttemptContent);
}
}
}

Foundation.ModelNotImplementedException for Local Notification in Xamarin Forms

I have PCL project and I need to show simple notification (when a task has finished show simple string). In iOS project, when I try to show notification in ReceivedLocalNotification with:
Window.RootViewController.PresentViewController(okayAlertController, true, null);
exception is thrown: Foundation.ModelNotImplementedException: Exception of type 'Foundation.ModelNotImplementedException' was thrown. Here is a code:
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ZXing.Net.Mobile.Forms.iOS.Platform.Init();
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var notificationSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, null
);
app.RegisterUserNotificationSettings(notificationSettings);
// check for a notification
if (options != null)
{
// check for a local notification
if (options.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
{
var localNotification = options[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;
if (localNotification != null)
{
UIAlertController okayAlertController = UIAlertController.Create(localNotification.AlertAction, localNotification.AlertBody, UIAlertControllerStyle.Alert);
okayAlertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
Window.RootViewController.PresentViewController(okayAlertController, true, null);
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}
}
}
}
base.FinishedLaunching(app, options);
}
public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
{
// show an alert
UIAlertController okayAlertController = UIAlertController.Create(notification.AlertAction, notification.AlertBody, UIAlertControllerStyle.Alert);
okayAlertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
Window.RootViewController.PresentViewController(okayAlertController, true, null);
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}
}
And NotificationService_iOS.cs:
public class NotificationService_iOS : INotificationService
{
public void Notify(string title, string text)
{
var notification = new UILocalNotification();
notification.FireDate = NSDate.FromTimeIntervalSinceNow(0); // Fire now
notification.AlertAction = title;
notification.AlertBody = text;
notification.ApplicationIconBadgeNumber = 1;
notification.SoundName = UILocalNotification.DefaultSoundName;
UIApplication.SharedApplication.ScheduleLocalNotification(notification);
}
}
The window object is not available, you need to use
var window = UIApplication.SharedApplication.KeyWindow;
see the answer here problem with Window object

Resources