DynamicLink receiving nil does not contain valid required param - ios

My issue is only on iOS, Android is having no problem receiving the dynamic link
Dynamiclink is nil and when it isn't query params are not there
Firebase Setup
short link: https://examplebase.page.link/migration
dynamic link: https://www.examplelink.com/migrate
I've followed every video in firebase dynamic links documentation
long link I'm trying to use
https://examplebase.page.link/?link=https://www.examplelink.com/migrate?migrationToken=123&docNumber=123&apn=py.com.exam.hoc.debug&isi=11223344&ibi=py.com.exam.development
Before using the dynamic link I encode the deep link to look like this
https://examplebase.page.link/?link=https%3A%2F%2Fwww%2Eexamplelink%2Ecom%2Fmigrate%3FmigrationToken%3D123%26docNumber%3D123&apn=py.com.exam.hoc.debug&isi=11223344&ibi=py.com.exam.development
Where I process the dynamic link
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
let handled = DynamicLinks.dynamicLinks().handleUniversalLink(userActivity.webpageURL!) { dynamiclink, error in
if let migrationToken = dynamiclink?.url?.valueOf("migrationToken"),
let documentNumber = dynamiclink?.url?.valueOf("docNumber") {
if !migrationToken.isEmpty && !documentNumber.isEmpty {
//do stuff
} else {
// error
}
} else {
// error
}
}
return handled
}
Something to point out my short link has the path variable /migration I'm currently not using that in my long link, but if I do, the dynamic link works differently
Case 1 Using base link without the path variable
Inside this method DynamicLinks.dynamicLinks().handleUniversalLink(userActivity.webpageURL!) { dynamiclink, error in }
dynamiclink is nil even though userActivity.webpageURL! has the value of the link received. Error received
Deep Link does not contain valid required params. URL params: {
apn = "py.com.exam.hoc.debug";
efr = 1;
ibi = "py.com.exam.development";
isi = 1505794177;
link = "https://www.examplelink.com/migrate?migrationToken=95e7f2f9-178f-4533-8907-50b656757695&docNumber=4233512";
}
Case 2 Using base link with path variable
Inside this method DynamicLinks.dynamicLinks().handleUniversalLink(userActivity.webpageURL!) { dynamiclink, error in }
dynamiclink has the value of https://www.examplelink.com/migrate without the query param
In console I see same error than in Case 1
Following one tutorial from Firecast the guy says to set the dynamic link on firebase with the query params set, then my Firebase setup would look like this
Firebase Setup
dynamic link: https://www.examplelink.com/migrate?migrationToken=123&docNumber=123
The issue here is that the app receives those params hard coded, they are never replaced

Related

iOS Forward Firebase Dynamic Link to Web Code Base

