I manage to do universal links and able to redirect it from continueUserActivity but the problem now is continueUserActivity will not work if the app is not in background. which means if you close the app and click the link it won't open.
I have found a code, but i don't know how to get the url. here is the solution
let activityDic = launchOptions?[UIApplicationLaunchOptionsUserActivityDictionaryKey]
if let isActivityDic = activityDic {
// Continue activity here
}
because when i check the type data of isActivityDic , it's an any object data. please help me to get the url
and this code also throw error
if let activityDic = launchOptions?[UIApplicationLaunchOptionsUserActivityDictionaryKey] {
let options = activityDic.allValues.filter({ (option:AnyObject) -> Bool in
return option is NSUserActivity
})
if options.count > 0 , let userActivity = options[0] as? NSUserActivity{
// User activity handling should be done here
}
}
Related
I have a ShareExtension in my iOS app. I am trying to use Suggestions. I can successfully 'donate' the intent using the following code from the apple developer website:
let groupName = INSpeakableString(spokenPhrase: "Juan Chavez")
let sendMessageIntent = INSendMessageIntent(recipients: nil,
content: nil,
speakableGroupName: groupName,
conversationIdentifier: "sampleConversationIdentifier",
serviceName: nil,
sender: nil)
// Add the user's avatar to the intent.
let image = INImage(named: "Juan Chavez")
sendMessageIntent.setImage(image, forParameterNamed: \.speakableGroupName)
// Donate the intent.
let interaction = INInteraction(intent: sendMessageIntent, response: nil)
interaction.donate(completion: { error in
if error != nil {
// Add error handling here.
} else {
// Do something, e.g. send the content to a contact.
}
})
This works fine and I am able to see my app icon in the suggestion row on the top for each conversation. However when I click on the suggestion, the intent property of the extentsionContext is nil:
override func viewDidLoad() {
super.viewDidLoad()
// Populate the recipient property with the metadata in case the user tapped a suggestion from the share sheet.
let intent = self.extensionContext?.intent as? INSendMessageIntent
if intent != nil { // this is nil despite selecting suggestion
let conversationIdentifier = intent!.conversationIdentifier
self.recipient = recipient(identifier: conversationIdentifier!)
}
}
My ShareExtension plist is as follows:
The other odd behaviour is that I'm able to do the donate from the main app but not from the app extension. In the main app the only relevant entry in the plist is the same NSUserActivityTypes entry. Not the NSExtension entries.
My understanding is that tapping on the suggestion, the extensionContext should contain the intent.
This is a bit of a tough one and I apologize if the title is incorrect. I wasn't sure how to word the title to have it make sense.
Essentially I am using the Jitsi SDK in iOS and we have it setup to use JWT for authentication and identifying host/guest. My problem comes in when the app is launched with a URL. The method...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {...
When that is launched it has the room number as part of the URL. That all makes sense. My problem is I need to call an API to "Checkin" the user and retrieve the jwt that is generated on the server. The above function has a return on whether or not the app should launch and all that jazz and in the Jitsi documentation it shows the return should be...
return JitsiMeet.sharedInstance().application(app, open: finalURL, options: options)
However I don't want it to do that. I need to make an API call, the callback of my api call will have the jwt that I need, then I want it to open the app normally so I can handle joining the conference on my own.
I guess my main question is.. If I make an API call that has a callback that will launch the application with the needed arguments, but then I just return false from that function, will it work correctly?
I know that may sound confusing, so here is a quick snippet of what I was thinking...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
guard NSURLComponents(url: url resolvingAgainstBaseURL: true) != nil else {
return false
}
let room = url.lastPathComponent
if let finalURL = URL(string: 'myserverhere/' + room) {
callCheckinAPI(room: room) { (res) in
if(res.success) {
DispatchQueue.main.async {
self.launchMainApp(room)
}
} else {
//not sure how to return false from here
}
}
return false
}
return false
}
I feel like that would be a problem because the function would get its return before the Api call is completed, since the return can't wait for the api call to finish, but I'm not sure how I could go about handling this scenario. That is the method that gets called in AppDelegate when the app is launched with a specific URL, which is what happens when a user clicks on the link to join the meeting. So, is there a way to make an API call if the app is launched with a specific URL, and then handle it accordingly? Am I on the right path and the above stuff should, theoretically, work? I am just kind of lost and need the advice of someone who is much better with swift than I am.
Thank you for reading through, and I apologize again if the title is not correct. It was hard to explain and I wasn't sure what the title should be.
When application(open,options) is called you have to return a bool right away. If you make an asynchronous call to the server for the JWT token and then return false it'll just stop right there.
So just return true and proceed with what you're doing. After looking at the documentation https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application
it seems like you're on your way to launching the app anyways if it's not already launched.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
guard NSURLComponents(url: url resolvingAgainstBaseURL: true) != nil else {
return false
}
let room = url.lastPathComponent
// new made up function
guard roomExists(url) else {
return false
}
if let finalURL = URL(string: 'myserverhere/' + room) {
callCheckinAPI(room: room) { (res) in
if(res.success) {
DispatchQueue.main.async {
// the view controller should already be attached to the window by this point
// so inside this function it should have a function to connect to the room
mainviewcontroller.connect(to:room)
// self.launchMainApp(room)
}
} else {
// show alert that something went wrong
}
}
}
return true
}
I am facing an issue with Firebase dynamic link. The link is generated from the backend. and it's working perfectly in android. but in iOS when I shared the dynamic link on whatsapp or messages app on iPhone. and tap on the link app is opened. and our following function is called in Appdelegate. and after that firebase function is called with the received URL. and the firebase functions return nil. But when I open that link from the safari app is opened and that same firebase function returns the deeplink URL. I have searched alot but did not find the solution. I am using firebase 7.11 version pods.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let incomingUrl = userActivity.webpageURL{
print("Incoming URL is \(incomingUrl)")
let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingUrl) { (dynamicLink, error) in
// guard error == nil else{
// print("Found an error! \(error!.localizedDescription)")
// return
// }
print("error \(error)")
print("dynamicLink \(dynamicLink)") // It returns nil when app open from whatsapp or any 3rd party app.
}
if linkHandled{
return true
}else{
return false
}
}
return false
}
I have also received the following error in both cases.
Error Domain=com.firebase.dynamicLinks Code=403 "(null)" UserInfo={code=403, message=Requests to this API firebasedynamiclinks.googleapis.com method google.firebase.dynamiclinks.v1.DynamicLinksService.GetIosReopenAttribution are blocked., status=PERMISSION_DENIED, details=(
{
"#type" = "type.googleapis.com/google.rpc.ErrorInfo";
domain = "googleapis.com";
metadata = {
consumer = "projects/37866531840";
service = "firebasedynamiclinks.googleapis.com";
};
reason = "API_KEY_SERVICE_BLOCKED";
}
)}
Did you add your custom domains in your info.plist file with FirebaseDynamicLinksCustomDomains parameter like image below.
Have you set AppBundle id as deepLinkURLScheme as like below in AppDelegate class under function didFinishLaunchingWithOptions
let kAppBundleID = Bundle.main.bundleIdentifier
FirebaseOptions.defaultOptions()?.deepLinkURLScheme = kAppBundleID
Please try with this.
Reference:
https://firebase.google.com/docs/dynamic-links/ios/create
By default, Dynamic Links uses your app's bundle identifier as the URL scheme needed to open up your application. We recommend staying with this default value to keep your implementation simple.
I am trying to open app B, from app A using universal links as below:
#IBAction func openButtonPressed(_ sender: Any) {
if let appURL = URL(string: "https://website.com/section/Paris") {
UIApplication.shared.open(appURL) { success in
if success {
print("The URL was delivered successfully.")
} else {
print("The URL failed to open.")
}
}
} else {
print("Invalid URL specified.")
}
}
In App B's AppDelegate -> application continueUserActivity restorationHandler method, it calls another function where the userActivity is processed.
Here there is a check for NSUserActivity's userInfo.
It is checking for a key PageName in NSUserActivity's userInfo property, and according to the value it assigns a type to the activity and routes the app accordingly.
NSString *page = activity.userInfo[#"PageName"];
if ([page isEqualToString:#"Places"]) {
return UserActivityTypePlaces;
} else {
return UserActivityTypeLink;
}
I was wondering if I can change or add a key value pair to userInfo in App A, so that App B can route me to another tab. (In this case I want it to open another tab and search the location string that is in the universal link)
I have checked other questions related with NSUserActivity's userInfo on StackOverflow and Apple Developer forums but they are not changing it on the calling app.(such as in App A)
Thank you
I made these in App A:
let activity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb)
activity.title = "Places"
activity.userInfo = ["Page" : "Places"]
let userInfoSet:Set = ["Page"]
activity.requiredUserInfoKeys = userInfoSet
self.userActivity = activity
self.userActivity?.becomeCurrent()
and added the NSUserActivityDelegate method:
override func updateUserActivityState(_ activity: NSUserActivity) {
let dict:Dictionary = ["Page":"Places"]
userActivity?.addUserInfoEntries(from: dict)
}
But the NSUserActivity object is not something that goes from one app to the other. So this change only works within app A.
Hope this helps someone else too.
I have integrated branch.io deep linking in my iOS app (Swift 4.2). What I want is canonical_url when user taps on branch.io short link.
When app is running in background I get all required parameters in following block. but when app is not running and try to tap branch.io short link then app launches but I do not get required parameters even clicked_branch_link is false.
Branch.getInstance().initSession(launchOptions: launchOptions)
{ (params, error) in
print(params as? [String: AnyObject] ?? {})
guard error == nil else { return }
guard let userDidClick = params?["+clicked_branch_link"] as? Bool else { return }
}
Please make sure you have all the basic implementation code as shown in our docs here: https://branchmetrics.github.io/docs/pages/apps/ios/#initialize-branch.
In addition, you can find a working implementation of the Swift Branch SDK in our Testbed application here: https://github.com/BranchMetrics/ios-branch-deep-linking/tree/master/Branch-TestBed-Swift.