Facebook SDK App events won't record events in events manager for iOS 14+ - swift

Testing Environment
Facebook SDK : v.11.0.0
Device Model: iPhone X
iOS Version: 14.1
+Expected Result
App events both Standard Events and Custom Events should be record for iOS 14. We've enable AdvertiserTrackingEnabled by Settings.setAdvertiserTrackingEnabled(true). And we've check debug log request saw SDK sent events success.
+ Actual behaviour
Even though we do set Settings.setAdvertiserTrackingEnabled(true) and debug log request success sent event. But it doesn't seem to be recording on dashboard.

I have developed these Facebook SDK for my one of the app for get the app events. In iOS under 14,15,16, everything works well.
These Facebook events appear once your application runs on real device and in that device have already installed facebook app and its login then the events will be appear in event manager otherwise not shows in event manager.
app added into Facebook developer page and get the app id and client token.
added info plist these below lines replace with your App ID and client token
<key>NSUserTrackingUsageDescription</key>
<string>${PRODUCT_NAME} please allow to tracking used to provide you a better and personalized ad experience.</string>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb674454383521423</string>
</array>
</dict>
<key>FacebookAppID</key>
<string>674454383521423</string>
<key>FacebookClientToken</key>
<string>3067e472e89ff8d7c39a18f38d931301</string>
<key>FacebookDisplayName</key>
<string>AppName</string>
Later in AppDelegate just import these two frameworks and added these code in to didFinshlunch method.
import FBSDKCoreKit
import AppTrackingTransparency
ApplicationDelegate.shared.initializeSDK()
Settings.shared.isAdvertiserTrackingEnabled = true
Settings.shared.enableLoggingBehavior(.appEvents)
Settings.shared.isAutoLogAppEventsEnabled = true
Settings.shared.isAdvertiserIDCollectionEnabled = true
Settings.shared.enableLoggingBehavior(.developerErrors)
Settings.shared.enableLoggingBehavior(.cacheErrors)
Settings.shared.enableLoggingBehavior(.uiControlErrors)
NEXT STEP WE NEED ADD THESE FUNCTION
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
if (url.scheme?.hasPrefix("fb"))! {
ApplicationDelegate.shared.application(
application,
open: url,
sourceApplication: sourceApplication,
annotation: annotation
)
}
}
Add these transport security permisssion alert in app delegate and call these function on didbecome active method or else didfinsh lunch methos.
func getTrackingPermission() {
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
// Tracking authorization dialog was shown
// and we are authorized
Settings.shared.isAutoLogAppEventsEnabled = true
Settings.shared.isAdvertiserTrackingEnabled = true
print("Authorized")
// Now that we are authorized we can get the IDFA
print(ASIdentifierManager.shared().advertisingIdentifier)
case .denied:
Settings.shared.isAutoLogAppEventsEnabled = false
Settings.shared.isAdvertiserTrackingEnabled = false
// Tracking authorization dialog was
// shown and permission is denied
print("Denied")
case .notDetermined:
// Tracking authorization dialog has not been shown
print("Not Determined")
case .restricted:
print("Restricted")
#unknown default:
print("Unknown")
}
}
}
}
Last step added these method in appdelegate
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
//AppUpdater.shared.showUpdate(withConfirmation: false)
if #available(iOS 15.0, *) {
ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
switch status{
case .authorized:
Settings.shared.isAutoLogAppEventsEnabled = true
Settings.shared.isAdvertiserTrackingEnabled = true
print(ASIdentifierManager.shared().advertisingIdentifier)
break
case .denied:
Settings.shared.isAutoLogAppEventsEnabled = false
Settings.shared.isAdvertiserTrackingEnabled = false
print(ASIdentifierManager.shared().advertisingIdentifier)
default:
break
}
})
}
AppEvents.shared.activateApp()
}
In these way we can write the events
please added these framework in your class and write the event like this
import FBSDKCoreKit
AppEvents.shared.logEvent(AppEvents.Name(FACEBOOK_EVENTS.paymentDone.rawValue))
----event with params like this one----
var fbParams: [AppEvents.ParameterName : AppEvents.ParameterValue] = [:]
fbParams[AppEvents.ParameterName.content] = AppEvents.ParameterValue.init(rawValue: "10")
fbParams[AppEvents.ParameterName.init(rawValue: "number")] = AppEvents.ParameterValue.init(rawValue: "200")
AppEvents.shared.logEvent(AppEvents.Name.purchased, parameters: fbParams)

