Check if URL is valid in Swift 4 - ios

How can you check to see if a URL is valid in Swift 4? I'm building a simple web browser for personal use and even though I know to enter the full URL each time I'd rather get an alert instead of the app crashing if I forget.
import UIKit
import SafariServices
class MainViewController: UIViewController {
#IBOutlet weak var urlTextField: UITextField!
#IBAction func startBrowser(_ sender: Any) {
if let url = self.urlTextField.text {
let sfViewController = SFSafariViewController(url: NSURL(string: url)! as URL)
self.present(sfViewController, animated: true, completion: nil)
}
print ("Now browsing in SFSafariViewController")
}
}
For example, if I was to type in a web address without http:// or https:// the app would crash with the error 'NSInvalidArgumentException', reason: 'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported.'

Reading the comments on the accepted answer, I could see that you actually want to validate the URL, to check if it's valid before trying to open with Safari to prevent any crash.
You can use regex to validate the string(I created an extension, so on any string, you can check if it is a valid URL):
extension String {
func validateUrl () -> Bool {
let urlRegEx = "((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?"
return NSPredicate(format: "SELF MATCHES %#", urlRegEx).evaluate(with: self)
}
}

You're probably crashing because you're using the ! operator and not checking that it will work. Instead try:
#IBAction func startBrowser(_ sender: Any) {
if let urlString = self.urlTextField.text {
let url: URL?
if urlString.hasPrefix("http://") {
url = URL(string: urlString)
} else {
url = URL(string: "http://" + urlString)
}
if let url = url {
let sfViewController = SFSafariViewController(url: url)
self.present(sfViewController, animated: true, completion: nil)
print ("Now browsing in SFSafariViewController")
}
}
}
This should give you the idea of how to handle the different cases, but you probably want something more sophisticated which can deal with https and strips whitespace.

Related

Correct way to handle incorrect URL when opening

The task is quite simple. With a given urlString open it when it is valid. This is what I tried:
func openURL(_ urlString: String) {
guard let url = URL(string: urlString) else {
showInvalidUrlAlert()
return
}
UIApplication.shared.open(url)
}
This work with this example: "https://www.google.de/?hl=de"
However when passing an invalid url, which is also possible in my application (for example: "asdfd") I get this error on the console but nothing happens in the app:
[default] Failed to open URL asdf: Error Domain=NSOSStatusErrorDomain Code=-50 "invalid input parameters" UserInfo={NSDebugDescription=invalid input parameters, _LSLine=252, _LSFunction=-[_LSDOpenClient openURL:options:completionHandler:]}
What is the best practice here?
You may want to use the completionHandler parameter:
func openURL(_ urlString: String) {
guard let url = URL(string: urlString) else {
showInvalidUrlAlert()
return
}
UIApplication.shared.open(url, completionHandler: { success in
if success {
print("opened")
} else {
print("failed")
// showInvalidUrlAlert()
}
})
}
Inside of guard statement, you can throw an exception created by your application instead only put a return, like this:
guard let urlString = url, !urlString.isEmpty, let url = URL(string: urlString) else {
throw ErrorEnum.invalidURL
}
With this approach, you can catch the error and send a UI Feedback for the User where it calls de func openURL.
Did the URL have a http or https scheme attached? to open a website, you must require the String to have http(s). other wise the application won't know how to handle it, since it also handles other protocols.
...supports many common schemes, including the http, https, tel, facetime, and mailto schemes...
https://developer.apple.com/documentation/uikit/uiapplication/1648685-open

IOS FacebookShare Error 'reserved' being returned

I've tried searching but could not find an answer. I have written an app and I am trying to share content to facebook. Basically I would like to share a URL and maybe a quote or title.
I keep getting an error called 'reserved' but I am not sure what it means or how to fix it. Any help would be great!
func fbClick() {
let content = LinkShareContent(url: URL(string: "www.google.com")!)
showShareDialog(content, mode: .native)
}
func showShareDialog<C: ContentProtocol> (_ content: C, mode: ShareDialogMode = .automatic) {
let dialog = ShareDialog(content: content)
dialog.presentingViewController = self
dialog.mode = mode
do {
try dialog.show()
} catch (let error) {
self.view.makeToast("Invalid share content. Failed to present share dialog with error \(error)", duration: 3.0, position: .top)
}
}
Figured it out.
This line...
let content = LinkShareContent(url: URL(string: "www.google.com")!)
Should have been like this...
let content = LinkShareContent(url: NSURL(string: "https://www.google.com")! as URL)
or like this
let content = LinkShareContent(url: NSURL(string: "https://www.google.com")! as URL, quote: quote)
Had the same reserved error but while using VideoShareContent. Spent 5 hours to find the issue and finally found. Really hope someone finds this helpful too.
Solution: when you are retrieving the url of your video from info param from the UIImagePickerController delegate method make sure you use the key "UIImagePickerControllerReferenceURL".
Example:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
picker.dismiss(animated: true)
if let videoURL = info["UIImagePickerControllerReferenceURL"] as? URL {
let video = Video(url: videoURL)
let content = VideoShareContent(video: video)
do {
try ShareDialog.show(from: self, content: content)
} catch {
print(error)
}
}
}
Additional info: initially I did not use this key "UIImagePickerControllerReferenceURL". Why: it's deprecated. According to Apple, you should use UIImagePickerControllerPHAsset instead. But the url from there also returns reserved error. Another try was to use key "UIImagePickerControllerMediaURL", but it also didn't succeed.

