How to post to user's facebook feed - ios

I need to post to user's facebook feed.
Based on several other SO questions I came up with the following posting request:
let request = GraphRequest(graphPath: "me/feed", parameters: ["message" : "Hello world"], accessToken: accessToken, httpMethod: .POST, apiVersion: GraphAPIVersion.defaultVersion)
request.start({ (response, requestResult) in
switch requestResult {
case .failed(let error):
print("error in graph request:", error)
case .success(let graphResponse):
if let responseDictionary = graphResponse.dictionaryValue {
print(responseDictionary)
}
}
})
This fails due to
error = {
code = 200;
"fbtrace_id" = GMp2cebddNb;
message = "(#200) Requires either publish_actions permission, or manage_pages and publish_pages as an admin with sufficient administrative permission";
type = OAuthException;
};
Based on the message, the problem seemed to be an easy to solve - all I need is to get either publish_actions, or manage_pages and publish_pages permissions. Based on this SO question, this seemed easy and I ended up in wrapping the code for posting with this:
let loginManager = LoginManager()
loginManager.logIn([PublishPermission.custom("publish_actions")], viewController: self) { (result) in
print(">> \(AccessToken.current?.grantedPermissions)")
switch result {
case .cancelled:
print(">>>> Cancelled")
case .failed(let error):
print(">>>> Error: \(error)" )
case .success(grantedPermissions: _, declinedPermissions: _, token: let accessToken):
print(">>>> Logged in!")
let request = GraphRequest(graphPath: "me/feed", parameters: ["message" : post], accessToken: accessToken, httpMethod: .POST, apiVersion: GraphAPIVersion.defaultVersion)
request.start({ (response, requestResult) in
switch requestResult {
case .failed(let error):
print("error in graph request:", error)
break
case .success(let graphResponse):
if let responseDictionary = graphResponse.dictionaryValue {
print(responseDictionary)
}
}
})
}
}
Now the "funny" part is, that then the facebook SDK shows a page telling me that I previously logged in to my app using Facebook and asks me if I would like to continue. When I press Continue, the SafariViewController dismisses and the .cancelled branch gets executed. What is going on here? I haven't cancelled, nor have I been asked to grant permissions to publish anything on my feed.
P.S.: I tried logging out first (loginManager.logOut() and/or AccessToken.current = nil), in that case the .success branch executes but again with the same error "(#200) Requires either publish_actions permission, or manage_pages and publish_pages as an admin with sufficient administrative permission".
The AccessToken.current.grantedPermissions in that case contains:
Set([FacebookCore.Permission(name: "user_friends"),
FacebookCore.Permission(name: "publish_pages"),
FacebookCore.Permission(name: "user_location"),
FacebookCore.Permission(name: "email"),
FacebookCore.Permission(name: "user_likes"),
FacebookCore.Permission(name: "pages_show_list"),
FacebookCore.Permission(name: "manage_pages"),
FacebookCore.Permission(name: "user_photos"),
FacebookCore.Permission(name: "public_profile"),
FacebookCore.Permission(name: "user_posts"),
FacebookCore.Permission(name: "user_birthday")])
So no publish_actions permission! Why does the login go through successfully while not granting me the permission that I ask for? Moreover, I obviously have "manage_pages" and "publish_pages", so why is that not enough?

https://developers.facebook.com/docs/graph-api/changelog/breaking-changes#4-24-2018
The publish_actions permission has been removed.
Since they do not mention any alternative, there is no way to post to the user feed anymore.

Related

Need to write test case for MongoDB Realm Swift SDK

I am working on MongoDB Realm Swift SDk and I need to write the test case for the login function which is as below:
let params: Document = ["username": "bob"]
app.login(credentials: Credentials.function(payload: params)) { (result) in
switch result {
case .failure(let error):
print("Login failed: \(error.localizedDescription)")
case .success(let user):
print("Successfully logged in as user \(user)")
// Now logged in, do something with user
// Remember to dispatch to main if you are doing anything on the UI thread
}
}
May I know how to write test case for this function with Mock Data so that I can verify that Login is working fine?

IAP Invalid product ids while App Store reviews, but it's ok in sandbox and testflight

