I made app which login in SFSafariViewController. It works well when login. but after I logout if I try to login it again, I'm already logged in. This is because my automatic login information is stored in SFSafariViewController. I tried to delete cache and cookies but it doesn't work. :(
this is my login code.
func requestCode() {
let scope = "user"
let urlString = "https://github.com/login/oauth/authorize?client_id=\(clientId)&scope=\(scope)"
guard let url = URL(string: urlString) else {
return
}
URLCache.shared.removeAllCachedResponses()
if let cookies = HTTPCookieStorage.shared.cookies {
for cookie in cookies {
HTTPCookieStorage.shared.deleteCookie(cookie)
}
}
let safariViewController = SFSafariViewController(url: url)
safariViewController.delegate = self
safariViewController.modalPresentationStyle = .automatic
if let topViewController = UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).flatMap({ $0.windows }).first(where: { $0.isKeyWindow }) {
topViewController.rootViewController?.present(safariViewController, animated: true, completion: nil)
}
}
prefersEphemeralWebBrowserSession = true
it makes your browser to private mode!!!
so you don't need to logout.
Related
I've those methods
func runOauth(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.oauth2!.afterAuthorizeOrFail = self.callBackOAuth
var url:URL?
do{
url = try appDelegate.oauth2!.authorizeURL(withRedirect:"kronos://oauth/callback", scope: "auth",params: ["tg":"addon/kronos/main","idx":"login.OAuth","formId":"iOS"])
do{
let authorizer = appDelegate.oauth2!.authorizer as! OAuth2Authorizer
safariVC = try authorizer.authorizeSafariEmbedded(from: self,at: url!)
}
/*catch error*/
}
}
func callBackOAuth(authParameters:OAuth2JSON!, error: OAuth2Error!){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
if (error == nil && appDelegate.oauth2!.accessToken != nil){//OAuth succeed in
self.keychain!.set(appDelegate.oauth2!.accessToken!,forKey:"Token")
appDelegate.reloadView()
}else {/*handle errors*/}
}
and in AppDelegate
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
let site=components?.host
if site == "oauth"{//OAuth terminated
if components?.path == "/callback" {
let viewController = self.window?.rootViewController as! ViewController
self.oauth2!.handleRedirectURL(url)
viewController.hideSafariView()
}
}
return true
}
My issue is that as I trigger runOauth like that it happens that application is called before callBackOAuth so after oauth viewDidAppear is recalled but with keychain token not set, so here is a way to "wait" in application that token is not nil
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//initialize OAuth2 config parameters
appDelegate.oauth2 = OAuth2CodeGrant(settings: OAuthParams )
appDelegate.oauth2!.authConfig.authorizeContext = KronosWebsite?.window
appDelegate.oauth2!.useKeychain = false
appDelegate.oauth2!.authConfig.authorizeEmbeddedAutoDismiss = true
appDelegate.oauth2!.logger = OAuth2DebugLogger(.debug)
appDelegate.oauth2!.afterAuthorizeOrFail = self.callBackOAuth
let token=self.keychain!.get("Token")
if(token == nil){//no token found, we launch the OAuth
runOauth()
}
}
EDIT:
I've tried to use a DispatchGroup with no success:
groupOauth.enter() in runOauth
groupOauth.leave() in callBackOAuth
and in AppDelegate::application
viewController.groupOauth.notify(queue: DispatchQueue.main) {
self.oauth2!.handleRedirectURL(url)
viewController.hideSafariView()
}
I've a solution by deporting the call of the URL which logs the user (with the Oauth token stored in keychain) from viewDidAppear to viewDidLoad
I have this in my Appdelegate.swift
case .reddit:
if (url.host == "oauth-callback") {
AuthorizationManager.removeRedditToken()
print("Oauth Request..")
let urlc = URLComponents(url: url, resolvingAgainstBaseURL: false)
let bearerToken = urlc?.queryItems?.first(where: { $0.name == "code" })?.value
let tokenPromise = TokenManager.sharedInstance.fetchAccesTokenPromise(withBearerToken: bearerToken ?? "")
tokenPromise.done { (token) in
TokenManager.sharedInstance.accessToken = token.accessToken
TokenManager.sharedInstance.refrestToken = token.refreshToken
print("Access Token")
print(token.accessToken)
print("_______")
print(token.refreshToken)
AuthorizationManager.saveRedditToken(token: token.accessToken)
AuthorizationManager.saveRedditRefreshToken(token: token.refreshToken!)
}.catch { (error) in
print(error)
}
And I want to close the web view that opens during the open url function, but I can't figure out how to do it. The only way the Web View dismisses is by pressing "Done". It should close when I receive successfully the token. Dunno what can I do
The dynamic links used to be working fine both on actual device, and also in the simulator. When I click on the link in the simulator (or pasting it in safari), I get redirected to the ".page.link" site where it says "open" - and clicking open opens the app perfectly. However, if I repeat the same steps on my actual device, I get redirected to the support url, and nothing happens (app doesn't open even though its installed on my phone).
This is my code in where I display a url
if let uid = Auth.auth().currentUser?.uid {
guard let link = URL(string: "https://www.testapp.com/uid=" + uid) else {
return
}
let dynamicLinksDomain = "testapp.page.link"
let linkBuilder = DynamicLinkComponents(link: link, domain: dynamicLinksDomain)
linkBuilder.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.burgertralla.theGreat")
linkBuilder.shorten { (url, _, _) in
if let url = url {
self.shareTopLabel.text = url.absoluteString
}
}
}
And this is my AppDelegate:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
self.handleIncomingDynamicLink(dynamicLink: dynamicLink)
return true
}
return false
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
if let incomingUrl = userActivity.webpageURL {
let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingUrl, completion: { (dynamicLink, _) in
if let dynamicLink = dynamicLink, let _ = dynamicLink.url {
self.handleIncomingDynamicLink(dynamicLink: dynamicLink)
}
})
if linkHandled {
return linkHandled
}
}
return false
}
func handleIncomingDynamicLink(dynamicLink: DynamicLink) {
guard let pathComponents = dynamicLink.url?.pathComponents else {
return
}
for pathComponent in pathComponents {
if pathComponent.contains("uid=") {
let uid = pathComponent.drop(prefix: "uid=")
Database.database().reference(withPath: "profile/" + uid).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists(), var data = snapshot.value as? [String: Any] {
data["uid"] = snapshot.key
let userProfileVC = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "UserProfileViewController") as! UserProfileViewController
if let tabBarVc = self.window?.rootViewController as? UITabBarController {
tabBarVc.selectedIndex = 1
if let discoveryVC = tabBarVc.viewControllers?[1] as? UINavigationController {
userProfileVC.selectedUser = data
discoveryVC.pushViewController(userProfileVC, animated: true)
}
}
}
}
}
}
}
Do anyone have any clue on whats going on here?
Thanks for the help guys :)
EDIT : It does work on my iPhone with Chrome, however not with Safari. I don't know why...
I am using Alamofire for my requests and I get cookies in some of them, everything works fine when I launch the app and use it but when I kill the app and reopen the cookies are not there anymore. I searched a lot and found this but none of the answers helped.
I try to save the cookies after each request and load them before sending request as below:
func saveCookies(response: DataResponse<Any>) {
let headerFields = response.response?.allHeaderFields as! [String: String]
let url = response.response?.url
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url!)
var cookieArray = [[HTTPCookiePropertyKey: Any]]()
for cookie in cookies {
cookieArray.append(cookie.properties!)
}
UserDefaults.standard.set(cookieArray, forKey: "savedCookies")
UserDefaults.standard.synchronize()
}
func loadCookies() {
guard let cookieArray = UserDefaults.standard.array(forKey: "savedCookies") as? [[HTTPCookiePropertyKey: Any]] else { return }
for cookieProperties in cookieArray {
if let cookie = HTTPCookie(properties: cookieProperties) {
HTTPCookieStorage.shared.setCookie(cookie)
}
}
}
But still when I kill the app, I can't get the data.
Try to save your cookies in UserDefaults like this:
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as! String
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
I am developing an App for the iPhone using Xwebview which enables me to download a page then interact with the javascript on the downloaded page.
All works, but if the internet connection drops, a default local page is loaded, informing the user there is no internet connection. The page displays a retry button that, when pressed checks, the internet connection: if the connection is made the app tries to connect again to the external page and load the page into the webview.
I cannot get this to work: the code downloads the page (I can see this in my session data) but I can't get that page to load back into the webview.
override func viewDidLoad() {
super.viewDidLoad()
login()
}
func login()
{
// *********** Get stored hashkey **************
let hashcode = getHashcode()
// ********** Check network connection *********
let netConnection = Connection.isConnectedToNetwork()
print("net connection: ", netConnection)
if netConnection == true
{
if hashcode != "00000"
{
print("local key found", hashcode)
// We dont have local key
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
//webview.loadRequest(NSURLRequest(URL: NSURL(string: "about:blank")!))
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let url:NSURL = NSURL(string: serverLocation + onlineLoginApi)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let paramString = "/?username=username&password=password"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.downloadTaskWithRequest(request) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let urlContents = try! NSString(contentsOfURL: location!, encoding: NSUTF8StringEncoding)
guard let _:NSString = urlContents else {
print("error")
return
}
print(urlContents)
}
task.resume()
// you must tell webview to load response
webview.loadRequest(request)
}
else{
print("local key found", hashcode)
// ********* Found local key go to site pass key over ************
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let req = NSMutableURLRequest(URL: NSURL(string:serverLocation + onlineLoginApi + "?hashcode=\(hashcode)")!)
req.HTTPMethod = "POST"
req.HTTPBody = "/?hashcode=\(hashcode)".dataUsingEncoding(NSUTF8StringEncoding)
NSURLSession.sharedSession().dataTaskWithRequest(req)
{ data, response, error in
if error != nil
{
//Your HTTP request failed.
print(error!.localizedDescription)
} else {
//Your HTTP request succeeded
print(String(data: data!, encoding: NSUTF8StringEncoding))
}
}.resume()
webview.loadRequest(req)
}
}
else{
// No connection to internet
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let root = NSBundle.mainBundle().resourceURL!
let url = root.URLByAppendingPathComponent("/www/error-no-connection.html")
webview.loadFileURL(url, allowingReadAccessToURL: root)
print("No internet connection")
}
}
class jsapi: NSObject {
// Reconnect button on interface
func retryConnection()
{
print("Reconnect clicked")
dispatch_async(dispatch_get_main_queue())
{
let netConnections = Connection.isConnectedToNetwork()
if netConnections == true {
let netalert = UIAlertView(title: "Internet on line", message: nil, delegate: nil, cancelButtonTitle: "OK")
netalert.show()
let url = self.serverLocation + self.onlineLoginApi
let hashcode = ViewController().getHashcode()
if(hashcode != "00000") {
let url = url + "?hashcode=\(hashcode)"
print("url: ", url)
}
ViewController().loadPagelive(url)
}
else{
let netalert = UIAlertView(title: "Internet off line", message: nil, delegate: nil, cancelButtonTitle: "OK")
netalert.show()
}
}
print("retryConnect end")
}
}
You try to perform the loadPagelive(url) on a new instance of your ViewController, not on the current one shown on the screen, that's why you don't see any update.
You should create a delegate or a completion block in order to execute code on you ViewController instance loaded on the screen: every time you do ViewController(), a new object is created.
You can try using the delegate pattern, which is simple to achieve. I will try to focus on the important part and create something that can be used with your existing code:
class ViewController: UIViewController {
let jsapi = jsapi() // You can use only 1 instance
override func viewDidLoad() {
super.viewDidLoad()
// Set your ViewController as a delegate, so the jsapi can update it
jsapi.viewController = self
login()
}
func loadPagelive(_ url: URL) {
// Load page, probably you already have it
}
}
class jsapi: NSObject {
weak var viewController: ViewController?
func retryConnection() {
// We check if the delegate is set, otherwise it won't work
guard viewController = viewController else {
print("Error: delegate not available")
}
[... your code ...]
// We call the original (and hopefully unique) instance of ViewController
viewController.loadPagelive(url)
}
}