Load webview URL from other VC

I'm trying to make it work but nothing.
I want to make (URL textfield) on (RightVC) as (webview)-url on (LeftVC).
I saved the textfield with this code:
#IBAction func save(sender: UIButton) {
NSUserDefaults.standardUserDefaults().setObject(urltxtfield.text, forKey: "urltxtfield")
NSUserDefaults.standardUserDefaults().synchronize()
}
override func viewDidLoad() {
super.viewDidLoad()
if NSUserDefaults.standardUserDefaults().objectForKey("urltxtfield") != nil {
urltxtfield.text = NSUserDefaults.standardUserDefaults().objectForKey("urltxtfield") as? String
} else {
urltxtfield.placeholder = "urltxtfield"
}
}
And it's ok with save. But how to make URL work with LeftVC webview?
This pic to make you understand me:
In your LeftVC (webView), you first need to get the NSUserDefaults and then make a request with your webView.
let prefs = NSUserDefaults.standardUserDefaults()
if let url = prefs.stringForKey("urltxtfield"){
// You have an URL and can make the request
let requestURL = NSURL(string: url)
let request = NSURLRequest(URL: requestURL!)
webView.loadRequest(request)
}else{
// Nothing stored in NSUserDefaults
}
Update
After I got the project I updated the reference to the webView from strong to weak and I added the temporary code
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
In info.plist to allow you to browse on websites (you have to check if you want this and I can recommend you to read more about it).
here: some sample of code to call load request to UIWebview when user enter url that urlstring it pass here as parameter Instead of google.com:
UIWebView.loadRequest(webviewInstance)(NSURLRequest(URL: NSURL(string: "google.cam")!))
OR
let url = NSURL (string: "http://www.google.com");
let requestObj = NSURLRequest(URL: url!);
myWebView.loadRequest(requestObj);

Swift, can't open second app using 'deep link'

It seems I can't open the second app using my method. Nothing happened. Is there any silly mistakes here?
My second app .plist file
My first app code
#IBAction func btnCRM(sender: AnyObject) {
var customURL: NSString = "CRM://"
if (UIApplication.sharedApplication().canOpenURL(NSURL(fileURLWithPath: customURL as String)!)){
UIApplication.sharedApplication().openURL(NSURL(fileURLWithPath: customURL as String)!)
}
}
In addition to the URL Schemes under Item 0, you need to add URL identifier which is CFBundleURLName, as outlined here.
try this code:
let url = NSURL(string: "CRM://")
if (UIApplication.sharedApplication().canOpenURL(url!)) {
UIApplication.sharedApplication().openURL(url!)
}
'openURL' was deprecated in iOS 10.0
Updated version:
guard let url = URL(string: "CRM://"), UIApplication.shared.canOpenURL(url) else {
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
Swift 5.7 2023
The code below opens the main application
private func openMainApp() {
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: { _ in
guard let url = URL(string: self.appURL) else {
return
}
_ = self.openURL(url)
})
}
// Courtesy: https://stackoverflow.com/a/44499222/13363449 👇🏾
// Function must be named exactly like this so a selector can be found by the compiler!
// Anyway - it's another selector in another instance that would be "performed" instead.
#objc private func openURL(_ url: URL) -> Bool {
var responder: UIResponder? = self
while responder != nil {
if let application = responder as? UIApplication {
return application.perform(#selector(openURL(_:)), with: url) != nil
}
responder = responder?.next
}
return false
}

Make button open link - Swift

