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.
Related
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
}
In my iOS app I'm using TwitterKit
The problem I have faced is that when twitter app is not installed there is no way to log in user
I saw answers that suggesting adding any callback URL but that doesn't work anymore
I have tried 2 things: search in their docs and found nothing. tried this answer Tweeting using Twitterkit fails when Twitter app is not installed in iOS 11. This solution doesn't work anymore
Thanks for any help!
Add callback URL in this format
twitter-twitterid://
(Replace "twitterid" with "your twitter app id")
After lot of R&D I got the success. Twitter has made call back url compulsory. I found from this link
You need to add twitterkit-123456478:// (twitterkit-consumerKey) in call back url at twitter dashboard setting.
I used this method for opening safari (make sure you add safari framework)
TWTRTwitter.sharedInstance().logIn(with: self) { (session, error) in
if (session != nil) {
print("signed in as \(session?.userName ?? "")");
} else {
print("error: \(error?.localizedDescription ?? "")");
}
}
Appdelegate
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if url.scheme?.caseInsensitiveCompare(("twitterkit-" + TwitterKey.consumerKey)) == .orderedSame{
TWTRTwitter.sharedInstance().application(app, open: url, options: options)
}
return true
}
I've added the Branch.io SDK to my iOS project. I have a custom URL scheme which works fine, I've added universal app links which work fine.
Now, I've enabled in my Dashboard the iOS app where I've added my custom URL scheme, custom URL for downloading the app, the App Prefix and the bundle identifier.
In the 'quick links' section I've created a quick link witch has a ["key": "value"] pair for the deep link section and added a redirect again to my jenkins where the ipa can be downloaded.
Now, if I access that link by copy/pasting in mobile safari, without the app installed i'm correctly taken to Jenkins.
At this point I run the app from xcode, and in app delegate I have
branch = Branch.getInstance()
branch.initSession(launchOptions: launchOptions, andRegisterDeepLinkHandler: {params, error in
if error == nil {
// params are the deep linked params associated with the link that the user clicked -> was re-directed to this app
// params will be empty if no data found
// ... insert custom logic here ...
print("params: %#", params as? [String: AnyObject] ?? {})
} else {
print(error?.localizedDescription ?? "")
}
})
The issue is here that params always only contains two params:
params: %# ["+clicked_branch_link": 0, "+is_first_session": 0]
I've also implemented
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let branchHandled = branch.application(application,
open: url,
sourceApplication: sourceApplication,
annotation: annotation
)
if (!branchHandled) {
// If not handled by Branch, do other deep link routing for the Facebook SDK, Pinterest SDK, etc
var handled = false
handled = //this is Facebook handling
guard handled == false else { return true }
} else {
// do some stuff if it's coming from branch
}
return branchHandled
}
My interest is for branch to pass my ["key": "value"] pair in order to have it handled like a deep link at this point, but I don't know what else to try.
The failure of deferred deep-linking could be due to mismatch of Branch key and Branch link for a particular Branch app. Verify the Branch link used for deferred deep-linking is generated using the same Branch key which is used in info.plist of the iOS project.
In my iOS app I have enabled force app update feature. It is like this.
If there is a critical bug fix. In the server we are setting the new release version. And in splash screen I am checking the current app version and if its lower than the service version, shows a message to update the app.
I have put 2 buttons "Update now", "Update later"
I have 2 questions
If I click now. App should open my app in the appstore with the button UPDATE. Currently I use the link "http://appstore.com/mycompanynamepvtltd"
This opens list of my company apps but it has the button OPEN, not the UPDATE even there is a new update for my app. whats the url to go for update page?
If he click the button "Update Later" is it ok to close the app programmatically? Does this cause to reject my app in the appstore?
Please help me for these 2 questions
Point 2 : You should only allow force update as an option if you don't want user to update later. Closing the app programmatically is not the right option.
Point 1 : You can use a good library available for this purpose.
Usage in Swift:
Library
func applicationDidBecomeActive(application: UIApplication) {
/* Perform daily (.daily) or weekly (.weekly) checks for new version of your app.
Useful if user returns to your app from the background after extended period of time.
Place in applicationDidBecomeActive(_:)*/
Siren.shared.checkVersion(checkType: .daily)
}
Usage in Objective-C: Library
-(void)applicationDidBecomeActive:(UIApplication *)application {
// Perform daily check for new version of your app
[[Harpy sharedInstance] checkVersionDaily];
}
How it works : It used lookup api which returns app details like link including version and compares it.
For an example, look up Yelp Software application by iTunes ID by calling https://itunes.apple.com/lookup?id=284910350
For more info, please visit link
Don't close the app programmatically. Apple can reject the app. Better approach will be do not allow user to use the app. Keep the update button. Either user will go to app store or close the app by himself.
According to Apple, your app should not terminate on its own. Since the user did not hit the Home button, any return to the Home screen gives the user the impression that your app crashed. This is confusing, non-standard behavior and should be avoided.
Please check this forum:
https://forums.developer.apple.com/thread/52767.
It is happening with lot of people. In my project I redirected the user to our website page of downloading app from app store. In that way if the user is not getting update button in app store, at least the user can use the website in safari for the time being.
To specifically answer your question:
Use this URL to directly open to your app in the app store:
https://apps.apple.com/app/id########## where ########## is your app's 10 digit numeric ID. You can find that ID in App Store Connect under the App Information section. It's called "Apple ID".
I actually have terminate functionality built into my app if it becomes so out of date that it can no longer act on the data it receives from the server (my app is an information app that requires connectivity to my web service). My app has not been rejected for having this functionality after a dozen updates over a couple years, although that function has never been invoked. I will be switching to a static message instead of terminating the app, just to be safe to avoid future updates from being rejected.
I have found that the review process is at least somewhat subjective, and different reviewers may focus on different things and reject over something that has previously been overlooked many times.
func appUpdateAvailable() -> (Bool,String?) {
guard let info = Bundle.main.infoDictionary,
let identifier = info["CFBundleIdentifier"] as? String else {
return (false,nil)
}
// let storeInfoURL: String = "http://itunes.apple.com/lookupbundleId=\(identifier)&country=IN"
let storeInfoURL:String = "https://itunes.apple.com/IN/lookup?
bundleId=\(identifier)"
var upgradeAvailable = false
var versionAvailable = ""
// Get the main bundle of the app so that we can determine the app's
version number
let bundle = Bundle.main
if let infoDictionary = bundle.infoDictionary {
// The URL for this app on the iTunes store uses the Apple ID
for the This never changes, so it is a constant
let urlOnAppStore = NSURL(string: storeInfoURL)
if let dataInJSON = NSData(contentsOf: urlOnAppStore! as URL) {
// Try to deserialize the JSON that we got
if let dict: NSDictionary = try?
JSONSerialization.jsonObject(with: dataInJSON as Data, options:
JSONSerialization.ReadingOptions.allowFragments) as! [String:
AnyObject] as NSDictionary? {
if let results:NSArray = dict["results"] as? NSArray {
if let version = (results[0] as! [String:Any]).
["version"] as? String {
// Get the version number of the current version
installed on device
if let currentVersion =
infoDictionary["CFBundleShortVersionString"] as? String {
// Check if they are the same. If not, an
upgrade is available.
print("\(version)")
if version != currentVersion {
upgradeAvailable = true
versionAvailable = version
}
}
}
}
}
}
}
return (upgradeAvailable,versionAvailable)
}
func checkAppVersion(controller: UIViewController){
let appVersion = ForceUpdateAppVersion.shared.appUpdateAvailable()
if appVersion.0 {
alertController(controller: controller, title: "New Update", message: "New version \(appVersion.1 ?? "") is available")
}
}
func alertController(controller:UIViewController,title: String,message: String){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Update", style: .default, handler: { alert in
guard let url = URL(string: "itms-apps://itunes.apple.com/app/ewap/id1536714073") else { return }
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}))
DispatchQueue.main.async {
controller.present(alertController, animated: true)
}
}
Use appgrades.io. Keep your app focus on delivering the business value and let 3rd party solution do their tricks. With appgrades, you can, once SDK integrated, create a custom view/alert to display for your old versions users asking them to update their apps. You can customize everything in the restriction view/alert to make it appear as part of your app.
Is it possible to redirect a user back to my app from my share extension (after finishing the posting/other action)?
I couldn't get UIApplication.sharedApplication() to work - method was unavailable.
Any ideas if this is possible/if Apple even allows it?
Actually this is possible by searching for UIApplication up in the responder chain and invoking openURL on it, like described in this response : https://stackoverflow.com/a/28037297/554203.
This is the code that works for XCode 8, Swift 3.0 and iOS10 (again, extracted from Julio Bailon's response above):
let url = NSURL(string:urlString)
let context = NSExtensionContext()
context.open(url! as URL, completionHandler: nil)
var responder = self as UIResponder?
while (responder != nil){
if responder?.responds(to: Selector("openURL:")) == true{
responder?.perform(Selector("openURL:"), with: url)
}
responder = responder!.next
}
As of iOS 9.3, this does not seem to be possible for a Share Extension.