FaceBook SDK3.5 closeAndClearTokenInformation calls completion handler of openActiveSessionWithReadPermissions - ios6

I have the following code that I use during facebook login.
- (BOOL)openFBSessionWithAllowLoginUI:(BOOL)allowLoginUI
withCompletionHandler:(void (^)())completionHandler
{
NSArray *permissions = [NSArray arrayWithObjects:
#"user_photos",
#"email",
nil];
return [FBSession openActiveSessionWithReadPermissions:permissions allowLoginUI:allowLoginUI completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
if (error != nil) {
...
} else {
switch (state) {
case FBSessionStateOpen:
{
...
}
case FBSessionStateClosed:
{
...
}
case FBSessionStateClosedLoginFailed:
{
...
}
default:
break;
}
}
}];
}
The above works fine for login. But, when I log out using the following code
[FBSession.activeSession closeAndClearTokenInformation];
this again calls the completionHandler of openActiveSessionWithReadPermissions:permissions allowLoginUI:. That does not make sense to me. I do not think it is the right behavior. Has anyone seen this problem? How do we log-out? I am using SDK 3.5 on iOS6.

According to this thread on the Facebook Developer bug tracker, this behavior is "by design".
In fact, I did suggest a better name for this method would be: openActiveSessionWithReadPermissions:allowLoginUI:stateChangeHandler:
as that more accurately describes what's happening (the "completionHandler" is in fact called on state change).
You can handle this in several ways: Ben Cohen suggest you can either set the completionHandler to nil within the completion block (to ensure run-once), this answer suggests creating an FBSessionStateHandler run-once handler, or you can switch on the state change.
Ideally, as we rely on the Facebook SDK for specific purposes (log in, log out, make requests, etc.), these would be provided via delegates, but since the SDK developers apparently got a bit carried away with "ooh blocks!!", you're left having to define your state change handler at the point where you first open your session.

This is a very bad behavior I think.
FBSession has a hidden property:
#property (readwrite, copy) FBSessionStateHandler loginHandler;
So you can set it to nil by this code in the block like this:
[FBSession openActiveSessionWithReadPermissions:FACEBOOK_PERMISSIONS
allowLoginUI:NO
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[[FBSession activeSession] performSelector:NSSelectorFromString(#"setLoginHandler:") withObject:nil];
#pragma clang diagnostic pop
// Your stuff...
}];

Related

GetNetworkType in Android 11

Following the changes posted here, the getNetworkType method is deprecated from Android R and onwards.
When trying to use this method in a R compiled application, results in the following exception being thrown:
java.lang.SecurityException: getDataNetworkTypeForSubscriber: uid 10225 does not have android.permission.READ_PHONE_STATE.
at android.os.Parcel.createExceptionOrNull(Parcel.java:2285)
at android.os.Parcel.createException(Parcel.java:2269)
at android.os.Parcel.readException(Parcel.java:2252)
at android.os.Parcel.readException(Parcel.java:2194)
at com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:7565)
at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2964)
at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2928)
at com.ironsource.environment.ConnectivityService.getCellularNetworkType(ConnectivityService.java:197)
at com.ironsource.sdk.service.DeviceData.updateWithConnectionInfo(DeviceData.java:98)
at com.ironsource.sdk.service.DeviceData.fetchMutableData(DeviceData.java:54)
at com.ironsource.sdk.service.TokenService.collectDataFromDevice(TokenService.java:120)
at com.ironsource.sdk.service.TokenService.getRawToken(TokenService.java:177)
at com.ironsource.sdk.service.TokenService.getToken(TokenService.java:166)
at com.ironsource.sdk.IronSourceNetwork.getToken(IronSourceNetwork.java:183)
This is fine and is expected according to the documentation. If I compile the application to any version before Android R, the exception doesn't show.
This exception indicates that I need to request the android.permission.READ_PHONE_STATE permission.
I wanted to know if there is a way to get the network type with any other API that does NOT require this permission (as this permission's level is dangerous and I would rather not ask the user for it).
Take runtime permission for READ_PHONE_STATE to ignore crash of getDataNetworkTypeForSubscriber
#Override
protected void onStart() {
super.onStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
int res = checkSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
if (res != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{android.Manifest.permission.READ_PHONE_STATE}, 123);
}
}
}
private final static int REQUEST_CODE_ASK_PERMISSIONS = 1002;
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_PERMISSIONS:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getApplicationContext(), "READ_PHONE_STATE Denied", Toast.LENGTH_SHORT)
.show();
} else {
}
stepAfterSplash();
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
You can still use getDataNetworkType(); This method does not necessarily need READ_PHONE_STATE, as stated in his Doc, but that it's sufficient "that the calling app has carrier privileges".
https://developer.android.com/reference/android/telephony/TelephonyManager#getDataNetworkType()
For what I know about getting those privigileges, it could be tricky/really hard, you may look into getting carrier privileges and using this method, which is also the suggested substitution for getNetworkType().
This method necessarily need READ_PHONE_STATE by this way in your activity not just manifest >>>
// Check if the READ_PHONE_STATE permission is already available.
if(ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_PHONE_STATE)) {
//here >> use getNetworkType() method
// like this example
mStationInfo.set_networkType(mTelephonyManager.getNetworkType());
}
else {}
We can use ConnectivityManager#getNetworkCapabilities and NetworkCapablities#hasTransport like this
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkCapabilities caps = cm.getNetworkCapabilities(cm.getActivityNetwork());
boolean isMobile = caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
boolean isWifi = caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
Reference:
android.net.NetworkInfo
This class was deprecated in API level 29. Callers should instead use the ConnectivityManager.NetworkCallback API
to learn about connectivity changes, or switch to use
ConnectivityManager#getNetworkCapabilities or
ConnectivityManager#getLinkProperties to get information
synchronously. Keep in mind that while callbacks are guaranteed to be
called for every event in order, synchronous calls have no such
constraints, and as such it is unadvisable to use the synchronous
methods inside the callbacks as they will often not offer a view of
networking that is consistent (that is: they may return a past or a
future state with respect to the event being processed by the
callback). Instead, callers are advised to only use the arguments of
the callbacks, possibly memorizing the specific bits of information
they need to keep from one callback to another.
So my friend I have the same trouble as you but so far I came with a temporary fix I use the compileSdkVersion 29 not 30 as well as the targetSdkVersion 29 and my buildToolsVersion 28.0.3 and my app is loading fine.
Since this problem by me its coming due a third party library so till the fix the error I can not fix it alone, but I think with this temporary solution for now is quite well.

