Im doing a POC of implementing the NFCTagReader into a xamarin.ios app.
https://developer.xamarin.com/samples/monotouch/ios11/NFCTagReader/
I've taken the NFCTagReader from the xamarin site and set all the appropriate provision settings to get access to the tag reader. The problem is that when i click scan the "Ready to Scan" window pops up as expected then i scan a tag and it shows the little tick on the screen to show that it found but it never breaks into the DidDetect method of my code in the delegate. It will hit the DidInvalidate method and give the code for ReaderSessionInvalidationErrorUserCanceled.
Any ideas what i'm missing. Following is my code snippet:
partial void Scan(UIBarButtonItem sender)
{
InvokeOnMainThread(() =>
{
Session = new NFCNdefReaderSession(this, null, true);
if (Session != null)
{
Session.BeginSession();
}
});
}
#endregion
#region NFCNDEFReaderSessionDelegate
public void DidDetect(NFCNdefReaderSession session, NFCNdefMessage[] messages)
{
foreach (NFCNdefMessage msg in messages)
{
DetectedMessages.Add(msg);
}
DispatchQueue.MainQueue.DispatchAsync(() =>
{
this.TableView.ReloadData();
});
}
public void DidInvalidate(NFCNdefReaderSession session, NSError error)
{
var readerError = (NFCReaderError)(long)error.Code;
if (readerError != NFCReaderError.ReaderSessionInvalidationErrorFirstNDEFTagRead &&
readerError != NFCReaderError.ReaderSessionInvalidationErrorUserCanceled)
{
InvokeOnMainThread(() =>
{
var alertController = UIAlertController.Create("Session Invalidated", error.LocalizedDescription, UIAlertControllerStyle.Alert);
alertController.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
DispatchQueue.MainQueue.DispatchAsync(() =>
{
this.PresentViewController(alertController, true, null);
});
});
}
}
Little bit of a blonde moment and was just going to close this question but thought would answer this just in case someone else comes across the same problem as I did.
The problem ended up being caused by the fact that the tags that I was given were blank. Therefore the phone would click when it detected the tag but would never hit the didDetect method. As soon as I wrote something to the NFC tag with an Android tag writer app then DidDetect fired as expected.
Related
I have created an IOS app using React Native. The app consists of a Song Menu screen and a song screen. On the song screen the user is able to press the play button to play the song.
I am currently testing it out using TestFlight. It is working fine on my phone. However, on my friend's phone it keeps crashing. The error is very generic giving RCTFatal as the error.
However, I have narrowed the problem down to code which stops songs from playing when the user navigates away from the Song page.
The relevant code is here:
export default class Song extends Component {
playAudio = (file) => {
var s = new Sound('audio/' + file, Sound.MAIN_BUNDLE, (error) => {
if (error){
console.log('error', error)
} else {
s.play(() => {
s.release()
})
}
})
/* Problem LINE #1 */
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
() => s.release()
)
}
/* Problem LINE #2 */
componentWillUnmount(){
this.willBlurSubsciption.remove()
}
/* Other code here for displaying UI and the clickable audio button which loads the playAudio function. */
...
}
When I remove the subscription code above (shown on two lines) then there is no crash. However, the song will not stop playing after the user goes back to the main menu. When I put the lines back in there it crashes for my tester with the RCTFatal error.
Note: The app crashes whether my tester plays the audio file or not, but always when he navigates back to the Song Menu.
What is the problem with my code? Why is it generating such a cryptic error? And why does my IOS device not crash but his does?
I guess, from the react-navigation event you're using (willBlur), the version of react-navigation is <=4.x, so the event subscription methods looks correct. If you're using react-navigation version 5 then these events have totally changed.
You should be very careful using these event subscriptions when disposing native modules.
You should try to be safe by checking if each function/method exists before calling it and of course wrap every function call to a try..catch error handler:
playAudio = (file) => {
// If audio playback is triggered by user,
// then you should always check if there is already an object
// and if there is, either exit the function or stop/dispose current sound and start a new one
if (this.sound) {
// either return and let the current sound play
// return;
// or stop and release the sound if you have play/stop/pause sound controls
try {
this.sound.release()
} catch(error) {}
finally {
this.sound = null
}
}
this.sound = new Sound(`audio/${file}`, Sound.MAIN_BUNDLE, (error) => {
if (error) {
console.log('error', error)
this.sound = null;
} else {
this.sound.play(() => {
try {
if (this.sound) {
this.sound.release()
}
} catch(error) {}
finally {
this.sound = null
}
})
}
})
/* Problem LINE #1 */
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
() => {
try {
if (this.sound) {
this.sound.release()
}
} catch(error) {}
finally {
this.sound = null
}
}
)
}
/* Problem LINE #2 */
componentWillUnmount() {
try {
this.willBlurSubsciption &&
this.willBlurSubsciption.remove &&
this.willBlurSubsciption.remove()
} catch(error) {}
finally {
this.willBlurSubsciption = null
}
}
I guest this.willBlurSubscription = this.props.navigation.addListener will create one listener every playAudio invoked. So from the second time, the s will be released 2 times -> cause the cash.
Try to push the listener in componentDidMount or check the listener before add it, like:
/* Problem LINE #1 */
if (this.willBlurSubscription) {
this.willBlurSubsciption.remove();
this.willBlurSubsciption = null;
}
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
() => s.release()
)
I'm making Android app with Xamarin, This use zxing.
When user click a button, It show QrScan page and dialog for asking allow camera permission.
I want to show dialog asking permission by user allow permission every time clicked button.
Now, If user click deny, permission dialog ever don't shown, before restart application.
Have you any idea?
This is my source.
Android --- MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ZXing.Net.Mobile.Forms.Android.Platform.Init();
LoadApplication(new App { OSVersion = "Android Version " + "2.0" });
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
// If this is not be, occur unexpected exception when user click deny
if(grantResults[0] == Permission.Denied)
{
return;
}
global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
And this is my Executing QrScan Method In PCL project which called clicked button.
public async void ImgQrScan_Clicked(object sender, EventArgs e)
{
this.TappedEvent?.Invoke(sender, e);
CustomScanViewMaker();
await Navigation.PushModalAsync(oCustomQrScanPage);
zxingPage.IsScanning = true;
string sScanResult = "";
zxingPage.OnScanResult += (result) =>
{
sScanResult = result.Text;
zxingPage.IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
this.OnClicked?.Invoke(sender, new QrScannerClickEventArgs(sScanResult));
await Navigation.PopModalAsync();
});
};
this.OnClicked?.Invoke(sender, new QrScannerClickEventArgs(sScanResult));
}
Thank you.
I want to show dialog asking permission by user allow permission every time clicked button.
You could try using shouldShowRequestPermissionRationale method to implement this feature, as the document said :
To help find situations where the user might need an explanation, Android provides a utiltity method, shouldShowRequestPermissionRationale(). This method returns true if the app has requested this permission previously and the user denied the request.
For its usage, you could refer to the official document Requesting Permissions at Run Time, in C#, it's something like this :
// Here, this is the current activity
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != Permission.Granted)
{
// Should we show an explanation?
if (ActivityCompat.ShouldShowRequestPermissionRationale(this, Manifest.Permission.Camera))
{
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
// For example if the user has previously denied the permission.
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
Log.Info(TAG, "Displaying camera permission rationale to provide additional context.");
}
else
{
// No explanation needed, we can request the permission.
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.Camera }, REQUEST_CAMERA);
// REQUEST_CAMERA is an app-defined int constant. The callback method gets the
// result of the request.
}
}
else
{
System.Diagnostics.Debug.WriteLine("Permission Granted!!!");
}
I just migrate my Xamarin iOS app to Xamarin Unified using the Migration Tool.
The code below was working fine and the app didn’t have any error or warning before the migration.
After the migration I got the following errors
Error-1
PresentViewController doesn’t accept the MediaPickerController object as a parameter.
Error-2
mediaPickerController doesn’t have the method DismissViewController
protected void TakePicture()
{
MediaPickerController mediaPickerController = mediaPicker.GetTakePhotoUI(new StoreCameraMediaOptions
{
Name = this.PictureName + ".jpg",
DefaultCamera = CameraDevice.Rear
});
if (!mediaPicker.IsCameraAvailable)
{
ShowUnsupported();
}
//Error-1
PresentViewController(mediaPickerController, true, null);
try
{
mediaPickerController.GetResultAsync().ContinueWith(t =>
{
BTProgressHUD.Show("Processing");
// Dismiss the UI yourself
//Error-2
mediaPickerController.DismissViewController(true, () =>
{
if (t.IsCanceled || t.IsFaulted)
{
BTProgressHUD.Dismiss();
return;
}
MediaFile file = t.Result;
FinishedPickingMedia(file);
BTProgressHUD.Dismiss();
});
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception ex)
{
Insights.Report(ex, ReportSeverity.Error);
}
}
Download the latest Xamarin.Mobile dll from here https://components.xamarin.com/view/xamarin.mobile and reference the lib/ios-unified/Xamarin.Mobile.dll instead of the old dll.
This will fix the errors.
I just migrate my Xamarin iOS app to Xamarin Unified using the Migration Tool. The code below was working fine and the app didn’t have any error or warning before the migration. After the migration I got the following errors Error-1 PresentViewController doesn’t accept the MediaPickerController object as a parameter. Error-2 mediaPickerController doesn’t have the method DismissViewController
protected void TakePicture()
{
MediaPickerController mediaPickerController = mediaPicker.GetTakePhotoUI(new StoreCameraMediaOptions
{
Name = this.PictureName + ".jpg",
DefaultCamera = CameraDevice.Rear
});
if (!mediaPicker.IsCameraAvailable)
{
ShowUnsupported();
}
//Error-1
PresentViewController(mediaPickerController, true, null);
try
{
mediaPickerController.GetResultAsync().ContinueWith(t =>
{
BTProgressHUD.Show("Processing");
// Dismiss the UI yourself
//Error-2
mediaPickerController.DismissViewController(true, () =>
{
if (t.IsCanceled || t.IsFaulted)
{
BTProgressHUD.Dismiss();
return;
}
MediaFile file = t.Result;
FinishedPickingMedia(file);
BTProgressHUD.Dismiss();
});
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception ex)
{
Insights.Report(ex, ReportSeverity.Error);
}
}
You need to update your Xamarin component that contains MediaPickerController to the latest version compatible with Xamarin.iOS unified code!
The latest version of Xamarin.Mobile is 0.7.6. Double check with your project if your are using an older one.
It appears that using the itms-apps//.... URL scheme doesn't work in iOS 6 with the new App Store to show the product reviews area. Now I'm using the code below, but it just shows you the product. How do you get to the review area to ask for a review and take the user to the right tab of the product displayed?
void DoReview()
{
var spp = new StoreProductParameters(appId);
var productViewController = new SKStoreProductViewController();
// must set the Finished handler before displaying the view controller
productViewController.Finished += (sender2, err) => {
// Apple's docs says to use this method to close the view controller
this.navigationController.DismissViewController(true, null);
MySettings.AskedForReview = true;
};
productViewController.LoadProduct(spp, (ok, err) => { // ASYNC !!!
if (ok)
{
this.navigationController.PresentViewController(productViewController, true, null);
}
else
{
Console.WriteLine(" failed ");
if (err != null)
Console.WriteLine(" with error " + err);
}
});
}
Hello you could try iRate from Nick Lockwood and see if that fits your needs, you can find the MonoTouch bindings of iRate here.
btw it uses the following URL to open AppStore in review mode:
itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id= your-AppID-here
Alex