Related

Why is my Plaid Link Integration not opening the Link UI

I am attempting to integrate with Plaid, their documentation is a link confusing to follow and their support team informed me they are working on it. I also reached out to them to see if they could help me with my current integration but they informed me they are not able to look at my code because every integration is different.
Per the Plaid documentation I added my ngrok url to the redirect uri section in the dashboard (This works with the Plaid Link demo app: https://github.com/plaid/plaid-link-ios).
Also I added allow arbitrary load to the info plist.
I was able to get their Plaid Link-demo app up and running but when I try to place this code in my project I get the following error:
Thread 12: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
// MARK: Start Plaid Link using a Link token
// For details please see https://plaid.com/docs/#create-link-token
func presentPlaidLinkUsingLinkToken() {
#warning("Replace <#GENERATED_LINK_TOKEN#> below with your link_token")
// In your production application replace the hardcoded linkToken below with code that fetches an link_token
// from your backend server which in turn retrieves it securely from Plaid, for details please refer to
// https://plaid.com/docs/#create-link-token
let linkToken = "TOKEN HERE"
// <!-- SMARTDOWN_PRESENT_LINKTOKEN -->
// With custom configuration using a link_token
var linkConfiguration = LinkTokenConfiguration(token: linkToken) { success in
print("public-token: \(success.publicToken) metadata: \(success.metadata)")
}
linkConfiguration.onExit = { exit in
if let error = exit.error {
print("exit with \(error)\n\(exit.metadata)")
} else {
print("exit with \(exit.metadata)")
}
}
let result = Plaid.create(linkConfiguration)
switch result {
case .failure(let error):
print("Unable to create Plaid handler due to: \(error)")
case .success(let handler):
// UI Update code here
handler.open(presentUsing: .viewController(self))
self.linkHandler = handler
}
// <!-- SMARTDOWN_PRESENT_LINKTOKEN -->
}
#IBAction func refreshButtonAction(_ sender: Any) {
// UI Update code here
self.presentPlaidLinkUsingLinkToken()
}
I tried to place the self.presentPlaidLinkUsingLinkToken() into the following snippet:
DispatchQueue.main.async {
self.presentPlaidLinkUsingLinkToken()
}
Also per the Plaid documentation I added the following method within my App delegate swift file:
// MARK: Continue Plaid Link for iOS to complete an OAuth authentication flow
// <!-- SMARTDOWN_OAUTH_SUPPORT -->
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let webpageURL = userActivity.webpageURL else {
return false
}
// Check that the userActivity.webpageURL is the oauthRedirectUri
// configured in the Plaid dashboard.
guard let linkOAuthHandler = window?.rootViewController as? LinkOAuthHandling,
let handler = linkOAuthHandler.linkHandler,
webpageURL.host == linkOAuthHandler.oauthRedirectUri?.host &&
webpageURL.path == linkOAuthHandler.oauthRedirectUri?.path
else {
return false
}
// Continue the Link flow
if let error = handler.continueFrom(redirectUri: webpageURL) {
print("Unable to continue from redirect due to: \(error)")
}
return true
}
// <!-- SMARTDOWN_OAUTH_SUPPORT -->
But the same error occurs. Any suggestions will be appreciated.
In case anyone is having issues with this, I was able to figure it out. It is something incorrect in their cocoapod. Once, I downloaded and dragged the LinkKit Framework everything worked as expected. Also Plaid does not use their own cocoapod in their demo project, they do the manual install. Maybe they are working on this issue.
It's hard to tell without seeing the code in your application calling presentPlaidUsingLinkToken(), but your application is crashing on Thread 12. UIKit is not multi-threaded, so if you are presenting a view controller on a background thread you can expect crashes.
I see you tried a DispatchQueue.main.async, however I suspect you may have another threading issue going on.
Just putting this out there since this is one of few results for this issue: My issue was that I wasn't retaining the Handler object. As a result, the Plaid UI would never appear and no events would fire, leaving me with an endless spinner. Simply maintaining the reference in the class did the trick (as shown in the sample project and mentioned in passing in the documentation).
Kona Farry's solution above works. From Plaid docs:
Create a Handler - A Handler is a one-time use object used to open a Link session. The Handler must be retained for the duration of the Plaid SDK flow.

