I am very puzzled by a situation here. Basically, i have an branch link with 2 parameters. In my iOS, it is handled in the deeplinkhandler setup during the setup.
However, the link works VERY differently. We installed on the TF on our phones. The app store does not have the version w branch yet.
configuration below:
from both of our chrome: works seamlessly, deeplinked works
from my phone, iMessage: goes to appstore
from his phone, iMessage: goes to app but deep linking does not work
so.....
if it works on chrome, does that mean my integration is done right?
why would it work differently from my phone and another phone, given we have the same OS?
if the main goal is to deeplink properly (like emails), wouldn't it be more effective to do a URL scheme, where we know for certain that the deep link will land ther user where we need?
EDIT: Figured out part of the problem: my device was "disabled for universal links"
Now, it is because continue userActivity is not being called even when the link appears to work! I know because it does not show up in my debug logs, AND when i initialized the app WHEN the deeplink DOES not appear to work, the deeplink is triggered when I open the app. :)
// This is my method signature, its in an appdelegate EXTENSION.
// could that be the problem?
// i am using swift 2.3
func application(application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: ([Any]?) -> Void) -> Bool {
application.fuckingComprehensivePrint("application continue in appdelegate")
return Branch.getInstance().continueUserActivity(userActivity)
}
EDIT 2: Ok, so all in all, there were THREE problems
My device was disabled for universal links
I put my method signature in an extension
My method signature was wrong (i used swift 3.0 syntax)
I also had URL handling in my app (separate from branch) that caused it to work in some instances.
Alex from Branch here: welcome to the crazy world of deep linking — this is why Branch exists :).
To answer your questions:
If things are working correctly in Chrome, it means your implementation is at least partially correct. This is because in some situations Branch can still use a URI scheme redirect with Chrome. If this is the case, your app will open without the 'forward to web' arrow in the top right corner (this is the giveaway that you were just Universal Linked). Based on what you're describing (Chrome deep links, but iMessage doesn't), it sounds like your openURL method (URI scheme links) is correctly configured but continueUserActivity (Universal Links) isn't.
Since behavior is different on identical devices, I suspect Universal Links on your device may have become disabled at some point during testing. This is very easy to do — thanks, Apple — but also fairly easy to fix: just paste the link into the iOS Notes app, long-press (not force-press) on the link, and then select Open in [app].
Up until iOS 9, URI schemes were the de facto best option. With Universal Links, and the changes Apple has imposed to force their adoption, URI schemes are an increasingly bad user experience (scary error messages, no fallback ability, etc.). You can read about this in greater detail in this blog post. Plain vanilla URI scheme deep links also cannot work when the app isn't yet installed, and many apps don't recognize them as valid, clickable links so they need to be wrapped inside a regular http link with a redirect.
Branch is a fundamentally different approach to content linking in apps.
Rather than standard links with paths to content, you can think of Branch links as unique tokens that deliver an unlimited number of key:value pairs to you in the app (including — if you really want it — a URI path that can be automatically triggered), in exactly the same way, in every situation.
So rather than needing to handle all the different and constantly-changing standards, trying to detect whether the app is installed when a link is clicked, worrying about which app or browser the user might be using to open the link, etc., all you need to do is wait for the link keys to be delivered. Branch has abstracted all of these technical details out into a separate framework.
It's conceptually different at first, but far more flexible because you're no longer limited to just the data that can be stored in a URI path.
Related
I have a question regarding executeJs function.
page.executeJs("$0.click();", downloadAnchor.getElement());
This line of code is not working in real iPhone Safari browser, though it works in mobile responsive mode from desktop safari. Appreciate your help on this
Browsers will be "suspicious" of anything starting a download that isn't a direct reaction to interaction by the user. This is done as a security precaution since starting to download files without the user's explicit consent can be dangerous in specific cases. Different browsers and configurations have different policies for exactly where to draw the line.
In your case, the download isn't started as a direct consequence of user interaction but instead as direct consequence of receiving a message from the server. This kind of pattern will simply not work reliably no matter what you do.
Instead, you need to design the interaction so that the download is directly triggered by the user. The easiest way of doing that is by having the user directly click on the actual download link. If you want to have some indirection, then you still need to make the action work directly in the browser without going through the server.
The new iTunes connect has analytics features now. You can specify the provider (pid) id and campaign id (cid) in the url. e.g.
https://itunes.apple.com/fr/app/candy-crush-saga/id553834731?mt=8&pid=1234&cid=My_Campaign
Can you pass those values into the app for various purposes?
I could not find anything that points to that, even after checking:
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
ref:
http://www.applift.com/blog/new-era-attribution-analytics.html
Unfortunately these params are not passed into the app. They are only used for iTunes Connect stats.
Here are the two common workarounds:
Using a fingerprint to implement deferred deep link and passing the params through that link (Tapstream can do this).
Using a provider like Tune/MobileAppTracking to match the link using the ID for advertising (IDFA).
I wrote a blog post to explain the details: https://attributionapp.com/blog/lets-solve-ios-attribution-methods/
I will edit this response in the future if Apple changes their API. If you let Apple know what you want to build and why passing in the params is a better policy they might change their minds faster: https://developer.apple.com/contact/
If you'd like to build your own fingerprinting mechanism to handle all of this deep linking like we have at branch.io, I can give you some instructions how! One side note as I read it in the accepted answer comments is that the INSTALL_REFERRER in Google Play is often very slow (> 1s delay) to launch after install, so if you're depending your user experience on this, your users in are in for a bumpy ride.
Here's how we built fingerprinting on iOS:
Configure a server endpoint (something like yoursite.com/redirect) to accomplish two things:
Serve up some client side JS to open your app if installed or fall back to the App Store if not. I listed out this code in this answer
Capture a set of of parameters about the clicking user by reading their user agent, etc. Here's a simple subset that will work well:
OS
OS version
IP address
Device model
When the user opens the app, send back that set of parameters, as read from the app to a new endpoint (something like yoursite.com/completematch).
If there's a match, you can pass back the parameters.
If you want to get savvy, you can drop a cookie on yoursite.com and save the match to the IDFA received from the app. Ideally, you'd only do this if you redirect into the app immediately so you're 100% sure of the match. Then if that same user ever clicks on the link again and installs the app, you don't have to use fingerprinting as you can just use the cookie-IDFA pair. We've got hundreds of millions of these at Branch so fingerprinting is more of a fallback mechanism at this point.
I want to send email through gmail or other message clients,is it possible in iOS.
I searched a lot but didn't find anything.
I don't want to use the default composer to send mail.I wants user to choose whether they want to send emails from the default mfmailcomposeviewcontroll or can send through gmail,yahoo.
There are a few ways in which 3rd-party developers may offer this functionality -- regardless of the manner selected, it is up to the 3rd-party vendor to decide how much (if any) of these methods are implemented and documented in the public domain as accessible to other applications or users. Ultimately you will need to consult the target product's documentation or support channels to determine if such mechanisms exist. While this is neither an exhaustive list, nor it is a list of formally supported mechanisms for achieving your goal, it should give you some insight into what to go research or ask additional questions about:
Option 1: Open URL Schemes
iOS Developers can register their applications to respond to a URL scheme like "tel://" or "sms://" or "http://". If vendors register a URL scheme it will be visible in the app's info.plist. While it won't speak to the format of any parameters you need to pass, the vendor's documentation or support personnel may be able to shine a light on those capabilities. For the HTTP scheme, iOS' MobileSafari.app is registered to respond -- the well-formed NSURL that gets passed to Safari is used as the URL to which Safari navigates when launched.
Option 2: 3rd-party SDK
Other vendors may elect to make an SDK available that you can implement in your app. Google Drive, Dropbox, and Facebook are all great examples. To incorporate these cloud filesystems or social capabilities in your own apps, you can register as a developer and follow install guides to both adopt their SDK and leverage the functionality the SDK provides.
As for your specific question, it appears as though Google's official Gmail app does have an undocumented URL Scheme that the community has discovered, reverse-engineered, and written up. While I VERY STRONGLY DISCOURAGE use of undocumented app capabilities there is nothing preventing you from using this functionality. Keep in mind that because this is undocumented, there is a distinct possibility that the feature could be removed, changed, or otherwise made unavailable in future builds that can leave your own app in a bit of a bind. Furthermore, for those of your users that do not have the Gmail app installed would not be able to leverage this URL Scheme and could lead to a poor user experience. All that said, with the caveats above, Tom Scotland has written up a series of blog posts on the Gmail app's URL scheme and updates to the scheme as a function of the rest of the Google constellation of iOS apps - http://tom.scogland.com/blog/2013/01/29/gmail-url-scheme/
Yahoo Mail is a separate issue -- As far as I can tell, this app offers neither of these access options, however I'll leave the digging as an exercise for the OP.
Again, I encourage you to use MFMailComposerViewController instead of unofficial or unapproved app capabilities so as to limit your own app's exposure to changes beyond your control.
Is there any way to consistently detect PhantomJS/CasperJS? I've been dealing with a spat of malicious spambots built with it and have been able to mostly block them based on certain behaviours, but I'm curious if there's a rock-solid way to know if CasperJS is in use, as dealing with constant adaptations gets slightly annoying.
I don't believe in using Captchas. They are a negative user experience and ReCaptcha has never worked to block spam on my MediaWiki installations. As our site has no user registrations (anonymous discussion board), we'd need to have a Captcha entry for every post. We get several thousand legitimate posts a day and a Captcha would see that number divebomb.
I very much share your take on CAPTCHA. I'll list what I have been able to detect so far, for my own detection script, with similar goals. It's only partial, as they are many more headless browsers.
Fairly safe to use exposed window properties to detect/assume those particular headless browser:
window._phantom (or window.callPhantom) //phantomjs
window.__phantomas //PhantomJS-based web perf metrics + monitoring tool
window.Buffer //nodejs
window.emit //couchjs
window.spawn //rhino
The above is gathered from jslint doc and testing with phantom js.
Browser automation drivers (used by BrowserStack or other web capture services for snapshot):
window.webdriver //selenium
window.domAutomation (or window.domAutomationController) //chromium based automation driver
The properties are not always exposed and I am looking into other more robust ways to detect such bots, which I'll probably release as full blown script when done. But that mainly answers your question.
Here is another fairly sound method to detect JS capable headless browsers more broadly:
if (window.outerWidth === 0 && window.outerHeight === 0){ //headless browser }
This should work well because the properties are 0 by default even if a virtual viewport size is set by headless browsers, and by default it can't report a size of a browser window that doesn't exist. In particular, Phantom JS doesn't support outerWith or outerHeight.
ADDENDUM: There is however a Chrome/Blink bug with outer/innerDimensions. Chromium does not report those dimensions when a page loads in a hidden tab, such as when restored from previous session. Safari doesn't seem to have that issue..
Update: Turns out iOS Safari 8+ has a bug with outerWidth & outerHeight at 0, and a Sailfish webview can too. So while it's a signal, it can't be used alone without being mindful of these bugs. Hence, warning: Please don't use this raw snippet unless you really know what you are doing.
PS: If you know of other headless browser properties not listed here, please share in comments.
There is no rock-solid way: PhantomJS, and Selenium, are just software being used to control browser software, instead of a user controlling it.
With PhantomJS 1.x, in particular, I believe there is some JavaScript you can use to crash the browser that exploits a bug in the version of WebKit being used (it is equivalent to Chrome 13, so very few genuine users should be affected). (I remember this being mentioned on the Phantom mailing list a few months back, but I don't know if the exact JS to use was described.) More generally you could use a combination of user-agent matching up with feature detection. E.g. if a browser claims to be "Chrome 23" but does not have a feature that Chrome 23 has (and that Chrome 13 did not have), then get suspicious.
As a user, I hate CAPTCHAs too. But they are quite effective in that they increase the cost for the spammer: he has to write more software or hire humans to read them. (That is why I think easy CAPTCHAs are good enough: the ones that annoy users are those where you have no idea what it says and have to keep pressing reload to get something you recognize.)
One approach (which I believe Google uses) is to show the CAPTCHA conditionally. E.g. users who are logged-in never get shown it. Users who have already done one post this session are not shown it again. Users from IP addresses in a whitelist (which could be built from previous legitimate posts) are not shown them. Or conversely just show them to users from a blacklist of IP ranges.
I know none of those approaches are perfect, sorry.
You could detect phantom on the client-side by checking window.callPhantom property. The minimal script is on the client side is:
var isPhantom = !!window.callPhantom;
Here is a gist with proof of concept that this works.
A spammer could try to delete this property with page.evaluate and then it depends on who is faster. After you tried the detection you do a reload with the post form and a CAPTCHA or not depending on your detection result.
The problem is that you incur a redirect that might annoy your users. This will be necessary with every detection technique on the client. Which can be subverted and changed with onResourceRequested.
Generally, I don't think that this is possible, because you can only detect on the client and send the result to the server. Adding the CAPTCHA combined with the detection step with only one page load does not really add anything as it could be removed just as easily with phantomjs/casperjs. Defense based on user agent also doesn't make sense since it can be easily changed in phantomjs/casperjs.
I know that there are some restricted api's or code that are not allowed to be in your app when you submit it to the app store.
How do you know what they are? Is there a way to check your app before you submit it to ensure you have not used such api's?
It is probably better to avoid this problem at the design stage, than trying to fix it later, so I was wondering if there is any tool in Xcode, or document to determine this.
The way Apple intends for you to do this is to use XCode's Validation feature. When you're submitting an app, you build for achiving (or Archive from the XCode menu). Then, you open up Organizer to see the archive you just created. At this point, you can press the Validate button in Organizer. That will perform a validation, without actually submitting the app. It will tell you if you're using Private APIs. Depending on how you use them, it might identify what the violation is:
There's definitely ways that code can fool this validation step, and "get away" with using Private APIs until the reviewer looks at the bundle. But, as far as I know, those ways would all be intentional methods of hiding Private API usage, and it sounds like you're trying to discover accidental usage.
If you fail this Validation test, then you might want to use something like AppScanner, mentioned in alan duncan's answer. But, for completeness, I wanted to make sure people knew that this Validation step is available in XCode, and checking for Private API usage is one of the things it's doing before you submit (and have to wait a few days to be told what you did wrong). Also, even if you don't use the Validate button in Organizer, but just use Submit, the tool is performing a Validation for you. The only difference is whether the bundle actually gets uploaded to iTunes Connect.
If you stick to documented interfaces as suggested above, you're fine. The only issue is with third-party libraries whose implementation may be opaque to you.
There is a Mac app called AppScanner that scans from private API usage. I have no experience with it, though.
You will get more information on Apple approval process from
App Store Review Guidelines for iOS apps (You must be a registered iOS developer for accessing this data).
iOS Human Interface Guidlines.
get the private API list.
use class-dump to process the Mach-O file, and get the processed string.
use regex to get the interface, class, or method in the string.
match the API to private API list.
then GOT it~
I opened a porject to do this, but because the reason of my company, canceled. very sorry for this.