I am using firebase deeplinks to open my app with specific parameters. Therefore I am using the library https://ionicframework.com/docs/native/firebase-dynamic-links. In Android it's working fine. But in iOS, I do not receive the link in the web code base:
CapacitorFirebaseDynamicLinks.addListener(
"deepLinkOpen",
(data: { url: string }) => {
console.log("Listener started);
}
);
FirebaseDynamicLinks.onDynamicLink().subscribe(
async (res: IDynamicLink) => {
console.log(res.deepLink);
console.log(res.matchType);
}
);
I followed the instructions of the plugin and firebase docs (https://firebase.google.com/docs/dynamic-links/ios/receive) to set up the iOS part. Therefore, I implemented the continue and open url functions in AppDelegate.swift as described in firebase docs. Currently I am receiving the link in the continue function but I do not know how to forward it to the subscriber in the web code base.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
let handled = DynamicLinks.dynamicLinks().handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
print("link received");
print(dynamiclink);
print(error);
}
return handled
}
There is no documentation about it, i also tried another deeplink plugin (https://github.com/ClipboardHealth/capacitor-firebase-dynamic-links).

iOS - getting both NFCTag hardware id and NDEF message in one reading session

I'm currently facing a problem with implementing a specific feature into an NFC based iOS 13 app. When reading a tag, I'd like to get the unique hardware id and the NDEF message in one session. So far I've checked different sample projects, including code provided by Apple and was able to get the information I'm interested in, but not in the same reading session.
I simplified the following code snippets to better focus on the problem (missing error checks, etc.).
Using an NFCTagReaderSession to get the unique hardware id:
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
let tag = tags.first!
session.connect(to: tag) { (error: Error?) in
if case let .miFare(mifareTag) = tag {
print(mifareTag.identifier as NSData) //this is the info I'm interested in
}
}
}
The payload type of a message record however seems to be only available in a NFCNDEFReaderSession:
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
let tag = tags.first!
session.connect(to: tag, completionHandler: { (error: Error?) in
tag.queryNDEFStatus(completionHandler: { (ndefStatus: NFCNDEFStatus, capacity: Int, error: Error?) in
tag.readNDEF(completionHandler: { (message: NFCNDEFMessage?, error: Error?) in
var statusMessage: String
if nil != error || nil == message {
statusMessage = "Fail to read NDEF from tag"
} else {
statusMessage = "Found 1 NDEF message"
let payload = message.records.first!
if let type = String(data: payload.type, encoding: .utf8) {
print("type:%#", type) //this is the info I'm interested in
}
}
session.alertMessage = statusMessage
session.invalidate()
})
})
})
}
As you can see, I can either get the hardware id, using a NFCTagReaderSession or the payload type of a message record, using a NFCNDEFReaderSession. Am I missing something here or are there indeed two different reading sessions required to get the information I'm interested in? Thanks in advance.
I finally found the solution for this :)
You can actually read the NDEF data in the delegate functions of your NFCTagReaderSession but in iOS 13 you have to use another delegate.
It seems in iOS 11 and 12 NFCTag was a protocol but in iOS 13 NFCTag became an enum and the former protocol was renamed to __NFCTag.
An __NFCTag can be casted to an NFCNDEFTag and then you can read NDEF data as usual.
To get an __NFCTag in your delegate functions you need to use __NFCTagReaderSessionDelegate instead.
To initialize your session you prepend __ to the pollingOption argument label of the initializer.
To connect to the tag you need to use __connect.
Here is how to read the identifier and the NDEF data.
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [__NFCTag]) {
let tag = tags.first!
session.__connect(to: tag) { (error: Error?) in
if let ndefTag = tag as? NFCNDEFTag {
if let miFareTag = ndefTag as? NFCMiFareTag {
// here you can get miFareTag.identifier like in your first code sample
}
ndefTag.readNDEF(completionHandler: { (message, error) in
// here you can read NDEF data like in your second code sample
})
}
}
}
I had the same issue and found solution in Apple's sample project, which I hardly recommend to read.
FYI:
Avoid using classes and methods with underscores as a prefix, it could lead you to the application rejection on AppStore.

How to safely handle multiple writes in firebase which must all happen

