Integrate twitter into iOS 5 but maintain backwards compatibility with older iOS - objective-c

Been following this great tutorial on how to integrate twitter into your app. I know there are other ways that programmers have used to integrate twitter before iOS 5 but my question is this:
My app supports iOS 3.0+ so if I integrate twitter using just the iOS 5 way of doing it, how will this affect my users that aren't using iOS 5? Will it even work for them?
Thanks!

If you are OK by only making Twitter available for iOS 5 users, you can check if Twitter is available with this:
// Don't forget to import Twitter!
#import <Twitter/Twitter.h>
....
if([TWTweetComposeViewController class] != nil) {
// your code here
}
Also, make sure that when adding the Twitter framework you set it as optional.

The official API framework wouldn't work unfortunately as the twitter app/integration is only available in iOS 5
A good solution is to use ShareKit, a free API that allows you to integrate twitter, facebook and other social network support.

You should look into DETweetComposeViewController. We built it just for this purpose. It is an iOS4 compatible re-implementation of the TWTweetComposeViewController.

Use weak linking and some code like the following:
- (void)tweet
{
Class tweeterClass = NSClassFromString(#"TWTweetComposeViewController");
if(tweeterClass != nil) {
if([TWTweetComposeViewController canSendTweet]) {
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
tweetViewController.completionHandler = ^(TWTweetComposeViewControllerResult result) {
if(result == TWTweetComposeViewControllerResultDone) {
}
[self dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:tweetViewController animated:YES completion:nil];
} else {
#if !(TARGET_IPHONE_SIMULATOR)
[self displayAlert:#"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup."];
#else
NSString *tweetString = [NSString stringWithFormat:#"http://mobile.twitter.com/home?status=%#%#", [self urlEncode:#"Check out this awesome pic: "] ,[self urlEncode:[_blobTweet.shortUrl absoluteString]]];
NSURL *tweetURL = [NSURL URLWithString:tweetString];
if ([[UIApplication sharedApplication] canOpenURL:tweetURL]) {
[[UIApplication sharedApplication] openURL:tweetURL];
}
#endif
}
} else {
// no Twitter integration could default to third-party Twitter framework
}
}
#end

Related

Native cellular call fails on VoIP incoming call in iOS 13

I have implemented CallKit for audio and video call with VoIP PushKit in iOS and it is working fine in iOS 12 and prior versions, and also it is working fine normally in iOS 13 and 13.1.
But it is failing in 2 scenarios:
1) Our App is in foreground state. When cellular call is running and VoIP push is received, then Call kit incoming call screen is showing for 5 - 10 seconds, and then both Cellular and VOIP calls are failing with Alert "Call Failed".
2) Our App is in Background or Killed state. When cellular call is running and VoIP push is received, then both Cellular and VOIP calls are failing with Alert "Call Failed". No incoming call UI is showing this time.
I am showing my code here:
- (void)registerAppForVOIPPush {
PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
pushRegistry.delegate = self;
pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
Then Push delegates
#pragma mark PKPushRegistryDelegate ----
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials: (PKPushCredentials *)credentials forType:(NSString *)type {
NSString *newToken = [self hexadecimalStringFromData:credentials.token];
//Make a note of this token to a server to send VOIP for a particular device
NSLog(#"VOIP token ::: %#", newToken);
_voipToken = newToken;
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type {
//available(iOS, introduced: 8.0, deprecated: 11.0)
[self pushRegistryDidReceivedPushWithPayload:payload forType:type withCompletionHandler:NULL];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
//available(iOS 11.0, *)
[self pushRegistryDidReceivedPushWithPayload:payload forType:type withCompletionHandler:completion];
}
- (void)pushRegistryDidReceivedPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
//Call kit configration
CXProviderConfiguration *providerConfig = [[CXProviderConfiguration alloc] initWithLocalizedName:#"my app Call"];
providerConfig.supportsVideo = NO;
providerConfig.maximumCallGroups = 1;
providerConfig.maximumCallsPerCallGroup = 1;
providerConfig.supportedHandleTypes = [[NSSet alloc] initWithObjects:[NSNumber numberWithInteger:CXHandleTypeGeneric], nil];
providerConfig.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:#"IconMask"]);
CXProvider *provider = [[CXProvider alloc] initWithConfiguration:providerConfig];
[provider setDelegate:self queue:nil];
//generate token
NSUUID *callbackUUIDToken = [NSUUID UUID];
//Display callkit
NSString *uniqueIdentifier = #"Max test";
CXCallUpdate *update = [[CXCallUpdate alloc] init];
update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:uniqueIdentifier];
update.supportsGrouping = FALSE;
update.supportsUngrouping = FALSE;
update.supportsHolding = FALSE;
update.localizedCallerName = uniqueIdentifier;
update.hasVideo = NO;
[provider reportNewIncomingCallWithUUID:callbackUUIDToken update:update completion:^(NSError * _Nullable error) {
NSLog(#"reportNewIncomingCallWithUUID error: %#",error);
}];
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
}
I have implemented CXProvider delegate method perfectly
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action{
[action fulfill];
}
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action{
[action fulfill];
}
and also managed other delegate methods to manage call and everything, and it is working perfectly in all conditions.
I have checked these two scenarios with other apps like Google Duo, Whatsapp and FaceTime and it's showing CallKit properly without failing, but in my app it is failing. I have no clue where it is failing.
So, I have this 2 stated issues for iOS 13 and later versions. Any help will be appreciated.
Thanks.
This is probably an iOS 13 bug and, if you haven't already done it, you should report it to Apple.
I think that the reason why apps like Whatapp (and the one I develop) are working, is that we build the app against the iOS 12 SDK. We do this because of the limitations of VoIP push notifications introduced in iOS 13. So, you can try to work around the issue—at least until April 2020—building against the iOS 12 SDK. Hopefully, Apple we'll soon fix this issue.
#Max I have faced the same issue that you faced in iOS version 13.0 to 13.2.0.
As many developers have reported this issue to Apple. The latest iOS version that released last week(iOS 13.2.2) has this bug resolved. So, now instead of building from older SDK, you can start working with latest SDK and xCode 11.2.1.

How to get the Ad Unit ID in Rewarded Video AdMob callbacks in Objective-C

We're developing an iOS app in objective-C and we've decided to add Firebase in our app. We also decided to add the GoogleMobileAds framework that comes with it in order to have Rewarded Video Ads.
I've implemented AdMob as detailed in the official guide but I can't figure out how to get the ad unit ID in each of the callbacks.
The only parameter I have is of type GADRewardBasedVideoAd and it doesn't seem to have any accessible data that provide the ad ID.
Here's one of the callbacks:
- (void)rewardBasedVideoAdDidOpen:(GADRewardBasedVideoAd *)rewardBasedVideoAd
{
NSLog(#"Opened reward based video ad.");
}
I need the ad ID because we're using several ads in our app and I need to know which one is ready/opened/completed/failed/etc.
I tried to use rewardBasedVideoAd.adMetadata[#"AdId"] but it returns nil.
Any help would be appreciated.
Thanks
here:
#implementation GameViewController{
GADRewardedAd *gameOverRewardedAd, *extraCoinsRewardedAd;
}
-(void)viewDidLoad{
gameOverRewardedAd = [self createAndLoadRewardedAdForAdUnit:#"ca-app-pub-YOURID"];
extraCoinsRewardedAd = [self createAndLoadRewardedAdForAdUnit:#"ca-app-pub-YOURID"];
}
-(GADRewardedAd *)createAndLoadRewardedAdForAdUnit:(NSString *) adUnitId {
GADRewardedAd *rewardedAd = [[GADRewardedAd alloc] initWithAdUnitID:adUnitId];
GADRequest *request = [GADRequest request];
[rewardedAd loadRequest:request completionHandler:^(GADRequestError * _Nullable error) {
if (error) {
// Handle ad failed to load case.
} else {
// Ad successfully loaded.
}
}];
return rewardedAd;
}
Then:
#pragma mark admob reward Ad delegate
- (void)rewardedAdDidDismiss:(GADRewardedAd *)rewardedAd {
//NSLog(#"rewardedAdDidDismiss:");
if (rewardedAd == gameOverRewardedAd) {
//do your things here
}else if (rewardedAd == extraCoinsRewardedAd){
}
}
Hope this helps.

FBSDKLoginManager error code 308 and singleton does not resolve it

I am using Facebook iOS SDK 4.5.1 . Didn't want to use 4.6 as it force login flow to SafariVirwcontrollee only.
I always get error 308 when trying to login if FBSDKLoginManager was called before. Creating singleton as other threads suggested does not work
To summarize error 308 occured in two cases
1. Call login twice.
2. Logout and then login again.
Here is the code for singleton
static FBSDKLoginManager *sharedLoginManager = nil;
+(FBSDKLoginManager*)sharedLoginManager;
{
static dispatch_once_t oncePredecate;
dispatch_once(&oncePredecate,^{
sharedLoginManager=[[FBSDKLoginManager alloc] init];
});
return sharedLoginManager;
}
- (id)init {
if (self = [super init]) {
}
return self;
}
Code to login
if ([FBSDKAccessToken currentAccessToken])
{
[self processLoginInfo];
} else
{
[FBSDKSettings enableLoggingBehavior:FBSDKLoggingBehaviorNetworkRequests];
[[FacebookLoginSingleton sharedLoginManager] logOut];
[[FacebookLoginSingleton sharedLoginManager]
logInWithReadPermissions: #[#"public_profile",#"email",#"user_friends"]
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {//}
}];
Code to logout
if ([FBSDKAccessToken currentAccessToken])
{
[[FacebookLoginSingleton sharedLoginManager] logOut];
[FBSDKAccessToken setCurrentAccessToken:nil];
}
I'm still dealing with this same error, and (assuming your info.plist, AppDelegate, and Facebook app are set up correctly), the issue completely goes away when not running the app with Xcode.
Yeah, it appears to be a bug where the only solution is to not use the debugging tools.
If you need to debug the login flow, there are probably a dozen different ways to do it outside if Xcode, but I had crashlytics installed and just let it log the crash enough times until I got out all the other kinks in my login code.

Launching Facetime from your app?

I am seeing that you can launch FaceTime from your app via
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"facetime://tel-number"]];
I am also reading that since there is no officially public FaceTime API apple will reject you.
Does anyone know if this rejection talk is true? PAIR has this feature and they have never been rejected.
This is now documented and legal:
https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/FacetimeLinks/FacetimeLinks.html#//apple_ref/doc/uid/TP40007899-CH2-SW1
My app got rejected for using FaceTime url. This is the part of response i got from Apple in resolution center.
We found the following non-public API/s in your app: Specifically,
your app uses the FaceTime URL scheme, which is undocumented.
If you have defined methods in your source code with the same names as
the above-mentioned APIs, we suggest altering your method names so
that they no longer collide with Apple's private APIs to avoid your
application being flagged in future submissions.
It was an update of a previous release. The first version got accepted without any problem. Now the update has been rejected due to the above mentioned reason. Seems i have to publish the app without the FaceTime thingy now.
Edit:
Its now legal to use FaceTime url in third party apps.
As a general rule, if you use undocumented API calls and apple catches you, they will reject your application. The reason is because they could change the API call that you are using in new IOS updates and thus would cause your application to crash or not work properly. You can try and submit using the undocumented API and hope that apple lets it through but as i said, you run the risk of Apple changing this api call or removing it completely from the OS in the future.
I don't see any reason this would be rejected, especially if there's already an app that uses this functionality. The App Store Review Guidelines are the best way to determine if your app will be rejected, and I don't see anything in there that applies to you situation.
Of course, Apple can do whatever they want, so the only way to be absolutely sure it will be accepted is to submit it, but I highly doubt you will have a problem.
It is official that you can use Native app URL strings for FaceTime video calls:
facetime:// 14085551234
facetime://user#example.com
Please refer to the link: https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/FacetimeLinks/FacetimeLinks.html
Though this feature is supported on all devices, you have to change the code a little bit for iOS 10.0 and above as openURL(_:) is deprecated.
https://developer.apple.com/documentation/uikit/uiapplication/1622961-openurl?language=objc
Please refer code below for the current and fallback mechanism, so this way it will not get rejected by Appstore.
-(void) callFaceTime : (NSString *) contactNumber
{
NSURL *URL = [NSURL URLWithString:[NSString
stringWithFormat:#"facetime://%#", contactNumber]];
if (#available(iOS 10.0, *)) {
[[UIApplication sharedApplication] openURL:URL options:#{}
completionHandler:^(BOOL success)
{
if (success)
{
NSLog(#"inside success");
}
else
{
NSLog(#"error");
}
}];
}
else {
// Fallback on earlier versions
NSString *faceTimeUrlScheme = [#"facetime://"
stringByAppendingString:contactNumber];
NSURL *facetimeURL = [NSURL URLWithString:faceTimeUrlScheme];
// Facetime is available or not
if ([[UIApplication sharedApplication] canOpenURL:facetimeURL])
{
[[UIApplication sharedApplication] openURL:facetimeURL];
}
else
{
// Facetime not available
NSLog(#"Facetime not available");
}
}
}
in contactNumber either pass phone number or appleid.
NSString *phoneNumber = #"9999999999";
NSString *appleId = #"abc#gmail.com";
[self callFaceTime:appleId];
objective-c ios

How to specify the app name user was using to post - (via app name) using SDK 3.1

Using the new Facebook SDK 3.1 and iOS 6 there are 2 (actually 3) ways to post.
(Seems the new trend is to have more options to make it more simple??) OMG!!
Here is one:
SLComposeViewController *fbPost = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[fbPost addURL:[NSURL URLWithString:href]];
[self presentViewController:fbPost animated:YES completion:nil];
And this is another way using native dialogs:
[FBNativeDialogs presentShareDialogModallyFrom:self
initialText: nil
image: nil
url: [NSURL URLWithString:href]
handler:^(FBNativeDialogResult result, NSError *error) {
if (error) {
}
else
{
switch (result) {
case FBNativeDialogResultSucceeded:
break;
case FBNativeDialogResultCancelled:
break;
case FBNativeDialogResultError:
break;
}
}
}];
We, developers, think this is cool because we give a nice functionality to the user and also because our app name appears in the post and that can make some promotion of the app.
The funny thing is that latest implementations are not allowing to specify the app name was posting, the name appears after 'via'.
I tried aswell using SLRequest:
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *fbType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
(options)[#"ACFacebookAppIdKey"] = kFacebookAppID;
(options)[#"ACFacebookPermissionsKey"] = #[#"publish_stream"];
(options)[#"ACFacebookAudienceKey"] = ACFacebookAudienceFriends;
[store requestAccessToAccountsWithType:fbType options:options completion:^(BOOL granted, NSError *error) {
if(granted) {
// Get the list of Twitter accounts.
NSArray *fbAccounts = [store accountsWithAccountType:fbType];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
(params)[#"link"] = href;
// (params)[#"picture"] = picture;
// (params)[#"name"] = name;
(params)[#"actions"] = #"{\"name\": \"Go Gabi\", \"link\": \"http://www.gogogabi.com\"}";
//Set twitter API call
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodPOST
URL:[NSURL URLWithString:#"https://www.facebook.com/dialog/feed"] parameters:params];
//Set account
[postRequest setAccount: [fbAccounts lastObject]];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if(error)
{
NSLog(#"%#", error.description);
}
else
{
NSLog(#"%#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
}
}];
} else {
}
}];
Unfortunatelly to share that name is not so trivial anymore, I wonder why and who was designing the new implementation...
I would appreciate to get some help on that, thanks in advance.
I try to make my questions funny because is soo boring spend time in so trivial topics...
When you use the SLComposeViewController, it's actually the system presenting to you their controller, and it's the user who sends using the post button. Therefore on Facebook it appears as "via iOS".
There's no way to change that.
Using the Facebook SDK 3.1, under the hood it is also using the iOS 6 native integration, so when you're calling the FBNativeDialogs, on iOS 6, it's using SLComposeViewController.
Facebook continued to develop their SDK because they provide a couple of nice modules to use "out of the box" - this includes friends list selector etc... But I believe the biggest reason for Facebook to continue supporting their SDK it for backward compatibility. Under the hood if you're not on iOS 6, it falls back to it's library, and if you are on iOS 6, it uses the system integration.
Facebook is a big thing, and now it's natively available a lot of developers will be using it, just like Twitter's integration last year. The problem of course is at that point the developer has the option to drop older iOS support, or... have a lot of duplicate code, in the sense that they will check for SLComposeViewController and if it's not available (iOS 5) then use the old Facebook SDK... You can imagine how this would become very messy very quickly.
So, the Facebook SDK (3.1) is using iOS system Facebook integration if available, or if not, it's own. In a nutshell, unless you really want the Facebook SDK goodies (friend picket to name one), and you're not planning on supporting iOS < 6 then you don't need to worry about their SDK, just use the Social framework.
So, back to your question, there are 3 ways to post to Facebook ? Actually taking into consideration what I mentioned, there are 2 ways in iOS 6: SLComposeViewController or, SLRequest. On older iOS versions, only 1: Facebook SDK.
Since the SLComposeViewController is owned by the system, not your app, it will always share as "via iOS".
On the other hand SLRequest will show your apps name. When you specify an account for your SLRequest, that account was acquired via the ACAccountStore as a result of passing in some options including ACFacebookAppIdKey, which will be used to determine your Facebook apps name to post onto the users feed as part of the post.
Hope this helps.