How to relinquish MacOS Camera,Microphone

In trying to code up a prototype, it struct me there's no way revert the request?
So, you add the entitlements of interest to your app's capabilities, and here is I check:
internal func avStatus(for media: AVMediaType) -> AVAuthorizationStatus {
let status = AVCaptureDevice.authorizationStatus(for: media)
switch status {
case .authorized: // The user has previously granted access to the microphone.
return .authorized
case .notDetermined: // The user has not yet been asked for microphone access.
return .notDetermined
case .denied: // The user has previously denied access.
return .denied
case .restricted: // The user can't grant access due to restrictions.
return .restricted
default:
Swift.print("Unknown AV status \(status) for .audio")
return status
}
}
I was thinking that a user action to request the use of the entitlement, and relinquish would be needed:
#objc #IBAction func audioVideoServicesPress(_ sender: AnyObject) {
let service = sender.title.components(separatedBy: " ").last
let media : AVMediaType = service == "Audio" ? .audio : .video
let status = self.avStatus(for: media)
guard ![.denied,.restricted].contains(status) else { return }
if status == .authorized {
print("how do we relinquish need in the a/v device")
}
else
{
AVCaptureDevice.requestAccess(for: media, completionHandler: { granted in
return
})
}
}
In other user actions, if they had disabled or denied, then I would route them to the proper settings app, but before I get there, how to undo the request?
This is no way to un-request access the a/v device?
I think that suggests I'm going about this all wrong?
There is nothing to "relinquish."
The user's button press is an intent. The user wants to perform some action that can be performed only if you have authorization. So if you have it or can get, your job now is to do it.
Okay, so either you have authorization or you don't. If authorization status is .undetermined, you might get it. If it's .authorized, you already did get it. In either of those cases, do what the user intends!
At the time of your print line, you have authorization so now go ahead and do whatever the user pressed the button intending to do.
Similarly, do not return in the completion handler for requesting access; instead, check granted and if true, do whatever the user pressed the button intending to do.
In any other case, you are hosed, so do nothing. The user's intent requires an authorization you don't have and cannot get. You might put up a dialog explaining why you can't do it, or send them off to where they can access their settings, but that's all you can do from here.

How to get authorization programmatically using Swift 4 to copy a video file to Photo Library from an application directory

When we fill value string section in Info.plist for "Privacy - Photo Library Additions Usage Description" and as the application try to copy video file to Photo Library iOS automatically ask authorization and everything goes ok after that.
But in that case it is not enough. We would like to do things if user do not want it.
We are doing it for camera usage as following;
func checkCameraAuthorizations(){
// Checks privacy authorizations and change aplication behaviour accordingly.
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
cameraUsageAuthorized = true
} else {
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
if granted {
self.cameraUsageAuthorized = true
} else {
self.cameraUsageAuthorized = false
}
})
}
}
We are using cameraUsageAuthorized variable in several place in application about camera usage.
But we could not find a similar function for video file copy from application document directory to Photo Library.
Also, we are filling Privacy values by hand in Info.List. Is there anyway to do it programmatically?
I could not find write only permission but as much as I see following code takes both write and read permission to photo library.
if PHPhotoLibrary.authorizationStatus() == .authorized {
self.photoLibraryAuthorized = true
} else {
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
self.photoLibraryAuthorized = true
} else {
self.photoLibraryAuthorized = false
}
}
}
But you must fill Privacy - Photo Library Additions Usage Description and Privacy - Photo Library Usage Description values.
I still don't know how to fill permission value strings in Info.plist programmatically though.

Determine login type using AWS Cognito during resume session (Swift)