I want to handle a friend request in my app written in Swift using Firebase. In my database, this means that the user sending the request needs to add the other user to their "sentRequests" dictionary, and the user receiving the request needs to add the user sending the requests to their "receivedRequests" dictionary. The problem is, if the user sending the request has a faulty connection and only does the first part, then it might cause issues. Either both writes should happen or none. What can I do to fix this? I included my code below for reference, but honestly if someone just sends me a good tutorial or answer here that would be just has helpful as correctly rewriting my code.
static func sendRequestFromCurrentUser(toUser userThatRequestWasSentTo : User, succeeded : #escaping (Bool)->Void ){
let ref = Database.database().reference().child("users").child(User.current.uid).child("sentRequests").child(userThatRequestWasSentTo.uid)
ref.setValue(userThatRequestWasSentTo.toDictionary(), withCompletionBlock: {(error, ref) in
if error == nil{
let currentUserRef = Database.database().reference().child("users").child(userThatRequestWasSentTo.uid).child("receivedRequests").child(User.current.uid)
currentUserRef.setValue(User.current.toDictionary(), withCompletionBlock: {(error, ref) in
if error == nil{
succeeded(true)
}
else{
succeeded(false)
}
})
}
else{
succeeded(false)
}
})
}
So I stole this from the Firebase blog and got it to match my code. The answer is fairly intuitive, I just hadn't considered it. Basically you just create a reference to the top level of your database and specify the paths you want to write to in the dictionary (so not by creating specific references with child()), and then just call updateChildValues().
static func sendRequestFromCurrentUser(toUser userThatRequestWasSentTo : User, succeeded : #escaping (Bool)->Void ){
let ref = Database.database().reference()
// Create the data we want to update
var updatedUserData : [String : Any] = [:]
updatedUserData["users/\(User.current.uid)/sentRequests/\(userThatRequestWasSentTo.uid)"] = userThatRequestWasSentTo.toDictionary()
updatedUserData["users/\(userThatRequestWasSentTo.uid)/receivedRequests/\(User.current.uid)"] = User.current.toDictionary()
// Do a deep-path update
ref.updateChildValues(updatedUserData, withCompletionBlock: { (error, ref) in
if let error = error {
print("Error updating data: \(error.localizedDescription)")
succeeded(false)
}
else{
succeeded(true)
}
})
}

CloudKit: Get users firstname/surname

I'm trying to get the users first name using cloud kit however the following code is not getting the users first name and is leaving firstNameFromFunction variable empty. Does anyone know how to achieve this in iOS 10?
let container = CKContainer.default()
container.fetchUserRecordID { (recordId, error) in
if error != nil {
print("Handle error)")
}else{
self.container.discoverUserInfo(
withUserRecordID: recordId!, completionHandler: { (userInfo, error) in
if error != nil {
print("Handle error")
}else{
if let userInfo = userInfo {
print("givenName = \(userInfo.displayContact?.givenName)")
print("familyName = \(userInfo.displayContact?.familyName)")
firstNameFromFunction = userInfo.displayContact?.givenName
}else{
print("no user info")
}
}
})
}
}
the permission screen that comes up when asking for the first time, IMO, is very poorly worded. They need to change that. It says "Allow people using 'your app' to look you up by email? People who know your email address will be able to see that you use this app." This make NO sense. This has nothing to do with asking the user to get their iCloud first name, last name, email address.
Speaking of email address - this and the phone number from the lookupInfo property is missing - i.e. set to nil, even though those values are legit and correct. Filing a bug tonight.
First, you will need to request permission to access the user's information.
Then, you can use a CKDiscoverUserIdentitiesOperation. This is just like any other CKOperation (eg. the modify record operation). You just need to create a new operation with the useridentitylookupinfo. Then you will also need to create a completion block to handle the results.
Here is an example function I created:
func getUserName(withRecordID recordID: CKRecordID,
completion: #escaping (String) -> ()) {
if #available(iOS 10.0, *) {
let userInfo = CKUserIdentityLookupInfo(userRecordID: recordID)
let discoverOperation = CKDiscoverUserIdentitiesOperation(userIdentityLookupInfos: [userInfo])
discoverOperation.userIdentityDiscoveredBlock = { (userIdentity, userIdentityLookupInfo) in
let userName = "\((userIdentity.nameComponents?.givenName ?? "")) \((userIdentity.nameComponents?.familyName ?? ""))"
completion(userName)
}
discoverOperation.completionBlock = {
completion("")
}
CKContainer.default().add(discoverOperation)
} else {
// iOS 10 and below version of the code above,
// no longer works. So, we just return an empty string.
completion("")
}
}
First you need to ask the user for permission to be discovered.
Use CKContainer.default().requestApplicationPermission method passing .userDiscoverability on applicationPermission parameter.
The CKContainer.default().discoverUserInfo method is deprecated on iOS 10. Instead use CKContainer.default().discoverUserIdentity method.
Do something like:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userIdentity, error) in
print("\(userIdentity?.nameComponents?.givenName)")
print("\(userIdentity?.nameComponents?.familyName)")
})
}
}