Application getting crashed on setting the vocabulary for carName using Siri Kit

I am trying to develop an application using SiriKit to get the car door lock status and set the same from Siri. I followed this blog https://www.appcoda.com/sirikit-introduction/ and did all the setup replacing the INStartWorkoutIntent with INGetCarLockStatusIntent.
But when i try to set the vocabulary for carName, the application is getting crashed with following exception,
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Illegal attempt to provide vocabulary of type INVocabularyStringTypeCarName by an app that does not handle any intents that could use that type of vocabulary'
The source code that i am using to set the vocabulary is,
INPreferences.requestSiriAuthorization { (status) in
}
INVocabulary.shared().setVocabularyStrings(["benz", "bmw", "audi"], of: .carName)
In AppDelegate, i have added the following method,
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
guard let intent = userActivity.interaction?.intent as? INGetCarLockStatusIntent else {
print("AppDelegate: Start Workout Intent - FALSE")
return false
}
print("AppDelegate: Start Workout Intent - TRUE")
print(intent)
return true
}
Also created the extension for intent handler and implemented INSetCarLockStatusIntentHandling, INGetCarLockStatusIntentHandling protocols. I am getting this issue when i try to run it in iPhone 10.
Check, if in TARGETS of your project in Build Phases->Embed App Extensions added your Siri Extension. Maybe if you replace the INStartWorkoutIntent with INGetCarLockStatusIntent, old INStartWorkoutIntent remained there.
My crash fix this.
I was facing a similar issue. Make sure your extension's Deployment Target is set to appropriate iOS version. Creating an extension with the latest Xcode (at the moment 10.1) will set the Deployment Target to 12.1 and thus cause crash when run on iOS 10. So you should change it to your desired minimum.

Strange Xcode 4.6 related bug

