how to share Image on Facebook using Swift in IOS - ios

I want to share an image on Facebook using swift language. I am able to share image using Objective C.
I tried using
1) How to Share image + text with facebook in swift iOS?
but not working, then I tried using other options but not able to share image using swift language. then I tried this
2) https://github.com/rajeshsegu/facebook-ios-swift/tree/master/FBApp
I copied Facebook.swift and write another function for share image my code for Facebook.swift
import Foundation
import Social
let FB = Facebook();
class Facebook {
var fbSession:FBSession?
init(){
self.fbSession = FBSession.activeSession();
}
func hasActiveSession() -> Bool{
let fbsessionState = FBSession.activeSession().state;
if ( fbsessionState == FBSessionState.Open
|| fbsessionState == FBSessionState.OpenTokenExtended ){
self.fbSession = FBSession.activeSession();
return true;
}
return false;
}
func login(callback: () -> Void){
let permission = ["publish_actions","email","user_location","user_birthday","user_hometown","user_photos","user_about_me"];
let activeSession = FBSession.activeSession();
let fbsessionState = activeSession.state;
var showLoginUI = true;
if(fbsessionState == FBSessionState.CreatedTokenLoaded){
showLoginUI = false;
}
if(fbsessionState != FBSessionState.Open
&& fbsessionState != FBSessionState.OpenTokenExtended){
FBSession.openActiveSessionWithPublishPermissions(permission, defaultAudience: FBSessionDefaultAudience.Friends, allowLoginUI: showLoginUI, completionHandler: { (session:FBSession!, state:FBSessionState, error:NSError!) -> Void in
if(error != nil){
println("Session Error: \(error)");
}
self.fbSession = session;
// println("Session : \(self.fbSession?.permissions)");
callback();
})
// FBSession.openActiveSessionWithReadPermissions(
// permission,
// allowLoginUI: showLoginUI,
// completionHandler: { (session:FBSession!, state:FBSessionState, error:NSError!) in
//
// if(error != nil){
// println("Session Error: \(error)");
// }
// self.fbSession = session;
// println("Session : \(self.fbSession?.permissions)");
// callback();
//
// }
// );
return;
}
callback();
}
func logout(){
self.fbSession?.closeAndClearTokenInformation();
self.fbSession?.close();
}
func getInfo(){
FBRequest.requestForMe()?.startWithCompletionHandler({(connection:FBRequestConnection!, result:AnyObject!, error:NSError!) in
if(error != nil){
println("Error Getting ME: \(error)");
}
println("\(result)");
var dictData:NSDictionary!=result as? NSDictionary
});
}
func handleDidBecomeActive(){
FBAppCall.handleDidBecomeActive();
}
func shareImage (imageName:UIImageView){
let fbsessionState = FBSession.activeSession().state;
if(fbsessionState == FBSessionState.Open)
{
//var arr : NSArray=NSArray(array: ["publish_actions"])
self.fbSession?.requestNewPublishPermissions(["publish_actions"], defaultAudience:FBSessionDefaultAudience.Friends, completionHandler: { (session:FBSession!, error:NSError!) -> Void in
if(error == nil){
var requestConneciton:FBRequestConnection=FBRequestConnection()
requestConneciton.errorBehavior=FBRequestConnectionErrorBehavior.None
requestConneciton.addRequest(FBRequest(forUploadPhoto:imageName.image)) { (connection:FBRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
println("\(error)");
println("\(result)");
//[self showAlert:#"Photo Post" result:result error:error];
}
requestConneciton.start()
}
else if(error.fberrorCategory == FBErrorCategory.UserCancelled){
var alt:UIAlertView!=UIAlertView(title:"Permission denied", message:"Unable to get permission to post", delegate:nil, cancelButtonTitle:"Ok")
alt.show()
}
})
}
}
func showAlert(msg:NSString!,result:AnyObject,error:NSError!) {
var alertTitle:NSString!
var alertMsg:NSString!;
if (error == nil) {
if((error.fberrorUserMessage != nil && FBSession.activeSession().isOpen) ) {
alertTitle = "";
}
else{
// Otherwise, use a general "connection problem" message.
alertMsg = "Operation failed due to a connection problem, retry later.";
}
}
else {
//var dictResult:NSDictonary = result as NSDictionary
alertMsg="Successfully posted "
var alertObj:UIAlertView!=UIAlertView(title:"Demo App", message:alertMsg, delegate:nil, cancelButtonTitle:"Ok");
alertObj.show();
}
}
func performPublishAction(action:() -> Void){
var arrP:NSArray!=NSArray(array: ["publish_actions"]);
fbSession?.requestNewPublishPermissions(arrP, defaultAudience:FBSessionDefaultAudience.Friends, completionHandler: { (session:FBSession!, error:NSError!) -> Void in
if(error == nil){
action()
}
else if(error.fberrorCategory == FBErrorCategory.UserCancelled){
var alt:UIAlertView!=UIAlertView(title:"Permission denied", message:"Unable to get permission to post", delegate:nil, cancelButtonTitle:"Ok")
alt.show()
}
})
}
}
and In ViewController.swift
import UIKit
class ViewController: UIViewController,FBLoginViewDelegate {
#IBOutlet var imageObj:UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnFBLoginClick(sender: UIButton) {
FB.login(self.handleLogin);
}
func handleLogin(){
println("SUCCESS");
FB.getInfo();
}
#IBAction func btnShareclick(sender: UIButton) {
FB.shareImage(imageObj)
}
}
Login button click working perfect and it can fetch all data of login user, but when i share the image using FB.shareImae(imageObj) its give me a permission error, I am working on this point from last 2 days now I am stuck. if i write same code in Objective C its working fine.
eror :
permissions:(
"public_profile",
email,
"user_friends"
)>, com.facebook.sdk:ParsedJSONResponseKey={
body = {
error = {
code = 200;
message = "(#200) Permissions error";
type = OAuthException;
};
};
code = 403;
}}
Can any one help me? to find out this problem...
I don't want to use SLComposeViewController, I want to use Facebook framework.
Thank you in advance!