I'm trying to integrate In-App-Purchases in my app. I have used a thrd party library, SwiftyStoreKit, as IAP helper.
It works fine when I use my device to test in-app purchases in sandbox, Whether run from xcode or testflight.
But it shows 'SKError.paymentInvalid' while App Store reviews it. It's really frustrating, I don't know how to fix it. I've tried reaching out to the review team but they only linked me with articles on IAP and testing my app.
The code for the user to click to purchase is below. When the App tore is reviewing the purchase, a .paymentInvalid error will be thrown, Any ideas appreciated...
func purchaseProduct(item: RCCoinItem) -> Promise<RCInAppPurSucceed> {
return Promise { (seal) in
SVHud.show(status: "Purchasing...", maskType: .clear)
RCDataAnalysis.track(eventName: "ClickBuyCoin", params: ["p_id" : item.type, "dollar" : "\(item.cost)"])
SwiftyStoreKit.purchaseProduct(item.type, quantity: 1, atomically: false) { result in
switch result {
case .success(let product):
// fetch content from your server, then:
if let receiptData = SwiftyStoreKit.localReceiptData {
let receiptString = receiptData.base64EncodedString(options: [])
NW.request(path: APPLE_SUB, method: .post, params: ["receipt-data" : receiptString, "product_id" : item.type]).done { (response) in
SVHud.dismiss()
if let dict = response as? [String : Any] {
print("purchse == \(dict)")
if product.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(product.transaction)
}
if let inapp = RCInAppPurSucceed.deserialize(from: dict) {
let realm = try! Realm()
try! realm.write {
Login.shared.user.coins = inapp.coins
Login.shared.user.firstRecharged = true
}
inapp.coins = item.coins
seal.fulfill(inapp)
}
RCDataAnalysis.track(eventName: "BuyCoinSuccess", params: ["p_id" : item.type, "dollar" : "\(item.cost)"])
print("Purchase Succeed: \(product.productId)")
} else {
RCDataAnalysis.track(eventName: "BuyCoinFail", params: ["p_id" : item.type, "reason" : "api wrong"])
seal.reject(NWError(msg: "Something is wrong with Apple_Sub Api.", code: 201))
SVHud.showError(status: "Api succeed, but something wrong happened, please try agin.")
}
}.catch{ (error) in
seal.reject(NSError(domain: NSCocoaErrorDomain, code: 1, userInfo: [NSLocalizedFailureReasonErrorKey : "Something is wrong with Apple_Sub Api."]))
SVHud.showError(status: "Something is wrong with Apple_Sub Api, Please try agin.")
RCDataAnalysis.track(eventName: "BuyCoinFail", params: ["p_id" : item.type, "reason" : "api wrong response"])
}
}
case .error(let error):
if !self.retrieved {
self.retrieveProductInfo()
}
switch error.code {
case .unknown:
SVHud.showInfo(status: "Unknown error. Please try again")
case .clientInvalid:
SVHud.showInfo(status: "Not allowed to make the payment")
case .paymentCancelled:
SVHud.showInfo(status: "The payment is canceled.")
case .paymentInvalid:
SVHud.showInfo(status: "The purchase identifier was invalid.")
case .paymentNotAllowed:
SVHud.showInfo(status: "The device is not allowed to make the payment")
case .storeProductNotAvailable:
SVHud.showInfo(status: "The product is not available in the current storefront")
case .cloudServicePermissionDenied:
SVHud.showInfo(status: "Access to cloud service information is not allowed")
case .cloudServiceNetworkConnectionFailed:
SVHud.showInfo(status: "Could not connect to the network")
case .cloudServiceRevoked:
SVHud.showInfo(status: "User has revoked permission to use this cloud service")
default:
SVHud.showError(status: (error as NSError).localizedDescription)
print((error as NSError).localizedDescription)
}
RCDataAnalysis.track(eventName: "BuyCoinFail", params: ["p_id" : item.type, "reason" : "apple \(error.code.rawValue)"])
seal.reject(error)
}
}
}
}

Error: Must provide exactly one of these parameters: [:customer, :issuing_card]

I am trying to integrate the Stripe API inside of my application using firebase cloud functions as my backend. I get the error listed above when I make a call to create an ephemeral key.
This is what my client side set up look like :
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("/ephemeral_keys")
Alamofire.request(url, method: .post, parameters: [
"api_version": apiVersion,"customer": "cus_somethingsomething"
])
.validate(statusCode: 200..<400)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
completion(nil, error)
}
}
}
and here is my server side index.js file look like:
exports.StripeEphemeralKeys = functions.https.onRequest((req, res) => {
const stripe_version = req.body.api_version;
const customer = req.body.customer_id;
stripe.ephemeralKeys.create(
{customer: customer},
{apiVersion: stripe_version}
).then((key) => {
res.status(200).send(key)
return admin.database().ref(`/strcustomers/tempkey`).set(key);
}).catch((err) => {
console.log('Inside error, fetching key failed', err)
});
});
I am providing a customer parameter already so I don't understand why I'm getting this error. There may be something I'm missing and could definitely use some help had anybody ran into similar issue before.
Here is the link of the tutorial I followed : https://www.iosapptemplates.com/blog/ios-development/stripe-firebase-swift

Uber SDK for iOS - get real time ride request status made via deep link