This is the code I have now, taken from an answer to a similar question.
#IBAction func GoogleButton(sender: AnyObject) {
if let url = NSURL(string: "www.google.com"){
UIApplication.sharedApplication().openURL(url)
}
}
The button is called Google Button and its text is www.google.com
How do I make it open the link when I press it?
What your code shows is the action that would occur once the button is tapped, rather than the actual button. You need to connect your button to that action.
(I've renamed the action because GoogleButton is not a good name for an action)
In code:
override func viewDidLoad() {
super.viewDidLoad()
googleButton.addTarget(self, action: "didTapGoogle", forControlEvents: .TouchUpInside)
}
#IBAction func didTapGoogle(sender: AnyObject) {
UIApplication.sharedApplication().openURL(NSURL(string: "http://www.google.com")!)
}
In IB:
Edit: in Swift 3, the code for opening a link in safari has changed. Use UIApplication.shared().openURL(URL(string: "http://www.stackoverflow.com")!) instead.
Edit: in Swift 4
UIApplication.shared.openURL(URL(string: "http://www.stackoverflow.com")!)
The string you are supplying for the NSURL does not include the protocol information. openURL uses the protocol to decide which app to open the URL.
Adding "http://" to your string will allow iOS to open Safari.
#IBAction func GoogleButton(sender: AnyObject) {
if let url = NSURL(string: "http://www.google.com"){
UIApplication.sharedApplication().openURL(url)
}
}
if let url = URL(string: "your URL") {
if #available(iOS 10, *){
UIApplication.shared.open(url)
}else{
UIApplication.shared.openURL(url)
}
}
as openUrl method is deprecated in iOS 10, here is solution for iOS 10
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString) as! URL
UIApplication.shared.open(settingsUrl, options: [:], completionHandler: nil)
In Swift 4
if let url = URL(string: "http://yourURL") {
UIApplication.shared.open(url, options: [:])
}
if iOS 9 or higher it's better to use SafariServices, so your user will not leave your app.
import SafariServices
let svc = SFSafariViewController(url: url)
present(svc, animated: true, completion: nil)
For Swift 3.0:
if let url = URL(string: strURlToOpen) {
UIApplication.shared.openURL(url)
}
This code works with Xcode 11
if let url = URL(string: "http://www.google.com") {
UIApplication.shared.open(url, options: [:])
}
The code that you have should open the link just fine. I believe, that you probably just copy-pasted this code fragment into your code. The problem is that the UI component (button) in the interface (in storyboard, most likely) is not connected to the code. So the system doesn't know, that when you press the button, it should call this code.
In order to explain this fact to the system, open the storyboard file, where your Google Button is located, then in assistant editor open the file, where your func GoogleButton code fragment is located. Right-click on the button, and drag the line to the code fragment.
If you create this button programmatically, you should add target for some event, for instance, UITouchUpInside. There are plenty of examples on the web, so it shouldn't be a problem :)
UPDATE: As others noted already, you should also add a protocol to the link ("http://" or "https://"). It will do nothing otherwise.
For Swift3 , below code is working fine
#IBAction func Button(_ sender: Any) {
UIApplication.shared.open(urlStore1, options: [:], completionHandler: nil)
}
Actually You Can Use It Like This In Your Action Button Works For Swift 5 :
guard let settingsUrl = URL(string:"https://yourLink.com") else {
return
}
UIApplication.shared.open(settingsUrl, options: [:], completionHandler: nil)
}
// How to open a URL in Safari
import SafariServices \\ import
#IBAction func google(_ sender: Any)
{
if let url = URL(string: "https://www.google.com")
{
let safariVC = SFSafariViewController(url: url)
present(safariVC, animated: true, completion: nil)
}
}
I made this way:
I imported SafariServices
import SafariServices
First step: I defined a button just above viewDidLoad:
let myButton = UIButton()
Second step: I called a function inside viewDidLoad:
func setupMyButton() {
view.addSubview(myButton)
myButton.configuration = .plain()
myButton.configuration?.cornerStyle = .capsule
myButton.configuration?.title = "Go to Google"
myButton.addTarget(self, action: #selector(selector), for: .touchUpInside)
myButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
myButton.widthAnchor.constraint(equalToConstant: 200),
myButton.heightAnchor.constraint(equalToConstant: 50),
])
}
Third step: At the bottom of the scope, I called an #objc func to use as selector. (Outside viewDidLoad)
#objc func selector() {
if let url = URL(string: "https://www.google.com")
{
let safariVC = SFSafariViewController(url: url)
present(safariVC, animated: true, completion: nil)
}
}
And I did not forget to call my func at the beginning of the viewDidLoad:
setupMyButton()
A dude named PRAVEEN BHATI helped me at the third step.
Hope this helps.

Resources