I'm making an app that's meant to automatically log into school internet, and I'm trying to make it so that you save the credentials in the main app, and the helper app gets registered to launch at login, which works.
However, when the helper app opens from logging in, it doesn't send a web request to log into the internet, but when I open the helper app manually, it does. Keep in mind that it's meant to send a request whenever the internet status is changed to "connected"
(it's also meant to send the request every time you're connected to internet, but it only fires once)
import Cocoa
import Network
#main
class AppDelegate: NSObject, NSApplicationDelegate {
let defaults = UserDefaults.init(suiteName: "replaced userdefaults group name thing")
func applicationDidFinishLaunching(_ aNotification: Notification) {
let usernamestored = defaults!.string(forKey: "username")!
let passwordstored = String(decoding: kread(service: "detnsw-autologin", account: usernamestored)!, as: UTF8.self) // kread() refers to a function i have in another file for reading values from keychain
let url = URL(string:"the login page url")
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let poststring = "csrfmiddlewaretoken=&username=\(usernamestored)&password=\(passwordstored)"
request.httpBody = poststring.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Error took place \(error)")
return
}
if let data = data, let _ = String(data: data, encoding: .utf8) {
//print("Response data string:\n \(dataString)")
//print(response.statusCode)
}
}
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
task.resume() // this is supposed to fire every time the app is connected to the internet
}
}
let queue = DispatchQueue(label: "Monitor")
monitor.start(queue: queue)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
I've fixed the issue by putting all of the code that loads the username and password into the function that runs when the internet is connected, so the final code looks something like this, instead of the code being outside of the function so that it only loads once.
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
let usernamestored = defaults!.string(forKey: "username")!
...
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in {...}
task.resume()
}
}
Related
I'm trying to do the unit tests for my app.
I've this function preparing the request
func getWeatherDataAtLocation() {
let WEATHER_URL = "http://api.openweathermap.org/data/2.5/weather"
let weatherAPI = valueForAPIKey(named:"weatherAPI")
let lat = String(locationService.latitude)
let lon = String(locationService.longitude)
do {
try networkService.networking(url: "\(WEATHER_URL)?APPID=\(weatherAPI)&lon=\(lon)&lat=\(lat)", requestType: "weather")
} catch let error {
print(error)
}
}
I've a service class networkservice processing the network request :
class NetworkService {
var weatherDataDelegate: WeatherData?
var session: URLSession
init(session: URLSession = URLSession(configuration: .default)) {
self.session = session
}
func networking(url: String, requestType: String) {
var request = URLRequest(url: requestUrl)
request.httpMethod = "GET"
var task: URLSessionDataTask
task = session.dataTask(with: request) { (data, response, error) in
switch requestType {
case "weather":
do {
let weatherJSON = try JSONDecoder().decode(WeatherJSON.self, from: data)
self.weatherDataDelegate?.receiveWeatherData(weatherJSON)
} catch let jsonErr {
print(jsonErr)
}
case // Other cases
default:
print("error")
}
}
task.resume()
}
}
Then i've the delegate running this function to update the JSON received
func receiveWeatherData(_ data: WeatherJSON) {
self.dataWeather = data
do {
try updateWeatherDataOnScreen()
} catch let error {
print(error)
}
}
The issue is I've no idea how I can write some code to test this and all the ressources I find is to test with a callback, any idea?
So there are mutliple steps in this.
1: Create a mocked version of the response of exactly this request. And save it in a json file. Named like weather.json
2: Once you have done that you want to add an #ifdef testSchemeName when executing request. And tell it to tell your function called networking() to read from a file named "\(requestType).json" instead of making the request.
Optional, more advanced way:
This actually intercepts your request and send you the file data instead. A bit more advanced, but your testing gets 1 level deeper.
I'm modifying code from 'Hacking with swift' Project 7 to take a JSON file using an API and placing it in a table view
I'm at a bit of loss of what to do next, tried moving around the call to the parse function and using the commented out code
override func viewDidLoad() {
super.viewDidLoad()
let username = "UserName"
let password = "Password"
let loginData = String(format: "%#:%#", username,
password).data(using: String.Encoding.utf8)!
let base64LoginData = loginData.base64EncodedString()
let url = URL(string: "......")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Basic \(base64LoginData)", forHTTPHeaderField:
"Authorization")
let task = URLSession.shared.dataTask(with: request) { data,
response,
error in
guard let data = data, error == nil else {
print("error")
return
}
if let httpStatus = response as? HTTPURLResponse {
parse(json: data)
print("status code = \(httpStatus.statusCode)")
}
}
task.resume()
}
// if let url = URL(string: urlstring){
// if let data = try? Data(contentsOf: url){
// parse(json: data)
// return
// }
// }
// showError()
//}
func parse(json: Data) {
let decoder = JSONDecoder()
if let jsonPetitions = try? decoder.decode(Petitions.self, from:
json) {
petitions = jsonPetitions.results
tableView.reloadData()
}
}
I receive a status code of '200' so I know the API call works fine.
The issue seems to be with calling the parse function I get the
following error "Call to method 'parse' in closure requires explicit
'self.' to make capture semantics explicit"
1- The error means to add self here
self.parse(json: data)
2- You should reload the table in main thread as callback of URLSession.shared.dataTask runs in a background thread to avoid un-expected results/crashes
DispatchQueue.main.async {
self.tableView.reloadData()
}
I am developing a MacOS app that has a login page. When the user pressed the login button i need to send a post request and if the response is code is 200 then i need to preform a segue.
I am running into an issue where the segue is occurring no matter what i try
I have tried using the IBAction for a button then calling preform segue however that resulted in a thread problem. I have now put everything in shouldPerformSegue
override func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?) -> Bool {
if emailTextField.stringValue.isEmpty || passwordTextField.stringValue.isEmpty {
instructionText.stringValue = "Email and Password Required"
return false
}
let emailPassword = "email="+emailTextField.stringValue+"&password="+passwordTextField.stringValue
print("before post")
let data = emailPassword.data(using: String.Encoding.ascii, allowLossyConversion: false)
let url = URL(string: "http://127.0.0.1:50896/api/v1/auth")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = data
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
if let response = response as? HTTPURLResponse {
print("statusCode: \(response.statusCode)")
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("data: \(dataString)")
}
}
}
task.resume()
return true
}
I would like to complete the post request, check response code then preform segue if the code is 200
The problem is - The task.resume() is asynchronous; its result is therefore useless, because you are already returning from shouldPerformSegue() with a true value. What that essentially means is that the task is executed sometime AFTER you said "It's ok to perform a segue". Instead, call the task from the buttons IBAction, and perform segue in 200 status code section. Good luck!
Edit: The thread problem with the IBAction is probably because you are doing main-thread stuff on an off-thread (UI updates, performSegue, ...). Check out In Swift how to call method with parameters on GCD main thread?
One way of doing it is using with completion callback using closures.
func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?,OnSucess sucess:#escaping(Bool)->Void){
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("error: \(error)")
sucess(false)
} else
{
if let response = response as? HTTPURLResponse {
print("statusCode: \(response.statusCode)")
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("data: \(dataString)")
}
sucess(true)
}
}
}
let identiferStory: NSStoryboardSegue.Identifier = "main"
shouldPerformSegue(withIdentifier: identiferStory, sender: nil) { (isSucess) in
if isSucess == true{
}
else{
}
}
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)
}
}
I am trying to write a basic function to make an HTTP Get request and parse the xml data that comes back. I already have completed the section for parsing XML from a local file, but i can't seem to get any data from the server. I tested this code below, but the completion block does not even run, for information to be passed back from the server. Any suggestions please.
func getDatafromURL (url: String) {
guard let urlforRequest = NSURL(string: url) else {
print("Error: cannot create URL")
return
}
let request = NSURLRequest(URL: urlforRequest)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
print(response)
print(error)
}
task.resume()
}