I'm having some trouble finding a way to do this. I got an app that uses Uber SDK for iOS. I was able to add the "RideRequestButton" with "Ride there with Uber" text to do a deeplink to the Uber app. Also, I'm done requesting the token using SSO and fetch the token with Request scope. Now the problem is... I'm not sure how to fetch the status of the current ride request made via the deeplink. Is this possible?
Thanks!
I tried following an algorithm mentioned in one of the thread which is to use the RidesClient to fetch the current ride then get the current ride detail after obtaining the ride requestID. Here's a glimpse of my code:
func refreshRideRequestStatus () {
var requestID: String = ""
self.ridesClient.fetchCurrentRide({
ride, response in
if (ride != nil) {
print ("[refreshRideRequestStatus] ride:", ride ?? "no active ride")
requestID = (ride?.requestID)!
}
else {
print ("[refreshRideRequestStatus] ride: no active ride")
print ("[refreshRideRequestStatus] response: ", response.response ?? "no response")
}
})
if (requestID.isEmpty) {
print ("[refreshRideRequestStatus] no active request")
}
else {
print ("[refreshRideRequestStatus] requestID: ", requestID)
self.ridesClient.fetchRideDetails(requestID, completion: {
ride, response in
self.isTorchUberFlashable = false
if (response.statusCode == 200) {
let status:Int = (ride?.status)!.rawValue
print("refreshRideRequestStatus] status:", status)
switch (status) {
case 1:
print("[refreshRideRequestStatus] accepted")
case 2:
print("[refreshRideRequestStatus] arriving")
case 3:
print("[refreshRideRequestStatus] completed")
case 4:
print("[refreshRideRequestStatus] driverCanceled")
case 5:
print("[refreshRideRequestStatus] inProgress")
case 6:
print("[refreshRideRequestStatus] noDriversAvailable")
case 7:
print("[refreshRideRequestStatus] processing")
case 8:
print("[refreshRideRequestStatus] ridersCanceled")
case 9:
print("[refreshRideRequestStatus] unknown")
default:break
}
}
else {
print("[refreshRideRequestStatus] error: ", response.response ?? "no data request found")
}
})
}
}
The function above is going to be called every time my app is set to foreground or became active.
Sorry but another question would be.
How will I know if this will work and is there a way to test this by requesting a real ride and complete a whole transaction?
With the request scope, you are only able to look at the status of rides that are initiated using your auth token. So if the user is deeplinking into the app and then requesting a ride from there, you will not have access to that information.
In order to get this information, you would be required to get authorized for the all_trips scope.

Facebook IOS SDK : how to do silent sign in?

I have an app doing a facebook login, which works well, but everytime I reopen it I have to connect again to facebook to do the sign-in. I'm also using google sign-in sdk where I can call the function gSignIn.signInSilently(), is there something similar for facebook? I found this for the javascript sdk but I don't know if it's possible for the ios SDK and how to use it in swift...
The Facebook SDK automatically maintains the login state, which can be confirmed by checking for the access token.
You can check for the access using the following method:
FBSDKAccessToken.currentAccessToken()
You can check for the presence of the token which would mean that the user is logged in.
Check the docs for more details.
i've tried this
if(![FBSDKAccessToken currentAccessToken])
{
FBSDKLoginManager *manager = [[FBSDKLoginManager alloc]init];
[manager logInWithReadPermissions:#[#"public_profile", #"email",#"user_photos"] handler:^(FBSDKLoginManagerLoginResult *result,NSError *error)
{
if(error == nil)
{
NSLog(#"Facebook - successfully login %#",result.token);
//login successfully
//do your stuff here
}
}];
}
else
{
//already login
//Do Your stuff here
}
I am saving the access token string and manually setting it on consecutive launches to bypass the re-login flow.
func loginWithFacebook() {
//Check for previous Access Token
if let accessToken = AccessToken.current {
//AccessToken was obtained during same session
getAccountDetails(withAccessToken: accessToken)
}
else if let strAuthenticationToken = UserDefaults.standard.string(forKey: "AccessToken_Facebook") {
//A previous access token string was saved so create the required AccessToken object
let accessToken = AccessToken(authenticationToken: strAuthenticationToken)
//Skip Login and directly proceed to get facebook profile data with an AccessToken
getAccountDetails(withAccessToken: accessToken)
}
else {
//Access Token was not available so do the normal login flow to obtain the Access Token
LoginManager().logIn(readPermissions: [.publicProfile, .email], viewController: nil) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions,
let declinedPermissions,
let accessToken):
//Save Access Token string for silent login purpose later
let strAuthenticationToken = accessToken.authenticationToken
UserDefaults.standard.set(strAuthenticationToken,
forKey: "AccessToken_Facebook")
//Proceed to get facebook profile data
self.getAccountDetails(withAccessToken: accessToken)
}
}
}
}
func getAccountDetails(withAccessToken accessToken: AccessToken) {
let graphRequest: GraphRequest = GraphRequest(graphPath : "me",
parameters : ["fields" : "id, name, email"],
accessToken : accessToken,
httpMethod : GraphRequestHTTPMethod.GET,
apiVersion : GraphAPIVersion.defaultVersion)
graphRequest.start { (response, result) in
switch result {
case .success(let resultResponse):
print(resultResponse)
case .failed(let error):
print(error)
}
}
}
NOTE: For the ease of this example, the Facebook Access Token string is being saved to UserDefaults but ideally it should be saved to the Keychain.
(Swift 4 / Facebook SDK 4.30)

Resources