Open URL from button action in Swift - ios

In my app I am retrieving an URL from a parse.com Object.
I want to open the device's browser and show the URL received from Parse when the user clicks on the button.
This is the action:
#IBAction func botonNuevos(sender: AnyObject) {
let query = PFQuery(className: "datos_contacto")
query.getObjectInBackgroundWithId("H52ZxGm3U8", block: {
(questionObject: PFObject?, error: NSError?) -> Void in
let webNuevos: AnyObject! = questionObject!.valueForKey("dato_contacto")
print(webNuevos) //= http://www.touchemotors.com
if let webCallURL = NSURL(string: webNuevos as! String ) {
let application = UIApplication.sharedApplication()
if application.canOpenURL(webCallURL) {
application.openURL(webCallURL)
}
else{
print("failed")
}
}
})
}
when the button is clicked, the log shows the received URL, but the browser is not launched and any error is shown.
Please tell what is wrong in my code. Thank you

UPDATE
Finally after discussion with OP it turned out that value returned by questionObject!.objectForKey("dato_contacto") had whitespace at the end so NSUrl(string:) did not parse it well.
UPDATE
You are using wrong method NSObject.valueForKey(). Use PFObject.objectForKey() instead.
For iOS 9:
From here:
If your app is linked on or after iOS 9.0, you must declare the URL
schemes you want to pass to this method. Do this by using the
LSApplicationQueriesSchemes array in your Xcode project’s Info.plist
file. For each URL scheme you want your app to use with this method,
add it as a string in this array.
If your (iOS 9.0 or later) app calls this method using a scheme you
have not declared, the method returns false, whether or not an
appropriate app for the scheme is installed on the device.
Add http scheme to LSApplicationQueriesSchemes array in Info.plist.

Related

iOS App Delegate params dictionary doesn't contain any relevant links

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.

Force user to update the app programmatically in iOS

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.

Send button click event from iOS Today Widget without launching the host app

I am writing a Today Widget for an iOS app. The widget has a few action buttons. I want to receive the click event when someone clicks on it. However, it should not launch the app.
I've already tried this but to no avail.
My current implementation is to define a URL Scheme, and call openURL on those button presses like so:
Button 1 links to myApp://button1
Button 2 links to myApp://button2
Button 3 links to myApp://button3
I am receiving these events in the AppDelegate's
application(_:open:options:)
Here's the Code in TodayWodgetController
#IBAction func widgetClicked(sender: UIButton){
if sender == button1 {
let u = NSURL(string: "myApp://button1")
self.extensionContext?.open(u! as URL, completionHandler: nil)
}
...
}
and here is the code I'm using in the host app's AppDelegate
func application(_ app: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if url.absoluteString.range(of: "button1") != nil{
print ("Button 1 Pressed")
}
....
return true
}
However, like I said, it also launches the host App. I want it to just send me the click event without launching the App.
Any help would be appreciated.
I don't think there is a way to do that from an app extension.
Definition of open(_ URL: URL, completionHandler: ((Bool) -> Void)? = nil) as described by Apple:
Asks the system open a URL on behalf of the currently running app
extension.
Each extension point determines whether to support this method, or
under which conditions to support this method. In iOS 8, only the
Today extension point (used for creating widgets) supports this
method.
Important: Apple allows a widget to use the open(_:completionHandler:) method to open the widget’s own containing
app.

APAddressBook with Swift 3 - EXC_ARM_BREAKPOINT

I am updating one of my apps to Swift 3 and i am using the APAddressBook Library (https://github.com/Alterplay/APAddressBook) as always. Here is a Swift example from APAddressBook but this fails, too
self.addressBook.loadContacts({ (contacts: [APContact]?, error: NSError?) in
if let uwrappedContacts = contacts {
// do something with contacts
}
else if let unwrappedError = error {
// show error
}
} as! APLoadContactsBlock)
The app crash with "Thread 1: EXC_BREAKPOINT (EXC_ARM_BREAKPOINT)" but i have no idea why.
this fix the problem , after add NSContactsUsageDescription in your info.plist
addressBook.loadContacts { (contacts: [APContact]?, error: Error?) in
if let unwrappedContacts = contacts {
//your code
}else if error != nil {
//your code for error
}
}
Firstly check if APAddressBook is supporting Swift 3. Then read carefully description of the framework.
Warning for iOS 10.0 and after
To protect user privacy, an iOS app linked on or after iOS 10.0, and
which accesses the user’s contacts, must statically declare the intent
to do so. Include the NSContactsUsageDescription key in your app’s
Info.plist file and provide a purpose string for this key. If your app
attempts to access the user’s contacts without a corresponding purpose
string, your app exits.

Redirect back to app from Share Extension

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.

Resources