I am trying to setup server using Vapor. As client, I have simple iOS app using NSUrlSession - URLSessionWebSocketTask. My question is... how I can set session.data from iOS app?
iOS App - Connect method
func connect(completion: #escaping ()->() = { }) {
guard webSocketTask == nil else { return }
self.username = "Name"
self.userID = UUID().uuidString
let url = URL(string: "ws://localhost:8080/connect")!
webSocketTask = URLSession.shared.webSocketTask(with: url)
webSocketTask?.receive(completionHandler: onReceive)
webSocketTask?.resume()
}
Vapor:
app.webSocket("connect") { request, ws in
let controller = Controller()
let userName = request.session.data["nickname"] ?? "Unknown user"
let data = request.session.data["data"] ?? "Empty Data"
controller.addUser(userName, with: room, withConnection: ws)
.....
....
...
..
.
You may use NSURLSessionDataTask.
https://developer.apple.com/documentation/foundation/nsurlsessiondatatask
Related
I wish to open app with the latest url viewd on a webview ios app.
Tryied this inside viewdidload()
var hasHistoryUrl = false
WebView.evaluateJavaScript("window.location.href") {
(result,error)->Void in if (result != nil){
hasHistoryUrl=true
}
}
if (hasHistoryUrl){
// let url = URL(string: historyUrl)
} else {
let url = URL(string: "https://www.url.com/")
let request = URLRequest(url: url!)
loadReq(request: request);
}
Not working on true device, on emulators allways opens with a clear cache.
When you open your app, all variable initialise and your previous data will be gone. So you need to save latest visited url in userdefaults as string like-
let url: String = "abcd.com"
UserDefaults.standard.set(url, forKey: "MyUrl")
And fetch url from userdefaults when you open the app as -
if let urlString = UserDefaults.string(forKey: ""MyUrl"") {
// Do stuff
}
In your code, insert it as-
WebView.evaluateJavaScript("window.location.href") {
(result,error)->Void in if (result != nil){
UserDefaults.standard.set(yourURL, forKey: "MyUrl")
}
}
if let urlString = UserDefaults.string(forKey: ""MyUrl"") {
// Do stuff
} else {
let url = URL(string: "https://www.url.com/")
let request = URLRequest(url: url!)
loadReq(request: request);
}
1- i try to create framework for my project but i didnt get true way to call my methods in another project inside appdelegate;
2- Using framework in another project App Transport Security warning !
my example framework codes under below;
myFramework.swift
import UIKit
var loginUrl = "http://bla/login.php"
let prefs = UserDefaults.standard
public class myFramework: NSObject {
public override init (){
print("Started.")
}
public func doSomething(){
print("works")
}
public func login(secret : String)
{
print("Login Request")
let post_data: NSDictionary = NSMutableDictionary()
post_data.setValue(secret, forKey: "secret")
let url:URL = URL(string: loginUrl)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
var paramString = ""
for (key, value) in post_data
{
paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
}
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["login"] as? NSDictionary
{
if let checksuccess = data_block["success"] as? Bool
{
if checksuccess == true {
if let getUserId = data_block["userId"] {
print("Service Connected")
print(getUserId)
prefs.set(secret, forKey: "secret")
prefs.set(getUserId, forKey: "userId")
}
}else{
if let getMessage = data_block["message"] {
print(getMessage)
}
}
DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
}
public func LoginDone()
{
}
}
Calling another project inside Appdelegate file.
import myFramework
myFramework().login(secret : "234234234234")
but i want to use `myframework without ()
must be;
myFramework.login(secret : "234234234234")
1- How can i do it?
(My all framework codes inside myFramework.swift)
2- When my framework using another project says me app App Transport Security warning , how can i fix it in my framework ? Warning message under below.
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file
Thank you !
In regards to your first question, if you are set on scoping your functions inside of a class, make your functions static or class functions like so:
class myFramework: NSObject {
public class func login(secret : String)
{
...
}
}
Now in code, you can call it like this:
import myFramework
myFramework.login("234234234234")
For your second question, see this post:
App Transport Security has blocked a cleartext HTTP resource
You don't need to put your functions inside a public class. Just make them public.
public func login() {
// insert code here
}
public func secondFunction() {
internalFunction()
}
internal func internalFunction() {
}
In your app:
include MyFramework
login()
secondFunction()
Note that you should name your frameworks like you do classes - capitalized.
You don't need the prefix your function calls with MyFramework.
Your app can see login() and secondFunction(), but cannot see internalFunction() as it's declared internal.
EDIT: Just saw your second question. Click on your framework target in the app explorer. Under General, DeploymentInfo, you'll see a checkbox labelled Allow App Extension API Only - check it. (I make this mistake often too!)
I have a login to an API which works and now want to load a web view which needs the authentication token. I'm testing my app using the Xcode Simulator for iOS 10.1.
When I load the web view I get a sign in page and a message from the API "It looks like you are not accepting cookies". Is this a case of mismatched cookies names expected or something I have missed?
func createWebViewRequest() {
self.createCookie()
let url = URL (string: "myDomain/info/wishlists")
let requestObj = NSMutableURLRequest(url: url!)
requestObj.httpShouldHandleCookies = true
let headers = HTTPCookie.requestHeaderFields(with: HTTPCookieStorage.shared.cookies!)
requestObj.allHTTPHeaderFields = headers
requestObj.addValue("iOS", forHTTPHeaderField: "X-My-Client")
self.webview!.loadRequest(requestObj as URLRequest)
self.webview!.delegate = self
self.view.addSubview(self.webview!)
}
func createCookie() {
let infoModel = UserInfoManager.getUserInfoModel()
if infoModel != nil {
let cookieProps = NSMutableDictionary()
cookieProps.setValue("NIDDEV", forKey: HTTPCookiePropertyKey.name.rawValue)
cookieProps.setValue(infoModel?.userAccessToken, forKey: HTTPCookiePropertyKey.value.rawValue)
cookieProps.setValue(".mydomain.com", forKey: HTTPCookiePropertyKey.domain.rawValue)
cookieProps.setValue("/", forKey: HTTPCookiePropertyKey.path.rawValue)
cookieProps.setValue("0", forKey: HTTPCookiePropertyKey.version.rawValue)
cookieProps.setValue(Date().addingTimeInterval(31536000), forKey: HTTPCookiePropertyKey.expires.rawValue)
let dict:NSDictionary = cookieProps as NSDictionary
let cookie = HTTPCookie(properties: dict as! [HTTPCookiePropertyKey : Any])
HTTPCookieStorage.shared.setCookie(cookie!)
}
else {
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate.callAuthTokenWebservice()
}
}
I dumped the create cookie method above and decided to save the cookies using the response header for the immediately prior check auth call which comes from an API and authenticates in the header. This stopped the target web site from complaining about not getting cookies but it still wouldn't accept the auth key.
private func setCookies(response: URLResponse) {
if let httpResponse = response as? HTTPURLResponse {
if let headerFields = httpResponse.allHeaderFields as? [String: String] {
let _ = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: response.url!)
// print(cookies)
}
}
}
I'm building a basic iOS app with Xcode that mainly just contains a webview with my web app inside.
I was wondering if there was a decent way to save the users username to the devices storage when logging in so that it can be automatically entered when opening the app next time. Since the app is a webview, I don't believe there is a way to keep the user logged in (like other major apps do, such as Facebook), so I think that auto filling the username will be beneficial for them.
I found this question and answer that could possibly solve my problem, although it's in good ol' Objective C.
My current attempt, that does absolutely nothing:
let savedUsername = "testusername"
let loadUsernameJS = "document.getElementById(\"mainLoginUsername\").value = " + savedUsername + ";"
self.Webview.stringByEvaluatingJavaScriptFromString(loadUsernameJS)
Is this a possibility with Swift?
for storing the password you should use the keychain, specifically web credentials. if done right, this will allow your app to use any existing keychain entries entered via Safari and will also allow Safari to access the password if saved via your app.
Code for setting and retrieving provided below:
private let domain = "www.youdomain.com"
func saveWebCredentials(username: String, password: String, completion: Bool -> Void) {
SecAddSharedWebCredential(domain, username, password) { error in
guard error == nil else { print("error saving credentials: \(error)"); return completion(false) }
completion(true)
}
}
func getExistingWebCredentials(completion: ((String, String)?, error: String?) -> Void) {
SecRequestSharedWebCredential(domain, nil) { credentials, error in
// make sure we got the credentials array back
guard let credentials = credentials else { return completion(nil, error: String(CFErrorCopyDescription(error))) }
// make sure there is at least one credential
let count = CFArrayGetCount(credentials)
guard count > 0 else { return completion(nil, error: "no credentials stored") }
// extract the username and password from the credentials dict
let credentialDict = unsafeBitCast(CFArrayGetValueAtIndex(credentials, 0), CFDictionaryRef.self)
let username = CFDictionaryGetValue(credentialDict, unsafeBitCast(kSecAttrAccount, UnsafePointer.self))
let password = CFDictionaryGetValue(credentialDict, unsafeBitCast(kSecSharedPassword, UnsafePointer.self))
// return via completion block
completion((String(unsafeBitCast(username, CFStringRef.self)), String(unsafeBitCast(password, CFStringRef.self))), error: nil)
}
}
which is used like this:
// save the credentials
saveWebCredentials("hello", password: "world", completion: { success in
// retrieve the credentials
getExistingWebCredentials { credentials, error in
guard let credentials = credentials else { print("Error: \(error)"); return }
print("got username: \(credentials.0) password: \(credentials.1)")
}
})
UPDATE
Recommend switching to using a WKWebView so you can easily pull out the response headers. Here is boilerplate code:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let webView = WKWebView(frame: self.view.bounds)
webView.navigationDelegate = self
self.view.addSubview(webView)
webView.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.google.com")!))
}
func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {
// make sure the response is a NSHTTPURLResponse
guard let response = navigationResponse.response as? NSHTTPURLResponse else { return decisionHandler(.Allow) }
// get the response headers
let headers = response.allHeaderFields
print("got headers: \(headers)")
// allow the request to continue
decisionHandler(.Allow);
}
}
You code is not working because you did not wrap savedUsername with quotes.
You should have this instead:
let loadUsernameJS = "document.getElementById(\"mainLoginUsername\").value = \"\(savedUsername)\";"
Also, this library might help you.
You are not passing a string to JavaScript, you should encapsulate the variable in additional quotes
let loadUsernameJS = "document.getElementById(\"mainLoginUsername\").value = \"" + savedUsername + "\";"
or
let loadUsernameJS = "document.getElementById('mainLoginUsername').value = '" + savedUsername + "';"
I have an application that allow users to stream songs from spotify. So to achieve that I need to renew session from time to time whenever users want to stream song from spotify. I'm using latest spotify sdk (beta-9), and I'm currently following tutorial from https://www.youtube.com/watch?v=GeO00YdJ3cE. In that tutorial we need to refresh token swap but when I looked from https://developer.spotify.com/technologies/spotify-ios-sdk/tutorial/ there is no need to refresh token swap.
and I end up not using the token swap, when I refresh my session then play song with renewed session, I got below error:
Error Domain=com.spotify.ios-sdk.playback Code=8 "Login to Spotify failed because of invalid credentials." UserInfo=0x7f840bf807b0 {NSLocalizedDescription=Login to Spotify failed because of invalid credentials.}
And I'm using this code below, for renewing my session:
let userDefaults = NSUserDefaults.standardUserDefaults()
if let sessionObj : AnyObject = NSUserDefaults.standardUserDefaults().objectForKey("spotifySession") {
let sessionDataObj : NSData = sessionObj as! NSData
let session = NSKeyedUnarchiver.unarchiveObjectWithData(sessionDataObj) as! SPTSession
self.playUsingSession(session)
if !session.isValid() {
SPTAuth.defaultInstance().renewSession(session, callback: { (error : NSError!, newsession : SPTSession!) -> Void in
if error == nil {
let sessionData = NSKeyedArchiver.archivedDataWithRootObject(session)
userDefaults.setObject(sessionData, forKey: "spotifySession")
userDefaults.synchronize()
self.session = newsession
self.playUsingSession(newsession)
}else{
println("renew session having problerm >>>>> \(error)")
}
})
}else{
println("session is still valid")
self.playUsingSession(session)
}
}else{
spotifyLoginButton.hidden = false
}
and below code to stream spotify songs:
func playUsingSession(sessionObj:SPTSession!){
if spotifyPlayer == nil {
spotifyPlayer = SPTAudioStreamingController(clientId: kSpotifyClientID)
}
spotifyPlayer?.loginWithSession(sessionObj, callback: { (error : NSError!) -> Void in
if error != nil {
println("enabling playback got error : \(error)")
return
}
var spotifyTrackUri : NSURL = NSURL(string: "spotify:track:3FREWTEY2uFxOorJZMmZPX")!
self.spotifyPlayer!.playURIs([spotifyTrackUri], fromIndex: 0, callback: { (error : NSError!) -> Void in
if error != nil {
println("\(error)")
}
})
})
}
Do I still need to refresh token swap for latest sdk? Or is there something missing with my code?
By default, users need to login once per hour for apps using the Spotify SDK unless you use the Authorization Code flow. To use this flow you'll need to setup a server to handle token swap and refresh.
Setup a free server with this one-click-deploy to Heroku https://github.com/adamontherun/SpotifyTokenRefresh
Using the URL of the server created above add the following when configuring your SPTAuth.defaultInstance():
SPTAuth.defaultInstance().tokenSwapURL = URL(string: "https://YOURSERVERNAME.herokuapp.com/swap")
SPTAuth.defaultInstance().tokenRefreshURL = URL(string: "https://YOURSERVERNAME.herokuapp.com/refresh")
Before using your session check if it is valid:
if SPTAuth.defaultInstance().session.isValid()
and if it isn't call
SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session, callback: { (error, session) in
if let session = session {
SPTAuth.defaultInstance().session = session
}
})
I recommend follow this tutorial: https://medium.com/#brianhans/getting-started-with-the-spotify-ios-sdk-435607216ecc and this one too: https://medium.com/#brianhans/spotify-ios-sdk-authentication-b2c35cd4affb
After you finished, you'll see that you create a file called "Constants.swift" just like this:
import Foundation
struct Constants {
static let clientID = "XXXXXXXXXXXXXXXXXXXX"
static let redirectURI = URL(string: "yourappname://")!
static let sessionKey = "spotifySessionKey"
}
Then, you can follow the steps in Heroku (Do not enter in panic is very simple):
https://github.com/adamontherun/SpotifyTokenRefresh
almost ready, when your server is "working", come back to your Xcode Project and add two static constants in your "Constants.swift" file, just like this:
import Foundation
struct Constants {
static let clientID = "XXXXXXXXXXXXXXXXXXXX"
static let redirectURI = URL(string: "yourappname://")!
static let sessionKey = "spotifySessionKey"
static let tokenSawp = URL(string: "https://yourappname.herokuapp.com/swap")
static let tokenRefresh = URL(string:"https://yourappname.herokuapp.com/refresh")
}
To finish, go to AppDelegate.swift and search "func setupSpotify()".. Add the new two constants, your function should look like this:
func setupSpotify() {
SPTAuth.defaultInstance().clientID = Constants.clientID
SPTAuth.defaultInstance().redirectURL = Constants.redirectURI
SPTAuth.defaultInstance().sessionUserDefaultsKey = Constants.sessionKey
SPTAuth.defaultInstance().tokenSwapURL = Constants.tokenSawp //new constant added
SPTAuth.defaultInstance().tokenRefreshURL = Constants.tokenRefresh //new constant added
SPTAuth.defaultInstance().requestedScopes = [SPTAuthStreamingScope]
do {
try SPTAudioStreamingController.sharedInstance().start(withClientId: Constants.clientID)
} catch {
fatalError("Couldn't start Spotify SDK")
}
}
As a last step, just add the SPTAuth.defaultInstance().renewSession in you signInSpotify function, should look like this:
#IBAction func SignInSpotify(_ sender: Any) {
if SPTAuth.defaultInstance().session == nil {
let appURL = SPTAuth.defaultInstance().spotifyAppAuthenticationURL()
let webURL = SPTAuth.defaultInstance().spotifyWebAuthenticationURL()!
// Before presenting the view controllers we are going to start watching for the notification
NotificationCenter.default.addObserver(self,
selector: #selector(receievedUrlFromSpotify(_:)),
name: NSNotification.Name.Spotify.authURLOpened,
object: nil)
if SPTAuth.supportsApplicationAuthentication() {
UIApplication.shared.open(appURL!, options: [:], completionHandler: nil)
} else {
let webVC = SFSafariViewController(url: webURL)
present(webVC, animated: true, completion: nil)
}
} else if SPTAuth.defaultInstance().session.isValid() == true {
print("YOUR SESSION IS VALID")
self.successfulLogin()
} else {
print("YOUR SESSION IS NOT VALID / NEED RENEW")
//Every 60 minutes the token need a renew https://github.com/spotify/ios-sdk
SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session, callback: { (error, session) in
if let session = session {
SPTAuth.defaultInstance().session = session
self.successfulLogin()
print("RENEW OK")
}
if let error = error {
print("RENEW NOT OK \(error)")
}
})
}
}
Good Luck!