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
}
Related
I'm using the Amplify framework for my auth on the combine framework.
I want to check if the user is currently logged in. This is done by the following function
Amplify.Auth.fetchAuthSession()
This returns:
AnyPublisher<AuthSession,AuthError>
I've put it in a function, so I can call it from my AuthenticationViewModel, which deals with the business login for auth.
enum AuthenticationsFunctions {
static func fetchCurrentAuthSession() -> AnyPublisher<AuthSession, AuthError> {
Amplify.Auth.fetchAuthSession().resultPublisher
}
}
For my view model, it has states and events. In the code below, I want to call the authentication function and return the appropriate events. Such as .onAlreadyLoggedIn. Because the auth function returns a different publisher, I can't figure out how to return the appropriate event to AnyPublisher<Event, Never>
AuthSession has a function .isSignedIn which is a boolean.
static func fetchCurrentLogin() -> Feedback<State, Event> {
Feedback { (state: State) -> AnyPublisher<Event, Never> in
guard case .loading = state else { return Empty().eraseToAnyPublisher() } //Checks if the state is loading (When the app first opens)
AuthenticationsFunctions.fetchCurrentAuthSession().allSatisfy { (AuthSession) -> Bool in
if (AuthSession.isSignedIn) {
return true
}
else {
return false
}
}
}
}
Amazon on their docs provide this:
func fetchCurrentAuthSession() -> AnyCancellable {
Amplify.Auth.fetchAuthSession().resultPublisher
.eraseToAnyPublisher()
.sink {
if case let .failure(authError) = $0 {
print("Fetch session failed with error \(authError)")
}
}
receiveValue: { session in
print("Is user signed in - \(session.isSignedIn)")
}
}
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 :)
I want user to login once and not have to reenter their login info everytime they open app unless they logout in the last session.
Login screen is currently displayed everytime the app is open. This is my rootview
struct AppRootView: View {
var body: some View {
AnyView {
// check if user has already logged in here and then route them accordingly
if auth.token != nil {
homeMainView()
} else {
LoginController()
}
}
}
}
currently this is what I use to login users
#objc func signUp() {
setLoading(true);
app.usernamePasswordProviderClient().registerEmail(username!, password: password!, completion: {[weak self](error) in
// Completion handlers are not necessarily called on the UI thread.
// This call to DispatchQueue.main.sync ensures that any changes to the UI,
// namely disabling the loading indicator and navigating to the next page,
// are handled on the UI thread:
DispatchQueue.main.sync {
self!.setLoading(false);
guard error == nil else {
print("Signup failed: \(error!)")
self!.errorLabel.text = "Signup failed: \(error!.localizedDescription)"
return
}
print("Signup successful!")
// Registering just registers. Now we need to sign in, but we can reuse the existing username and password.
self!.errorLabel.text = "Signup successful! Signing in..."
self!.signIn()
}
})
}
#objc func signIn() {
print("Log in as user: \(username!)");
setLoading(true);
app.login(withCredential: AppCredentials(username: username!, password: password!)) { [weak self](maybeUser, error) in
DispatchQueue.main.sync {
self!.setLoading(false);
guard error == nil else {
// Auth error: user already exists? Try logging in as that user.
print("Login failed: \(error!)");
self!.errorLabel.text = "Login failed: \(error!.localizedDescription)"
return
}
guard let user = maybeUser else {
fatalError("Invalid user object?")
}
print("Login succeeded!");
//
let hostingController = UIHostingController(rootView: ContentView())
self?.navigationController?.pushViewController(hostingController, animated: true)
}
how could I implement one time login so that users do have to login each time they open the app?
A correctly configured and initialized RealmApp class will persist the session information for you between app restarts, you can check for an existing session using the .currentUser() method from this class. So in your case something like:
if app.currentUser() != nil {
homeMainView()
} else {
LoginController()
}
While using Realm to persist login is a good idea, but I would highly
advice against using it for managing user authentication credentials such
as passwords. A better approach if you want to save sensitive information is
using KeyChain just like what Apple and password manager apps do. With a light
weight keyChain wrapper library such as SwiftKeychainWrapper You can easily
save your login credentials in the most secure way.
Here is a sample using a keyChain wrapper linked above.
With simple modification you can use this helper class to manage your sign in credentials anywhere in your app.
import SwiftKeychainWrapper
class KeyChainService {
// Make a singleton
static let shared = KeyChainService()
// Strings which will be used to map data in keychain
private let passwordKey = "passwordKey"
private let emailKey = "emailKey"
private let signInTokenKey = "signInTokenKey"
// Saving sign in info to keyChain
func saveUserSignInInformation(
email: String,
password: String,
token: String
onError: #escaping() -> Void,
onSuccess: #escaping() -> Void
) {
DispatchQueue.global(qos: .default).async {
let passwordIsSaved: Bool = KeychainWrapper.standard.set(password, forKey: self.passwordKey)
let emailIsSaved: Bool = KeychainWrapper.standard.set(email, forKey: self.emailKey)
let tokenIsSaved: Bool = KeychainWrapper.standard.set(token, forKey: self.signInTokenKey)
DispatchQueue.main.async {
// Verify that everything is saved as expected.
if passwordIsSaved && emailIsSaved && tokenIsSaved {
onSuccess()
}else {
onError()
}
}
}
}
// Retrieve signIn information for auto login
func retrieveSignInInfo(onError: #escaping() -> Void, onSuccess: #escaping(UserModel) -> Void) {
DispatchQueue.main.async {
let retrievedPassword: String? = KeychainWrapper.standard.string(forKey: self.passwordKey)
let retrievedEmail: String? = KeychainWrapper.standard.string(forKey: self.emailKey)
let retrievedToken: String? = KeychainWrapper.standard.string(forKey: self.signInTokenKey)
if let password = retrievedPassword,
let email = retrievedEmail,
let token = retrievedToken {
// Assuming that you have a custom user model named "UserModel"
let user = UserModel(email: email, password: password,token: token)
// Here is your user info which you can use to verify with server if needed and auto login user.
onSuccess(user)
}else {
onError()
}
}
}
}
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.
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.")
}