I want to show a website in a WKWebView in my iOS app for which I need to set specific http cookies for the user's session. The cookies are already stored in the shared HttpCookieStorage of the app.
At the moment, I try to copy them from there to the WKWebsiteDataStore of the web view before loading the website with the following code:
class ViewerViewController: UIViewController {
// MARK: - Properties
var webView: WKWebView!
// MARK: - Life Cycle
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
addCookiesToWebView {
print("Load website")
if let url = Bundle.main.url(forResource: "MyWebsite", withExtension: "html"), let html = try? String(contentsOf: url) {
self.webView.loadHTMLString(html, baseURL: url)
}
}
}
// MARK: - Functions
func addCookiesToWebView(completionHandler: #escaping (() -> Void)) {
let sharedCookies = HTTPCookieStorage.shared.cookies!
let webViewCookieStore = webView.configuration.websiteDataStore.httpCookieStore
var count = 0
sharedCookies.forEach { cookie in
DispatchQueue.main.async {
webViewCookieStore.setCookie(cookie) {
count += 1
print("Added cookie \(cookie.name)")
if count == sharedCookies.count {
completionHandler()
}
}
}
}
}
}
Now, what is incomprehensible for me, is that this sometimes works and the user is authenticated in his user session, but sometimes it doesn't.
It seems like there might be a race condition that leads to the web view loading the website before all the cookies have been copied? When I researched this problem I noticed that other people have problems with cookies in WKWebView as well, but I didn't find any solution so far that actually solves my issue.
Is there a bug in my code or is there a better approach for copying multiple cookies from HttpCookieStorage to a WKWebView?
I have just started using Digits - Twitter API for Phone Number verification, but it seems I'm unable to read the user's Phone number, I'm not sure if there is a function for that or so, but after reading a while I knew that I can do that with a Call back after successful phone verification but no explanation for that !
AuthConfig.Builder authConfigBuilder = new AuthConfig.Builder()
.withAuthCallBack(callback)
.withPhoneNumber(phoneNumberOrCountryCodeFromMyActivity)
found this snippet but again not sure where to implement it.
HERE is my Action for the login button with phone verification:
fileprivate func navigateToMainAppScreen() {
performSegue(withIdentifier: "signedIn", sender: self)
}
#IBAction func tapped(_ sender: Any) {
let configuration = DGTAuthenticationConfiguration(accountFields: .defaultOptionMask)
configuration?.appearance = DGTAppearance()
configuration?.appearance.backgroundColor = UIColor.white
configuration?.appearance.accentColor = UIColor.red
// Start the Digits authentication flow with the custom appearance.
Digits.sharedInstance().authenticate(with: nil, configuration:configuration!) { (session, error) in
if session != nil {
// Navigate to the main app screen to select a theme.
self.navigateToMainAppScreen()
} else {
print("Error")
}
}
}
So I found the answer after digging a lot more in Digits Documentations and it was pretty simple, I had to add:
print(session.phoneNumber)
print(session.userID)
In the didTap function, so the complete code will be:
#IBAction func tapped(_ sender: Any) {
let configuration = DGTAuthenticationConfiguration(accountFields: .defaultOptionMask)
configuration?.appearance = DGTAppearance()
configuration?.appearance.backgroundColor = UIColor.white
configuration?.appearance.accentColor = UIColor.red
// Start the Digits authentication flow with the custom appearance.
Digits.sharedInstance().authenticate(with: nil, configuration:configuration!) { (session, error) in
if session != nil {
//Print Data
print(session?.phoneNumber)
print(session?.userID)
// Navigate to the main app screen to select a theme.
self.navigateToMainAppScreen()
} else {
print("Error")
}
}
}
Here is the Reference I have used:
https://docs.fabric.io/apple/examples/cannonball/index.html#sign-in-with-digits
var buttonTapPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .background).async {
do{
let correctSoundUrl: URL = URL(fileURLWithPath: Bundle.main.path(forResource: "buttonTap", ofType: "wav")!)
self.buttonTapPlayer = try AVAudioPlayer(contentsOf: correctSoundUrl)
self.buttonTapPlayer.prepareToPlay()
}
catch{}
}
}
#IBAction func buttonPressed(_ sender: UIButton) {
self.buttonTapPlayer.play()
performSegue(withIdentifier: "showDetail", sender: sender)
}
I have the above code in my swift project. My project crashes the very first time I run the project in simulator at self.buttonTapPlayer.play()
with the following error:
Thread1:EXC_BAD_ACCESS(code=1, address = 0X48)
However, when I re-run it, everything is fine. How do I resolve this?
You can't do it. You've build your buttonTapPlayer in a background thread and you try to call it from the main thread (to IBAction method). The compiler crashed to play() because your player is not yet ready.
To avoid this, you could build your player inside:
DispatchQueue.main.async {
// build your stuff to the main thread
}
or simply remove DispatchQueue.global(qos: .background).async {}
I'm trying to add a Rate App button on my iOS Application and I've looked everywhere online by trying different ways of doing it but none of them have worked.
Here's what I've tried
1.
let appId = 1040912970
#IBAction func btnRateApp(sender: UIButton) {
let url = "itunes.apple.com/app/id\(appId)"
UIApplication.sharedApplication().openURL(NSURL(string: url)!)
}
2.
let appId = 1040912970
#IBAction func btnRateApp(sender: UIButton) {
let urlString = "http://itunes.apple.com/app/id\(appId)?mt=8"
let url = NSURL(string: urlString)
UIApplication.sharedApplication().openURL(url!)
}
3.
#IBAction func btnRateApp(sender: UIButton) {
UIApplication.sharedApplication().openURL(NSURL(string: url)!)
UIApplication.sharedApplication().openURL(NSURL(string:"https://itunes.apple.com/us/app/wsw-song-chants/id1040912970?ls=1&mt=8")!);
}
4.
#IBAction func btnRateApp(sender: UIButton) {
UIApplication.sharedApplication().openURL(NSURL(string : "itms-apps://itunes.apple.com/app/id1040912970")!);
}
When do this one above and click the button, a message on the output console will say
ERROR: There is no registered handler for URL scheme itms-apps Message
from debugger: Terminated due to signal 15
I've also checked if my app Id is correct which shows on itunes connect.
First and foremost you must use store-kit in order to actually have in-app ratings.
Secondly check iRate GitHub Page
It's a very common requirement and this Library will handle it.
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.