Parse + Swift + Anonymous

In an effort to create the easiest user experience possible, I am on a mission to accept a user as an anonymous user using Parse + Swift. I had thought to use the Anonymous user functions in Parse to accomplish that. As a result, I created the following code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.setupParse()
// self.setupAppAppearance()
This first section is to create a user and see if at this point in the process - I have a nil objectId (typically true for the user when first they attempt to open the application).
var player = PFUser.currentUser()
if player?.objectId == nil {
}
else
{
println(player!.objectId)
}
If I have an objectId (indicating that I've been down this road before and saved an anonymous user object) - throw that to the console so I can see what it is and check it in the Parse user object. Cool - good so far.
Next - Check to see if the Object is nil again - this time to decide whether or not to attempt to perform an anonymous login - there's not a thing to use to generate an anonymous user other than this anonymous login action.
if player?.objectId == nil {
PFAnonymousUtils.logInWithBlock({
(success, error) -> Void in
if (error != nil)
{
println("Anonymous login failed.")
}
else
{
println("Anonymous login succeeded.")
If anonymous Login succeeded (still considering doing a network available check before trying to run these bits...but assuming network is valid) save a Bool to "isAnonymous" on the server to make sure that we have identified this user as anonymous - I may want that information later, so it seemed worth throwing this action.
Question 1: Do I need to re-query PFUser.currentUser() (known as player) to make sure that I have a valid anon user object that is connected to the server, or will the player object that I allocated earlier recognize that I've logged in and thereby recognize that I can throw other info into the associated record online? I think this is working as is - but I've been getting weird session token errors:
[Error]: invalid session token (Code: 209, Version: 1.7.5)
player!["isAnonymous"] = true as AnyObject
player!.saveInBackgroundWithBlock {
(success, error) -> Void in
if (error != nil)
{
println("error updating user record with isAnonymous true")
}
else
{
println("successfully updated user record with isAnonymous true")
}
}
}
})
}
else
{
}
return true
}
func setupParse()
{
Parse.setApplicationId("dW1UugqmsKkQCoqlKR3hX8dISjvOuApcffGAWR2a", clientKey: "BtXxjTjBRZVnCZbJODhd3UBUU8zuoPU1HBckXh4t")
enableAutomaticUserCreateInParse()
This next bit is just about trying to figure out some way to deal with those token problems. No idea whether it's doing me any good at all or not. It said to turn this on right after instantiating the Parse connection.
PFUser.enableRevocableSessionInBackgroundWithBlock({
(error) -> Void in
if (error != nil) {
println(error?.localizedDescription)
}
})
Next - just throwing around objects because I keep struggling with being connected and storing stuff or not being connected or losing session tokens. So - til I get this worked out - I'm creating more test objects than I can shake a stick at.
var testObject = PFObject(className: "TestObject")
testObject["foo"] = "barnone"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved.")
}
}
Question2: it appears to me that PFUser.enableAutomaticUser() while very handy - causes some headaches when trying to figure out whether I'm logged in/online/whatever. Anyone have any solid experience with this and able to guide me on how you'd check whether you were connected or not - I need to know that later to be able to decide whether to save more things to the user object or not.
func enableAutomaticUserCreateInParse() {
if PFUser.currentUser()?.objectId == nil
{
myHumanGamePlayer.playerDisplayName = "Anonymous Player"
PFUser.enableAutomaticUser()
}
}
Anyone out there who's an expert on using anonymous users in Parse with Swift, let's get in touch and post a tutorial - because this has cost me more hours than I'd like to think about.
Thank you!
Xylus
For player!["isAnonymous"] = true as AnyObject, don't save it as any object. Save it as a bool and look at your parse to see if it's a bool object. Try querying for current user in a different view controller and print to the command line. I hope this helped

Resources