The error we're getting is something like this:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_CDSnapshot_Widget_ unlockObjectStore]: unrecognized selector sent to instance 0x1c5a4350'
sometimes the Widget class is sent that selector, sometimes it's __NSCFString, sometimes the crash is this:
[NSManagedObjectContext unlockObjectStore]: message sent to deallocated instance 0x1ec658c0
I think I've narrowed down where the issue is occurring, but I have no idea why this code would be causing it.
Here's an example structure of our data access classes:
// DataController.m
static NSPersistentStoreCoordinator *_persistentStoreCoordinator;
static NSManagedObjectModel *_managedObjectModel;
static NSManagedObjectContext *_mainManagedObjectContext;
#implementation DataController
- (NSManagedObjectContext *) privateManagedObjectContext {
return [DataController buildManagedObjectContextForConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (NSManagedObjectContext *) defaultManagedObjectContext {
return [self managedObjectContextForConcurrencyType: self.defaultConcurrencyType];
}
- (NSManagedObjectContext *) managedObjectContextForConcurrencyType: (NSManagedObjectContextConcurrencyType) type {
if (type == NSMainQueueConcurrencyType)
return [self mainManagedObjectContext];
else if (type == NSPrivateQueueConcurrencyType)
return [self privateManagedObjectContext];
return nil;
}
// calling _dataController.defaultManagedObjectContext from within the Widgets class
// essentially calls this method for a new context using NSPrivateQueueConcurrencyType as
// the type parameter
+ (NSManagedObjectContext *) buildManagedObjectContextForConcurrencyType: (NSManagedObjectContextConcurrencyType) type {
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType: type];
[context setUndoManager: nil];
[context setMergePolicy: NSMergeByPropertyObjectTrumpMergePolicy];
[context setPersistentStoreCoordinator: _persistentStoreCoordinator];
return context;
}
#end
and our widgets class
// Widgets.m
static DataController *_dataController;
#implementation Widgets
+ (void) initialize {
_dataController = [[DataController alloc] init];
}
+ (NSArray *)certainWidgets {
return [self certainWidgetsInManagedObjectContext:_dataController.defaultManagedObjectContext];
}
+ (NSArray *) certainWidgetsInManagedObjectContext: (NSManagedObjectContext *) context {
// boiler plate CoreData fetching code
}
#end
This is an example of code used to fetch widgets
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (Widget *w in [Widgets certainWidgets]) {
if ([w.isValid intValue]) {
// do something extraoridarily fantastic with the widget
}
}
});
This only happens in Xcode 4.6, Release mode (not in Debug). We're not seeing anything in the Xcode 4.6 release notes that would give us a clue as to what's happening.
I suspect the issue has to do with how we've structured our data access class (DataController) coupled with the fact that we're using class methods to handle all data access within the Widgets class. The reason for my suspicion is that when we remove the class methods from the Widgets class and make them instance methods instead, get rid of the +initialize method, and set up an NSManagedObjectContext for each instance of the Widgets class, the problem seems to go away.
Just to clarify, I think I've fixed the issue, but I'm not comfortable pushing out the fix until I understand why the above mentioned changes fix it. It looks like there's some sort of memory issue or bad programming paradigm that we're not catching onto. Any pointers?
Turns out this is a bug related to CLang optimizations. We've found a few other people experiencing the same problem. Turning all optimizations off seems to fix the issue.
It happens, for us, when creating a static data management instance, spawning a new thread, and using that static instance to create a managed object context.
Our fix will be to rethink our data management paradigm.
If you're using this screen over your app, or it appears several time & crashes on second one than check your memory management over this screen. It may be the case, when you're holding in memory several copies of screen. Check notification centre removal observer for example.

iOS 6 Game Center authenticateHandler can't login after a cancel

