I need to add tap functionality to labels (to open a website) in my app dynamically, I was thinking in create static function inside a class. I want to launch the function in any ViewController of my app.
I´ve done this using Swift 3:
class Launcher {
static func openWeb(label: UILabel, url: String) {
func tapFunction(sender:UITapGestureRecognizer) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap)
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
}
But doesn't work because I'm getting error in action: #selector(tapFunction)
error: Argument of '#selector' cannot refer to local function 'tapFunction(sender:)'
If I use this inside a ViewController code using action: #selector(myView.tapFunction) like the following, works
Inside viewDidLoad
let tap = UITapGestureRecognizer(target: self, action: #selector(MyViewController.tapFunction))
mylabel.isUserInteractionEnabled = true
mylabel.addGestureRecognizer(tap)
Separated function inside ViewController
func tapFunction(sender:UITapGestureRecognizer) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
I want to convert this last code to static function inside my Class to call it in any ViewController. Thank you
Try resolve it using extension
extension UILabel {
func openWeb(url: String) {
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
self.isUserInteractionEnabled = true
self.addGestureRecognizer(tap)
openURL(url: url)
}
func tapFunction(sender:UITapGestureRecognizer) {
openURL(url: "")
}
func openURL(url: String) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
}
let label = UILabel()
label.openWeb(url: "123")
To store link into label you can use associations with label object.
A method that is supposed to communicate with an instance cannot be a static / class method. That's what static / class method means; it is about the class — there is no instance in the story. Thus what you are proposing is impossible, unless you hand the static method the instance it is supposed to talk to.
To me personally, a tappable URL-opening label sounds like a label subclass, and this functionality would then be an instance method of that label.
First, there are some general problems with your approach. Selectors work by message passing. E.g. with UITapGestureRecognizer(target:, action:) The message calling the method (the action) is sent to the variable ( the target).
When you create a local function, that function is a member of the enclosing function and not the containing class or instance, so your approach categorically cannot work.
Even if it could work, it would also go against OOP and MVC design principals. A Label should not be in charge of what happens when it's tapped, just as the title of a book is not in charge of opening the book.
Taking all that into consideration, this is how I would solve your problem:
extension UIViewController {
func addTapGesture(to view: UIView, with selector: Selector) {
view.isUserInteractionEnabled = true
view.addGestureRecognizer(UITapGestureRecognizer(target: view, action: selector))
}
}
extension UIApplication {
func open(urlString: String) {
if #available(iOS 10.0, *) {
open(URL(string: urlString)!, options: [:], completionHandler: nil)
} else {
openURL(URL(string: urlString)!)
}
}
}
class YourVC: UIViewController {
var aLabel: UILabel? = nil
func addTapGesture() {
addTapGesture(to: aLabel!, with: #selector(tapGestureActivated))
}
func tapGestureActivated(gesture: UITapGestureRecognizer?) {
UIApplication.shared.open(urlString: "YourURLHere")
}
}
This abstracts away the boilerplate and is simple at the point of use, while still properly separating out Type responsibilities and using generalized functionality that can be re-used elsewhere.
Related
Can we customize SwiftEventBus Library to only trigger in the current active ViewController.
I'm trying to trigger an action when ever a notification occurs, so i'm using swift event bus to trigger when ever a push notification comes but it is triggering in all the places it is registered. Can we make so that it will only trigger the action in the active view. If not is there any other library I can use?
Wouldn't it be enough to deregister inactive ViewControllers as mentioned in the SwiftEventBus readme?
//Perhaps on viewDidDisappear depending on your needs
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
SwiftEventBus.unregister(self)
}
Modify library(or subclass SwiftEventBus) like below:
#discardableResult
open class func on(_ target: AnyObject, name: String, sender: Any? = nil, queue: OperationQueue?, handler: #escaping ((Notification?) -> Void)) -> NSObjectProtocol {
let id = UInt(bitPattern: ObjectIdentifier(target))
//modification start
let handlerIner:((Notification?) -> Void) = { [weak target] n in
if let vc = target as? UIViewController, vc.view?.window != nil {
handler(n)
}
}
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: name), object: sender, queue: queue, using: handlerIner)
// modification end
let namedObserver = NamedObserver(observer: observer, name: name)
Static.queue.sync {
if let namedObservers = Static.instance.cache[id] {
Static.instance.cache[id] = namedObservers + [namedObserver]
} else {
Static.instance.cache[id] = [namedObserver]
}
}
return observer
}
I've 2 siri shortcuts in my App.
I use NSUserActivity to donate these shortcuts. I've also created 2 NSUserActivityTypes in my info.plist.
There are 2 view controllers which handle these shortcuts (1 view controller for 1 shortcut).
If I add 1 siri shortcut from 1 view controller and then go to 2nd view controller the native siri shortcut button (INUIAddVoiceShortcutButton) on 2nd view controller automatically picks the first shortcut (created from 1st view controller) and shows "Added to Siri" with suggested phrase instead of showing "Add to Siri" button. I double checked that each NSUserActivity has different identifier but still somehow its picks the wrong shortcut.
View Controller 1:
let userActivity = NSUserActivity(activityType: "com.activity.type1")
userActivity.isEligibleForSearch = true
userActivity.isEligibleForPrediction = true
userActivity.title = shortcut.title
userActivity.suggestedInvocationPhrase = suggestedPhrase
let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributes.contentDescription = description
userActivity.contentAttributeSet = attributes
let shortcut = INShortcut(userActivity: userActivity)
let siriButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
siriButton.translatesAutoresizingMaskIntoConstraints = false
siriButton.shortcut = shortcut
self.view.addSubview(siriButton)
View Controller 2:
let userActivity2 = NSUserActivity(activityType: "com.activity.type2")
userActivity2.isEligibleForSearch = true
userActivity2.isEligibleForPrediction = true
userActivity2.title = shortcut.title
userActivity2.suggestedInvocationPhrase = suggestedPhrase
let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributes.contentDescription = description
userActivity2.contentAttributeSet = attributes
let shortcut = INShortcut(userActivity: userActivity2)
let siriButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
siriButton.translatesAutoresizingMaskIntoConstraints = false
siriButton.shortcut = shortcut
self.view.addSubview(siriButton)
A similar thing happens when I delete the App and reinstall without deleting the shortcuts from Phone's Settings App.
Seems like its an IOS bug. I figured out a workaround for this problem. You have to create a new siri button every time the user add/edit the siri shortcut. Before creating siri button do the following things
1- Get all the voice shortcuts from INVoiceShortcutCenter by calling the function. Note that this happens asynchronously, so you need to do it some time before you need the data (e.g. in your AppDelegate). You'll also need to re-load this whenever the user adds a Siri Shortcut (probably in the INUIAddVoiceShortcutViewControllerDelegate.addVoiceShortcutViewController(_:didFinishWith:error) method).
INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
if let error = error as NSError? {
os_log("Failed to fetch voice shortcuts with error: %#", log: OSLog.default, type: .error, error)
}
return
}
self.voiceShortcuts = voiceShortcutsFromCenter
}
2- In View Controller-1 check if the shortcut is already added or not by iterating all the voice shortcuts
let voiceShorcut = voiceShortcuts.first { (voiceShortcut) -> Bool in
if let activity = voiceShortcut.shortcut.userActivity, activity.activityType == "com.activity.type1" {
return true
}
return false
}
3- If your voice shortcut is registered then pass the INShortcut to siri button otherwise don't set it.
if voiceShorcut != nil {
let shortcut = INShortcut(userActivity: userActivity1)
siriButton.shortcut = shortcut
}
Do the same thing in Second View Controller.
It's iOS 12.0 bug.
You can fix it by update INUIAddVoiceShortcutButton.voiceShortcut with correct value.
Use KVO to observe "voiceShortcut" property and when it change assign correct value to it.
I've moved to intents setup now and I find that even having just one intent setup and working the INUIAddVoiceShortcutButton is not able to track my shortcut. Once phrase is recorded it shows the Added to Siri with phrase.
But every time the app relaunches the Add to Siri button shows up instead of the Added to Siri button with recorded phrase.
I have tried going by Bilal's suggestion and although I can see the INVoiceShortcutCenter showing me my shortcut as present it doesn't loaded it into the Siri button.
My code looks like this for the button itself.
private func addSiriButton() {
let addShortcutButton = INUIAddVoiceShortcutButton(style: .blackOutline)
addShortcutButton.delegate = self
addShortcutButton.shortcut = INShortcut(intent: engine.intent )
addShortcutButton.translatesAutoresizingMaskIntoConstraints = false
siriButtonSubView.addSubview(addShortcutButton)
siriButtonSubView.centerXAnchor.constraint(equalTo: addShortcutButton.centerXAnchor).isActive = true
siriButtonSubView.centerYAnchor.constraint(equalTo: addShortcutButton.centerYAnchor).isActive = true
}
I have all the protocols implement and I had a close look at the Soup app but just can't figure out what drives this inaccuracy.
Funny enough, even British Airways app developers have given up on that as their button has exactly the same fault behaviour.
Update: I've built another test project with minimal amount implementation for the Intent and the Add to Siri and Added to Siri works perfectly. I'm guessing at this point that there is something in my own apps codebase that is causing this unwanted behaviour.
update 2 Just wanted to let everyone know I have fixed the issue. Using intents works fine but there is definitely a little sensitivity in the Intents definition file itself. All I had to do is create a new intent which then was generated and that worked. Seems my initial intent was somehow corrupt but there were no errors. After creating another intent and re-assigning intent handling function to that it all worked as intended. (pun intended)
I encountered this error when I had an existing intent and working configuration, but added a new parameter. However, in my Intent configuration, I had not added the new parameter name to a supported combination under the Shortcuts app section.
For example, if I had two properties myId and myName, and specified them as such:
let intent = MyIntent()
intent.myId = 1234
intent.myName = "banana"
Then I would need a supported combination of myId, myName in my intents definition file. In my particular case, I had forgotten myName so the INUIAddVoiceShortcutButton was attempting to do a lookup using myId, myName but didn't know how.
I just fixed this issue myself by changing my implementation (originally based on the soupchef app) to this code sample provided by apple (https://developer.apple.com/documentation/sirikit/inuiaddvoiceshortcutbutton):
EDIT: I added code that shows how I create and pass in the shortcutObject (INShortcut) for both UserActivity and custom Intent shortcuts.
The Shortcut class is an enum that contains a computed property called intent that returns an instantiation of the custom intent.
private func addShortcutButton(shortcut: Shortcut, parentViewController: UIViewController, shortcutViewControllerDelegate: INUIAddVoiceShortcutViewControllerDelegate) {
guard let view = parentViewController.view else { return }
if let intent = shortcut.intent {
shortcutObject = INShortcut(intent: intent)
} else if let userActivity = view.userActivity {
shortcutObject = INShortcut(userActivity: userActivity)
}
self.shortcutViewControllerDelegate = shortcutViewControllerDelegate
addSiriButton(to: shortcutButtonContainer)
}
func addSiriButton(to view: UIView) {
let button = INUIAddVoiceShortcutButton(style: .whiteOutline)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
view.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: button.centerYAnchor).isActive = true
button.addTarget(self, action: #selector(addToSiri(_:)), for: .touchUpInside)
}
// Present the Add Shortcut view controller after the
// user taps the "Add to Siri" button.
#objc
func addToSiri(_ sender: Any) {
guard let shortcutObject = shortcutObject else { return }
let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcutObject)
viewController.modalPresentationStyle = .formSheet
viewController.delegate = shortcutViewControllerDelegate
parentViewController?.present(viewController, animated: true, completion: nil)
}
So we can't use the default Siri button, you have to use custom UIButton.
The class VoiceShortcutsManager will check all the voice intents and then we can search that list check if exist one match if yes so we should suggest edition if not we should suggest adding.
public class VoiceShortcutsManager {
private var voiceShortcuts: [INVoiceShortcut] = []
public init() {
updateVoiceShortcuts(completion: nil)
}
public func voiceShortcut(for order: DeviceIntent, powerState: State) -> INVoiceShortcut? {
for element in voiceShortcuts {
guard let intent = element.shortcut.intent as? ToggleStateIntent else {
continue
}
let deviceIntent = DeviceIntent(identifier: intent.device?.identifier, display: intent.device?.displayString ?? "")
if(order == deviceIntent && powerState == intent.state) {
return element
}
}
return nil
}
public func updateVoiceShortcuts(completion: (() -> Void)?) {
INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
if let error = error {
print("Failed to fetch voice shortcuts with error: \(error.localizedDescription)")
}
return
}
self.voiceShortcuts = voiceShortcutsFromCenter
if let completion = completion {
completion()
}
}
}
}
And then implement in your ViewController
class SiriAddViewController: ViewController {
let voiceShortcutManager = VoiceShortcutsManager.init()
override func viewDidLoad() {
super.viewDidLoad()
contentView.btnTest.addTarget(self, action: #selector(self.testBtn), for: .touchUpInside)
}
...
#objc func testBtn() {
let deviceIntent = DeviceIntent(identifier: smartPlug.deviceID, display: smartPlug.alias)
//is action already has a shortcut, update shortcut else create shortcut
if let shortcut = voiceShortcutManager.voiceShortcut(for: deviceIntent, powerState: .off) {
let editVoiceShortcutViewController = INUIEditVoiceShortcutViewController(voiceShortcut: shortcut)
editVoiceShortcutViewController.delegate = self
present(editVoiceShortcutViewController, animated: true, completion: nil)
} else if let shortcut = INShortcut(intent: intentTurnOff) {
let addVoiceShortcutVC = INUIAddVoiceShortcutViewController(shortcut: shortcut)
addVoiceShortcutVC.delegate = self
present(addVoiceShortcutVC, animated: true, completion: nil)
}
}
}
#available(iOS 12.0, *)
extension SiriAddViewController: INUIAddVoiceShortcutButtonDelegate {
func present(_ addVoiceShortcutViewController: INUIAddVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
addVoiceShortcutViewController.delegate = self
addVoiceShortcutViewController.modalPresentationStyle = .formSheet
present(addVoiceShortcutViewController, animated: true, completion: nil)
}
func present(_ editVoiceShortcutViewController: INUIEditVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
editVoiceShortcutViewController.delegate = self
editVoiceShortcutViewController.modalPresentationStyle = .formSheet
present(editVoiceShortcutViewController, animated: true, completion: nil)
}
}
#available(iOS 12.0, *)
extension SiriAddViewController: INUIAddVoiceShortcutViewControllerDelegate {
func addVoiceShortcutViewController(_ controller: INUIAddVoiceShortcutViewController, didFinishWith voiceShortcut: INVoiceShortcut?, error: Error?) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
#available(iOS 12.0, *)
extension SiriAddViewController: INUIEditVoiceShortcutViewControllerDelegate {
func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didUpdate voiceShortcut: INVoiceShortcut?, error: Error?) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
func editVoiceShortcutViewControllerDidCancel(_ controller: INUIEditVoiceShortcutViewController) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
}
}
This code was inspired/copy from this webpage:
https://www.nodesagency.com/test-drive-a-siri-shortcuts-intro/
My experience with solving this was a little different. Some intents added via the Add to Siri button worked, which adjusted to "Added to Siri", while others didn't. I realised the actions that worked didn't require parameters.
After setting default values for intents that exposed parameters, which are passed into INShortcut (and then assigned to INUIAddVoiceShortcutButton), all buttons updated their state correctly!
I want redirection to AppStore to native iOS Mail app by using the following code but its not working,i also replaced itms with http but then it opens safari browser but I need to open AppStore.
I'm using the following code it works in case of HTTP instead of itms.
Do I need to add anything more in plist because Allow Arbitrary Loads is already True. Please suggest me any idea to solve this problem.
let urlStr = "itms://itunes.apple.com/in/app/mail/id1108187098?mt=8"
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: urlStr)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: urlStr)!)
}
Starting from iOS 6 right way to do it by using SKStoreProductViewController class.
https://stackoverflow.com/a/32008404/5148089
func openStoreProductWithiTunesItemIdentifier(identifier: String) {
let storeViewController = SKStoreProductViewController()
storeViewController.delegate = self
let parameters = [ SKStoreProductParameterITunesItemIdentifier : identifier]
storeViewController.loadProductWithParameters(parameters) { [weak self] (loaded, error) -> Void in
if loaded {
// Parent class of self is UIViewContorller
self?.presentViewController(storeViewController, animated: true, completion: nil)
}
}
}
func productViewControllerDidFinish(viewController: SKStoreProductViewController) {
viewController.dismissViewControllerAnimated(true, completion: nil)
}
don't forget to import and delegate:
import StoreKit
class RateMeViewController: UIViewController, SKStoreProductViewControllerDelegate {
And simply call this:
openStoreProductWithiTunesItemIdentifier("1108187098")
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
}
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.