This is a code i created in an old project. Give it a try ;)
#IBAction func btn_Share(sender: AnyObject) {
let facebookPost = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookPost.completionHandler = {
result in
switch result {
case SLComposeViewControllerResult.Cancelled:
//Code to deal with it being cancelled
break
case SLComposeViewControllerResult.Done:
//Code here to deal with it being completed
break
}
}
facebookPost.setInitialText("Test Facebook") //The default text in the tweet
facebookPost.addImage(masked_image) //Add an image
facebookPost.addURL(NSURL(string: "http://facebook.com")) //A url which takes you into safari if tapped on
self.presentViewController(facebookPost, animated: false, completion: {
//Optional completion statement
})
}`

I solved my problem (I put my comment here cause I don't have enough place to explain). So I was trying to share a link on Facebook.
First I was wrong with the params, I was writing
var params=["http://www.google.com":"link"]
and the correct way is
var params=["link":"http://www.google.com"]
(Because of the way it's written in Obj-C on dev.facebook.com I got confused).
Secondly, if I follow the logic of the original github project, I call my function this way:
FB.performPublishAction(self.shareLinkOnFB)
where performPublishAction (in the Facebook class) ask for the new publish permissions:
func performPublishAction(action:() -> Void){
fbSession?.requestNewPublishPermissions(fbSettings.publishPermissions, defaultAudience: fbSettings.publishAudience, completionHandler: {(session:FBSession!, error:NSError!)->Void in
if error==nil {
println("perform publish action / no error")
action()
}else{
println("perform publish action / error: \(error.localizedDescription)")
println(error)
}
})
and shareLinkOnFb function calls FB.requestToShareLink():
func shareLinkOnFB(){
FB.requestToShareLink()
}
func requestToShareLink(){
var params=["link":"\(self.link)", "message":"\(self.message)"]
FBRequestConnection.startWithGraphPath("/me/feed", parameters:params, HTTPMethod: "POST", completionHandler: { (connection:FBRequestConnection!, result:AnyObject!, error:NSError!) in
if error != nil {
println("Request to share link / An error occurred: \(error.localizedDescription)")
println(error)
//handle error
}else{
println("link successfully posted: \(result)")
}
})
}
I'm not sure you have the same problem but I hope it can help you.

check this guy's answer https://stackoverflow.com/a/58481784/12250012
also now facebook is not allowing programatically.

You have to request in your facebook app the "publish_actions" permissions.

I have had the same issue. I have solve it by writing such code. If you have an active Facebook session this code is perfectly work.
func performPublishAction(callBack:() -> Void){
self.fbSession?.requestNewPublishPermissions(["publish_actions"], defaultAudience: FBSessionDefaultAudience.Friends, completionHandler: {(session:FBSession!, error:NSError!)->Void in
if error == nil {
callBack()
}else{
println(error)
}
})
}
func shareImage (imageName:UIImage){
var requestConneciton:FBRequestConnection=FBRequestConnection()
requestConneciton.errorBehavior=FBRequestConnectionErrorBehavior.None
requestConneciton.addRequest(FBRequest(forUploadPhoto:imageName)) { (connection:FBRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
println("\(error)");
println("\(result)");
}
requestConneciton.start()
}
use of these methods will be as:
facebookHelper.performPublishAction { () -> Void in
facebookHelper.shareImage(self.itemImage)
//NkCommonClass.displayToast("Posted successfully on your wall.")
}

Related

Show AdMob gdpr consent form in Xcode

I have developed my game in Godot game engine and I'm now trying to implement googles gdpr consent form into my Xcode project.
I am using Xcode 11.5
The code below is my current code which does not result in the consent form popping up.
It might be something with the class I made but I'm not sure how that works.
import UIKit
import PersonalizedAdConsent
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
PACConsentInformation.sharedInstance.requestConsentInfoUpdate(
forPublisherIdentifiers: ["I have the id just not posted here"])
{(_ error: Error?) -> Void in
if let error = error {
// Consent info update failed.
} else {
// Consent info update succeeded. The shared PACConsentInformation
// instance has been updated.
}
}
guard let privacyUrl = URL(string: "My_privacy_policy(not_actual)"),
let form = PACConsentForm(applicationPrivacyPolicyURL: privacyUrl) else {
print("incorrect privacy URL.")
return
}
form.shouldOfferPersonalizedAds = true
form.shouldOfferNonPersonalizedAds = true
form.shouldOfferAdFree = false
form.load {(_ error: Error?) -> Void in
print("Load complete.")
if let error = error {
// Handle error.
print("Error loading form: \(error.localizedDescription)")
} else {
// Load successful.
}
}
//Finally present the consent form
form.present(from: self) { (error, userPrefersAdFree) in
if let error = error {
// Handle error.
} else if userPrefersAdFree {
// User prefers to use a paid version of the app.
} else {
// Check the user's consent choice.
let status =
PACConsentInformation.sharedInstance.consentStatus
}
}
}
}
Any help is appreciated :)

GKTurnBasedEventListener could not be set to delegate of my ViewController?

In objC the syntax written by Rawendrich for GKTurnBasedEventListener, which was GKTurnBasedEventHandler there at that time, now changed by Apple is as below.
if (!gameCenterAvailable) return;
void (^setGKEventHandlerDelegate)(NSError *) = ^ (NSError *error)
{
GKTurnBasedEventHandler *ev =
[GKTurnBasedEventHandler sharedTurnBasedEventHandler];
ev.delegate = self;
};
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO) {
[[GKLocalPlayer localPlayer]
authenticateWithCompletionHandler:
setGKEventHandlerDelegate];
} else {
NSLog(#"Already authenticated!");
setGKEventHandlerDelegate(nil);
}
Now after converting this to swift, and with the composition of writing down GKTurnBasedEventListener instead of GKTurnBasedEventHandler, this comes the following way.
// Converted with Swiftify v1.0.6381 - https://objectivec2swift.com/
if !gameCenterAvailable {
return
}
var setGKEventHandlerDelegate: ((_: Error) -> Void)? = {(_ error: Error?) -> Void in
var ev = GKTurnBasedEventHandler.shared()
ev.delegate = self
}
print("Authenticating local user...")
if GKLocalPlayer.localPlayer().authenticated == false {
GKLocalPlayer.localPlayer().authenticate(withCompletionHandler: setGKEventHandlerDelegate)
}
else {
print("Already authenticated!")
setGKEventHandlerDelegate(nil)
}
Unfortunately this is not the right syntax to set delegate of GKTurnBasedEventListener for my ViewController.
Please if anyone of you could solve this for me, because without this I'm not able to read through event listener's default functions.
Cheers!
FYI, if you want a working example of how to use GKLocalPlayerListener during a turn-based GameKit match, you are welcome to take a look at this example project for a turn based game. I hope it helps to see all the pieces in context.
Finally after just about harsh 10 hours, I figured out this problem from Here. Although this syntax is in objC, but there's no problem of converting it to swift from Swiftify.
Although a bit later than the real time, but I'm now able to understand that setting delegate of GKTunBasedEventListener is not like the one we do for UITableViewControllerDelegate.
Here, one must have to first authenticate local player, then after you have to register local player's listener to the ViewController's delegate GKLocalPlayerListener.
One other thing I found on Apple's Documentation:
Do not implement GKChallengeListener, GKInviteEventListener, GKSavedGameListener, and GKTurnBasedEventListener directly; implement GKLocalPlayerListener instead. You can listen for and handle multiple events using GKLocalPlayerListener.
So then on I've implemented in the following way.
import GameKit
class ViewController: UIViewController, GKTurnBasedMatchmakerViewControllerDelegate,
GKLocalPlayerListener {
.....
func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) {
print("#1")
print(player)
print("#2")
print(match)
print("#3")
print(didBecomeActive)
if match.status == GKTurnBasedMatchStatus.open
{
if GKLocalPlayer.localPlayer() == match.currentParticipant
{
if didBecomeActive
{
// Active now
}
else
{
// Active already
}
}
else
{
// It's someone's turn
if match.matchData != myMatch?.matchData
{
// Match Data being Updated by Someone
print(player.alias ?? "No Name:")
}
}
}
thirdTopLabel.text = match.matchID! + "\n" + didBecomeActive.description
}
....
Now in ViewDidLoad() function put the following code.
// In the ViewDidLoad function
if(!GKLocalPlayer.localPlayer().isAuthenticated)
{
authenticatePlayer { (auth) in
weak var weakSelf = self
weak var weakPlayer = GKLocalPlayer.localPlayer()
if(auth){
weakPlayer?.register(weakSelf!)
self.suthentication = true;
}
else{
print("failed in authentication")
self.suthentication = false;
}
}
}
else
{
// Already Authenticated
GKLocalPlayer.localPlayer().register(self)
localPlayer = GKLocalPlayer.localPlayer()
}
And finally your Authentication function should be like this.
// authenticate local player :: Just Authentication
func authenticatePlayer(completionHandler: #escaping (_ resultedPlaces: Bool) -> Void) {
localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler =
{ (viewController , error ) -> Void in
if viewController != nil
{
self.present(viewController!, animated:true, completion: nil)
}
else
{
if self.localPlayer.isAuthenticated
{
completionHandler(true);
}
else
{
completionHandler(false);
print("not able to authenticate fail")
self.gameCenterEnabled = false
if (error != nil)
{
print("\(error.debugDescription)")
}
else
{
print( "error is nil")
}
}
}
}
}
NOTE: GKLocalPlayerListener won't work on simulator.

Can't prevent user from cancelling Touch ID

When the Touch ID alert is displayed, there is also a "Cancel" button. I would prefer to NOT allow the user to cancel because they are prohibited from continuing any further. 1. Is there a way to remove the "Cancel" button. 2. If the "Cancel" button is required, how can I force the user to re-authenticate with a fingerprint? If authenticate() is called a second time, the Touch ID API just lets them in. There is no alternative passcode and I'd hate to have to code up yet another view controller for it.
func authenticate() {
let myContext:LAContext = LAContext()
let authError:NSErrorPointer = nil
if (myContext.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: authError)) {
myContext.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Press fingerprint", reply: { (success:Bool, error:NSError?) -> Void in
if success == true {
log.debug("SUCCESSFUL AUTHENTICATION")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.performSegueWithIdentifier("showUI", sender: self)
})
}
else {
log.debug("FAILED AUTHENTICATION")
self.authenticate()
}
})
}
}
You need to dispatch your failure call to self.authenticate on the main queue;
func authenticate() {
let myContext:LAContext = LAContext()
let authError:NSErrorPointer = nil
if (myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: authError)) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Press fingerprint", reply: { (success:Bool, error:NSError?) -> Void in
if success {
log.debug("SUCCESSFUL AUTHENTICATION")
DispatchQueue.main.async {
self.performSegueWithIdentifier("showUI", sender: self)
}
}
else {
log.debug("FAILED AUTHENTICATION")
DispatchQueue.main.async {
self.authenticate()
}
}
})
}
}

Custom Twitter login with Swift and Parse

I've been stuck on how to implement a custom Twitter login with Parse for my Swift app for a while now. Most resources I've seen for Twitter login with iOS are for Objective-C, and I've tried changing the suggested solutions into Swift, but no luck so far. This is my current code:
#IBAction func twitterLogIn(sender: AnyObject) {
PFTwitterUtils.logInWithBlock( { (user: PFUser?, error: NSError?) -> Void in
// apparently both user and currentUser are nil...
if PFTwitterUtils.isLinkedWithUser(PFUser.currentUser()) {
var info: NSURL! = NSURL(string: "https://api.twitter.com/1.1/account/settings.json")
var request: NSMutableURLRequest = NSMutableURLRequest(URL: info)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response: NSURLResponse!, data: NSData!, connectionError: NSError!) -> Void in
if let d = data {
var dict: NSDictionary = NSJSONSerialization.JSONObjectWithData(d, options: NSJSONReadingOptions.MutableLeaves, error: nil) as! NSDictionary
// do stuff with user's profile info
} else {
println("Apparently no Twitter response.")
}
})
} else {
println("Apparently failed login.")
}
})
}
This prints out Apparently failed login. I've also tried this:
PFTwitterUtils.logInWithBlock( { (user: PFUser?, error: NSError?) -> Void in
if let user = user {
if user.isNew {
println("User signed up and logged in with Twitter.")
// put information in user object
user.save()
} else {
println("User logged in with Twitter.")
}
} else {
println("User was nil.")
}
})
It printed out User was nil. And I also tried this:
if let twitter = PFTwitterUtils.twitter() {
if twitter.userId != nil && twitter.screenName != nil && twitter.authToken != nil && twitter.authTokenSecret != nil {
PFTwitterUtils.logInWithTwitterId(twitter.userId!, screenName: twitter.screenName!, authToken: twitter.authToken!, authTokenSecret: twitter.authTokenSecret!, block: { (user: PFUser?, error: NSError?) -> Void in
if let user = user {
if user.isNew {
println("User signed up and logged in with Twitter.")
// put info in user object
user.save()
} else {
println("User logged in with Twitter.")
}
} else {
println("User was nil. Something went wrong. Did the user cancel the login?")
}
})
} else {
println("Couldn't access a Twitter account on this device.")
}
} else {
println("Couldn't find a Twitter account on this device.")
}
Which printed Couldn't access a Twitter account on this device. The only other option I know of right now is to use the built-in login view controller, but I'd rather use a custom one. How can I implement a custom Twitter login function?
Edit: I tried another method that didn't work yet again:
PFTwitterUtils.logInWithBlock( { (user: PFUser?, error: NSError?) -> Void in
// apparently both user and currentUser are nil...
println(2)
if !(PFTwitterUtils.isLinkedWithUser(user)) {
println(3)
PFTwitterUtils.linkUser(user!, block: { (succeeded: Bool, error: NSError?) -> Void in
println(4)
if succeeded {
println("User linked with Twitter.")
}
})
}
println(5)
if let u = user {
println(6)
if u.isNew {
println("User signed up and logged in with Twitter.")
// put info in user object
var info: NSURL! = NSURL(string: "https://api.twitter.com/1.1/account/settings.json")
println(7)
var request: NSMutableURLRequest = NSMutableURLRequest(URL: info)
println(8)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response: NSURLResponse!, data: NSData!, connectionError: NSError!) -> Void in
println(9)
if let d = data {
println(10)
var dict: NSDictionary = NSJSONSerialization.JSONObjectWithData(d, options: NSJSONReadingOptions.MutableLeaves, error: nil) as! NSDictionary
// do stuff with this data
} else {
println("Apparently no Twitter response.")
}
})
} else {
println("User logged in with Twitter.")
}
} else {
println("User was nil.")
}
})
This prints up to 3 and then complains of a nil optional value, most likely user.
Edit: I've been testing Fabric as a possible solution and although it provides a good Twitter login method, I can't figure out how to use it with Parse, specifically, how to create and handle a corresponding user object as I can't access the user's password. I'd much prefer to use PFTwitterUtils for the best integration with Parse, but I'm running out of options for that. Can anyone help?

Multiple Log ins (Facebook and Spotify) with AppDelegate

I am making an app that uses both Spotify login and Facebook login. Both of their tutorials say to modify the application() in AppDelegate.swift file. My issue is that both log-ins calculate the return value (boolean) of the function separately, and I don't know how to combine them. My question is how to have both log ins work and use just the one return value from application(). For clarity, the desired for each log in is shown below.
The spotify SDK wants:
func application(...) -> Bool() {
let auth = SPTAuth.defaultInstance()
let authCallback = { (error : NSError?, session : SPTSession?) -> () in
if (error != nil) {
NSLog("*** Auth Error \(error)")
return
}
auth.session = session
NSNotificationCenter.defaultCenter().postNotificationName("sessionUpdated", object: self)
}
if auth.canHandleURL(url) {
auth.handleAuthCallbackWithTriggeredAuthURL(url, callback: authCallback)
return true
}
return false
}
And the facebook SDK wants:
  
func application(...) -> Bool {
var wasHandled = FBAppCall.handleOpenURL(url, sourceApplication:sourceApplication)
// any app-specific handling code here
return wasHandled
}
This may not be the best solution, but you can perhaps check if the URL was handled with one type and then try with the next type if it was not handled.
If both types cannot handle it then return false.
For example:
func application(...) -> Bool {
var wasHandled = FBAppCall.handleOpenURL(url, sourceApplication:sourceApplication)
// any app-specific handling code here
if (!wasHandled) {
// spotify code here...
if auth.canHandleURL(url) {
auth.handleAuthCallbackWithTriggeredAuthURL(url, callback: authCallback)
return true;
}
return false;
}
return wasHandled;
}
For Spotify login
YOUR_URL_SCHEME_IDENTIFIER_FOR_SPOTIFY it it the same string mentioned in your ios project url schemes and also in spotify MyApplications Redirect URIs.
if ([[url absoluteString] hasPrefix:#"YOUR_URL_SCHEME_IDENTIFIER_FOR_SPOTIFY"]) {
let auth = SPTAuth.defaultInstance()
let authCallback = { (error : NSError?, session : SPTSession?) -> () in
if (error != nil) {
NSLog("*** Auth Error \(error)")
return
}
auth.session = session
NSNotificationCenter.defaultCenter().postNotificationName("sessionUpdated", object: self)
}
if auth.canHandleURL(url) {
auth.handleAuthCallbackWithTriggeredAuthURL(url, callback: authCallback)
return true
}
return false
}

Resources