When using the authenticateHandler in iOS 6, game center won't present the login view if the user cancels it. I realize game center will auto lockout an app after 3 cancel attempts, but I'm talking about just 2 attempts. If they cancel the login, they have to leave the app and come back before game center will present the login even through the authenticateHandler is getting set again. Any ideas on how to handle this case in iOS 6?
It works fine when using the older authenticateWithCompletionHandler method:
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0
GKLocalPlayer.localPlayer.authenticateHandler = authenticateLocalPlayerCompleteExtended;
#else
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:authenticateLocalPlayerComplete];
#endif
The reason this is important for my app is that it requires Game Center for multi-player. The app tries to authenticate to game center on launch, but if the user cancels we don't ask them at launch again so they won't get nagged. What we do is show a Game Center Login button if they aren't logged in when they select multi-player. The login button forces a game center login by calling authenticateWithCompletionHandler (and now by setting GKLocalPlayer.localPlayer.authenticateHandler again).
Better use runtime checks (instancesRespondToSelector:) instead of preprocessor #if statements, so that you can use recommended methods where they are available and depreciated ones elsewhere. I actually found I need to distinguish three cases before setting the invite handler, as the authentication handler might also get called with a nil view controller:
-(void)authenticateLocalPlayer
{
if ([[GKLocalPlayer class] instancesRespondToSelector:#selector(setAuthenticateHandler:)]) {
[[GKLocalPlayer localPlayer] setAuthenticateHandler:^(UIViewController *gameCenterLoginViewController, NSError *error) {
if (gameCenterLoginViewController) {
[self.presentedViewController presentViewController:gameCenterLoginViewController
animated:YES
completion:^{
[self setInviteHandlerIfAuthenticated];
}];
} else {
[self setInviteHandlerIfAuthenticated];
}
}];
} else { // alternative for iOS < 6
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error) {
[self setInviteHandlerIfAuthenticated];
}];
}
}
Yet more cases must be distinguished within the invite handler, as matchForInvite:: is new in iOS6 as well and avoids yet another round through game center view controllers:
-(void)setInviteHandlerIfAuthenticated
{
if ([GKLocalPlayer localPlayer].isAuthenticated) {
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {
if (acceptedInvite) {
if ([GKMatchmaker instancesRespondToSelector:#selector(matchForInvite:completionHandler:)]) {
[self showInfoAnimating:YES completion:NULL];
[[GKMatchmaker sharedMatchmaker] matchForInvite:acceptedInvite
completionHandler:^(GKMatch *match, NSError *error) {
// ... handle invited match
}];
} else {
// alternative for iOS < 6
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:acceptedInvite] autorelease];
mmvc.matchmakerDelegate = self;
// ... present mmvc appropriately
// ... handle invited match found in delegate method matchmakerViewController:didFindMatch:
}
} else if (playersToInvite) {
// ... handle match initiated through game center
}
};
}
}
Let me know if this helps.
I dont' think this is possible in iOS 6.0. There were API calls to do this in the early SDK builds that were removed before release.
In the WWDC 2012 Video: Session 516 - Integrating Your Games with Game Center [8:30] They actually show code where you call an authenticate method:
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticationHandler = //handle the callback...
[localPlayer authenticate];
This method is now private API but you can see it in action by calling:
[[GKLocalPlayer localPlayer] performSelector:#selector(_authenticate)];
It does exactly what you want, but can't be used because it's now private.
You can also trigger the authentication process by posting the UIApplicationWillEnterForegroundNotification notification:
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
I assume it would be unadvisable to do this in live code.

WP7 check if internet is available

My app WP7 was not accepted because it fails to load if the internet is not available. I looked for a way to check it and found this command
NetworkInterface.GetIsNetworkAvailable()
But it isn't working on the emulator and I do not have any device to test it.
Could someone tell me if it returns false if the device is in Airplane mode? If not, how can I check for it?
Thanks,
Oscar
Edit: I also tried with this code:
try
{
wsClient.CurrenciesCompleted += new EventHandler<CurrencyConversion.CurrenciesCompletedEventArgs>(wsClient_CurrenciesCompleted);
wsClient.CurrenciesAsync(null);
}
catch
{
NetworkNotAvailable();
}
But I am not able to catch the exception, I also tried in the wsClient_CurrenciesCompleted method, but also no good.
Where could I test it?
Don't test for "the internet in general" - test for the service you'll actually be connecting to. Test for it by trying to connect to it - make some simple, non-destructive request on start-up. Yes, that will take a tiny bit of the user's data allowance, but:
You will be warming up the networking stack and making a connection which should end up being kept alive automatically, so future latency will be reduced.
You could warn the user that they may have limited functionality if the connection fails.
An Alternative to Jon's suggestion is to check which network interface is available. This is very handy in cases were you need to adjust which service you call based on network speed. For example the switch statement below could be modified to return an Enum to represent the quality of the network.
public class NetworkMonitorClass
{
private Timer timer;
private NetworkInterfaceType _currNetType = null;
private volatile bool _valueRetrieved = false;
public NetworkMonitorClass()
{
//using a timer to poll the network type.
timer = new Timer(new TimerCallBack((o)=>
{
//Copied comment from Microsoft Example:
// Checking the network type is not instantaneous
// so it is advised to always do it on a background thread.
_currNetType = Microsoft.Phone.Net.NetworkInformation.NetworkInterface.NetworkInterfaceType;
_valueRetrieved = true;
}), null, 200, 3000); // update the network type every 3 seconds.
}
public NetworkInterfaceType CurrentNetworkType
{
get
{
if(false == _valueRetrieved ) return NetworkInterfaceType.Unknown;
return _currNetType;
}
private set { ;}
}
public bool isNetworkReady()
{
if(false == _valueRetrieved ) return false;
switch (_currentNetworkType)
{
//Low speed networks
case NetworkInterfaceType.MobileBroadbandCdma:
case NetworkInterfaceType.MobileBroadbandGsm:
return true;
//High speed networks
case NetworkInterfaceType.Wireless80211:
case NetworkInterfaceType.Ethernet:
return true;
//No Network
case NetworkInterfaceType.None:
default:
return false;
}
}
}
See http://msdn.microsoft.com/en-us/library/microsoft.phone.net.networkinformation.networkinterface.networkinterfacetype(VS.92).aspx
GetIsNetworkAvailable() will always return true in the emulator. For testing in the emulator you'll need to work round this in code.
This can be a useful quick check but you also (as Jon pointed out) need to handle the scenario of not being able to connect to your specific server.
Handling this can be done by catching the WebException when you try and get the response in the callback.
private static void DownloadInfoCallback(IAsyncResult asynchronousResult)
{
try
{
var webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
// This will cause an error if the request failed
var webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
.....
}
catch (WebException exc)
{
// Handle error here
}
}
GetIsNetworkAvailable() works properly on device.
You can mock your handling of this for testing in the emulator using Microsoft.Devices.Environment.DeviceType.
I would be inclined to test both for avaiability of the internet and availability of your site through exception handling and provide feedback to the user of the app that indicates what the true reason is for features being unavailable.

Resources