I'm having a hard time trying to figure out how to determine the login type during a resume session using AWS Cognito. My code is based upon the MobileHub sample (below).
I've integrated a name/password mode for user pools (account creation and login) as well as as a Facebook login button which all works perfectly.
I have some logic in my application that needs to behave differently depending on the login type but I can't figure out how to do it.
Anyone done this?
func didFinishLaunching(_ application: UIApplication, withOptions launchOptions: [AnyHashable: Any]?) -> Bool {
print("didFinishLaunching:")
// Register the sign in provider instances with their unique identifier
AWSSignInManager.sharedInstance().register(signInProvider: AWSFacebookSignInProvider.sharedInstance())
AWSIdentityProfileManager.sharedInstance().register(FacebookIdentityProfile.sharedInstance(), forProviderKey: AWSFacebookSignInProvider.sharedInstance().identityProviderName)
AWSSignInManager.sharedInstance().register(signInProvider: AWSCognitoUserPoolsSignInProvider.sharedInstance())
AWSIdentityProfileManager.sharedInstance().register(UserPoolsIdentityProfile.sharedInstance(), forProviderKey: AWSCognitoUserPoolsSignInProvider.sharedInstance().identityProviderName)
setupAPIGateway()
setupS3()
let didFinishLaunching: Bool = AWSSignInManager.sharedInstance().interceptApplication(application, didFinishLaunchingWithOptions: launchOptions)
if (!isInitialized) {
AWSSignInManager.sharedInstance().resumeSession(completionHandler: { (result: Any?, authState: AWSIdentityManagerAuthState, error: Error?) in
print("didFinishLaunching Result: \(String(describing: result)) AuthState: \(authState) \n Error:\(String(describing: error))")
if authState == .authenticated {
// Facebook or Cognito???
AWSCognitoUserAuthHelper.getCurrentUserAttribute(name: "sub", completionHandler: { (userid) in
// we need to fetch the user
ObjectManager.instance.getUser(userid: userid, completionHandler: { (user) in
ObjectManager.instance.setCurrentUser(user: user)
})
})
}
}) // If you get an EXC_BAD_ACCESS here in iOS Simulator, then do Simulator -> "Reset Content and Settings..."
// This will clear bad auth tokens stored by other apps with the same bundle ID.
isInitialized = true
}
return didFinishLaunching
}
One solution I found was to cast to the different identity profile types such as the following:
let identityManager = AWSIdentityManager.default()
if let fbIdentityProfile = identityManager.identityProfile as? FacebookIdentityProfile {
print("didFinishLaunching - Facebook login")
} else if let upIdentityProfile = identityManager.identityProfile as? UserPoolsIdentityProfile {
print("didFinishLaunching - User Pools login")
}
I can model logic in my application around this. Not sure if there is a cleaner approach using the MobileHub helper classes or AWS APIs but this works.

iOS: cannot request authorization from Spotify app

I'm developping app using spotify-iOS-SDK, i have succesfully connect my app to Spotify from Safari, but when i try to connect my app from Spotify App, it doesn't request authorization in spotify app, instead it throw me back to my app after a checkmark icon show in Spotify and it caused crash to my app because the session is null.
This is my code:
var auth = SPTAuth.defaultInstance()!
auth.redirectURL = URL(string: ENV.SPOTIFY_REDIRECT_URL
auth.clientID = ENV.SPOTIFY_CLIENT_ID
auth.requestedScopes = [SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope,
SPTAuthPlaylistModifyPublicScope, SPTAuthPlaylistModifyPrivateScope]
if SPTAuth.supportsApplicationAuthentication(){
UIApplication.shared.openURL(auth.spotifyAppAuthenticationURL())
}else{
if UIApplication.shared.openURL(auth.spotifyWebAuthenticationURL()){
if auth.canHandle(auth.redirectURL) {
// To do - build in error handling
}
}
}
I have put spotify-action in my LSApplicationQueriesSchemes. What am i doing wrong here? I saw DemoProject from https://github.com/spotify/ios-sdk
and it worked. It should request authorization right after my app go to Spotify App
You need some code in your AppDelegate that looks like follows:
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let auth = SPTAuth()
if auth.canHandle(auth.redirectURL) {
auth.handleAuthCallback(withTriggeredAuthURL: url, callback: { (error, session) in
// Do other things to save session...
return true
}
return false
}
i have resolved my issues, the problem is my Bundle ID in my app is different from my BundleID in my Spotify Dashboard.
https://developer.spotify.com/my-applications
Just match my bundle ID and it worked!