POST data while when getting JSON with SwiftyJSON - ios

I'm developing an iOS app with Xcode and Swift.
I'm getting JSON data with SwiftyJSON.swift and the following code:
import UIKit
class ViewController: UIViewController {
var dict = NSDictionary()
#IBOutlet weak var firstLb: UILabel!
#IBOutlet weak var secondLb: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "http://example.com/showUserInfo.php"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = JSON(data: data)
print(json)
let f = json[0]["name"].stringValue
let s = json[0]["age"].stringValue
firstLb.text = f
secondLb.text = s
}
}
}
}
That works fine but I want to be able to post to my PHP script. (My PHP script is ready for it. It can receive POST data and handle it.)
I normally use the following code to POST data to PHP:
let request = NSMutableURLRequest(URL: NSURL(string: "http://example.com/showUserInfo.php")!)
request.HTTPMethod = "POST"
let postString = "username=MyUserName"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
That means: I know how to get data with JSON and I know how to POST a value to PHP. But unfortunately I'm not able to combine both.
Does anybody know how to combine them? How to POST a value to a PHP script where getting the JSON data?

To post data to Server you should use In-build APIs, or you can use a very famous library called : Alamofire
Sample Code:
let params:[String:AnyObject] = [
"foo" : 1,
"bar" : 2
]
request(.POST, "URL", parameters: params)
.responseJSON { response in
switch response.result {
case .Success(let JSON):
print("JSON: \(JSON)")
case .Failure(let error):
print("Request failed with error: \(error)")
}
}

Related

Display data from POST request Swift

New to swift and wanted to display some data from a post request each time a user presses a button.
Class and struct for Post request:
import Foundation
struct Response: Codable, Identifiable{
var id = UUID()
var model = String()
var choices = String()
var generatedtext: [GeneratedText]
}
struct GeneratedText: Codable{
var finish_reason = String()
var index = Int()
var logprobs = String()
var text = String()
}
class GPT3TextComepletion: ObservableObject{
#Published var response = [Response]()
func textCompletion(promptText:String, completion:#escaping ([Response]) -> () ){
let token = "redacted"
let url = URL(string: "redacted URL")!
// prepare json data
var json: [String: Any] = [
"temperature": 0.8,
"max_tokens": 64,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
"stop": ["\n\n"]]
json["prompt"] = promptText
let jsonData = try? JSONSerialization.data(withJSONObject: json)
// create post request
var request = URLRequest(url: url)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue( "Bearer \(token)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
//TODO: Remove this once i get the response to display
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
let response = try! JSONDecoder().decode([Response].self, from: data)
DispatchQueue.main.async {
completion(response)
}
}
task.resume()
}
}
View:
Removed the unnecessary styling and other code from the below view:
#State var prompt: String = "This is some editable text..."
#State var response = [Response]()
utton(action: {GPT3TextComepletion().textCompletion(promptText: prompt, completion: {(response) in self.response = response })})
List (response){ response in
VStack{
Text(response.model)}}
However upon running the emulator and attempting the post call there seems to be an issue with how i am decoding the data and most likely how i have organized my structs, error message:
"Expected to decode Array but found a dictionary instead."
JSON:
["id": cmpl-4PCJZrxEm8YlCwFSTZlfLpnAUcMPl, "created": 1641910885, "object": text_completion, "model": davinci:2020-05-03, "choices": <__NSSingleObjectArrayI 0x6000012f1dc0>(
{
"finish_reason" = stop;
index = 0;
logprobs = "<null>";
text = "";
}
)
]
Obviously i am doing something wrong with how i am defining my struct however I have tried a number of different things with no success over the last few days.
Two main questions:
Is there an alternate way to display response of the post request without using List and making the struct identifiable?
What I am doing wrong with my structs here?
Thanks!

How to use access token from response using swift 3

So am making a login application that sends username and password, and the server replies with access token....am trying to save this token for later use but i really cant. here is my code, and the server response incase you need it
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var PhoneNumber: UITextField!
#IBOutlet weak var Password: UITextField!
//***************************************LogIn**********************************************\\
#IBAction func Login(_ sender: UIButton) {
guard let url = URL(string: "https://goollyapp.azurewebsites.net/Token") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
let postString = "username=" + PhoneNumber.text! + "&password="+Password.text!+"&grant_type=password"
print(postString)
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let response = response {
print(response)
}
}
task.resume()
}
}
Where you declare the json variable replace with:
let json = try? JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
Then you can access the token with:
let token = json["access_token"] as! String
(after receiving a successful JSON response)
You can then implement a Singleton as follows:
class Token {
// Can't init a singleton
private init() { }
static let sharedInstance = Token()
var tokenString = ""
}
If you want to save the token in a variable for using it later , try this
(key being the corresponding name of the key in your response object)
let token = httpResponse?.allHeaderFields["SECURITY-TOKEN"] as? String
let task = URLSession.shared.dataTask(with: request) { data, response, error in
let httpResponse = response as? HTTPURLResponse
let token = httpResponse?.allHeaderFields["SECURITY-TOKEN"] as? String
print(token)
}
task.resume()

Saving username information to a server instead of NSDefaults (iOS)

I have a server for storing username and password data in my application. When testing the app I had everything save to the device locally using NSDefaults, but now that the app is close to being fully launched, I am trying to save them to the server instead, as it is safer that way for the user's information.
When I had it save to NSDefaults, it was easy and short work. Now however, I am trying to POST the data to the server and keep getting build errors. What do I need to change for this to work? Am I not fully understanding how POST and GET works? Thanks. Using Swift 2 as of right now, not my choice, I prefer 3, but my boss isn't letting us update it yet.
The current error is coming from the POST USER DATA TO SERVER section, where xcode claims that userNmeTxt cannot be converted into NSData. Thank you in advance.
EDIT: Error is on line 87: "Cannot convert value of type UITextField! to expected argument type NSData!"
import UIKit
class UserNameViewController: AuthorizationViewController {
#IBOutlet weak var userNameTxt: UITextField!
#IBOutlet weak var continueBtn: UIButton!
var userModel: ProfileModel!
//MARK: - SYSTEMS METHODS
override func viewDidLoad() {
super.viewDidLoad()
userNameTxt.delegate = self
userNameTxt.autocapitalizationType = .Sentences
setEnabledButton()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = false
self.navigationItem.leftBarButtonItem = getBackButton()
self.title = ""
}
override func viewWillDisappear(animated: Bool) {
self.navigationController?.navigationBarHidden = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
continueBtn.layer.cornerRadius = 10
}
override func popToRoot(sender:UIBarButtonItem){
self.navigationController!.popViewControllerAnimated(true)
}
//MARK: - CHECK FOR AVALABILITY
func setEnabledButton(){
if userNameTxt.text == "" {
continueBtn.backgroundColor = UIColor.lightGrayColor()
} else {
continueBtn.backgroundColor = UIColor(colorLiteralRed: 63.0/255.0, green: 220.0/255.0, blue: 236.0/255.0, alpha: 1.0)
}
continueBtn.userInteractionEnabled = userNameTxt.text != ""
}
//MARK: - POST USER DATA TO SERVER
func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {
// Indicate download
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let url = NSURL(string: "myPlaceholderURLgoesHere")!
// print("URL: \(url)")
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// Verify downloading data is allowed
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
} catch let error as NSError {
print("Error in request post: \(error)")
request.HTTPBody = nil
} catch {
print("Catch all error: \(error)")
}
// Post the data
let task = session.dataTaskWithRequest(request) { data, response, error in
completionHandler(data: userNameTxt, response: userModel, error: error)
// Stop download indication
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
// Stop download indication
}
task.resume()
}
//MARK: - SEGUE
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toPassword"{
let controller = segue.destinationViewController as! PasswordViewController
controller.userModel = userModel
}
}
//MARK: - IB ACTIONS
#IBAction func continuePressed(sender: AnyObject) {
userModel.userNickName = userNameTxt.text!
performSegueWithIdentifier("toPassword", sender: self)
}
}
extension UserNameViewController: UITextFieldDelegate{
func textFieldDidEndEditing(textField: UITextField) {
self.setEnabledButton()
}
}
There are a couple of things you need to change.
userNameTxt is not the username, it's the UITextField containing the username. The text you need is userNameTxt.text?
If the function is expecting Data, you have to convert your text to Data first
let task = session.dataTaskWithRequest(request) { data, response, error in
completionHandler(data: userNameTxt.text?.data(using: .utf8), response: userModel, error: error)
I assume you have to send the data to the server.
If you don't have too you can save the data in keychain access, see: SO: Keychain Access
In order to resolve the error please edit the question with the error message and line of code (if possible).
I would suggest that you use Alamofire for POST/GET (REST). To use Alamofire you need basic knowledge of Cocoapods. It's better in the long term.
NOTE: There can be two possible error outcomes when you make the request.
1) Incorrect data format or bug from your side
2) Server error due backend bug from server side.
The data can be sent from your device with POST where the data is in the BODY or HEADER of the request. Usually it is in the body (parameters in the alamofire methods).
Here is an example:
import Alamofire
...
// MARK:- Login Feature - Universal Met for login
internal static func loginWith(serverUrl: String, parameters: [String: AnyObject]?, headers: [String: String]?, notificationName: String, serviceType: LoginService)
{
Alamofire.request(.POST, serverUrl, parameters: parameters, headers: headers).responseJSON
{ (response) in
print("\n Login feature - \n")
print(" Login url - \(serverUrl)\n")
print(" Login parameters - \(parameters)\n")
print(" Login notificationName - \(notificationName)\n")
print(" Login response - \(response)\n")
EXCDataParserH.parseResponseFrom(ServiceType.Login(type: serviceType),
operation: nil,
data: response.data,
notification: notificationName)
}
}
Instead of writing the whole thing every time you make a server request, try to do as follows:
import Foundation
import SystemConfiguration
class HTTPHelper{
class func httpPostDataDic(postURL:NSURL,postString:NSString,completionHandler:#escaping (NSDictionary?, NSError?) -> Void ) -> URLSessionTask{
var responseResultData: NSDictionary = NSDictionary()
let request = NSMutableURLRequest(url:postURL as URL);
request.httpMethod = "POST";// Compose a query string
request.httpBody = postString.data(using: String.Encoding.utf8.rawValue);
print(request)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil
{
print("error=\(error)")
completionHandler(nil, error as NSError?)
return
}
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
if let responseString = responseString {
print("responseString = \(responseString)")
}
do {
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
responseResultData=myJSON!
completionHandler(responseResultData, nil)
} catch {
print(error)
}
}
task.resume()
return task
}
}
Now whenever you need to make a server POST request,in your ViewController class, do as follows:
//Requesting server
func requestServer() -> Void{
let postvariable = "Value"
let url = URL(string: "your url")!
let postString = "postkey=\(postvariable)"
HTTPHelper.httpPostDataDic(postURL: url as NSURL, postString: postString) {
(responseResult, error) -> Void in
DispatchQueue.main.async {
if error != nil{
print(error ?? "unknown")
}
else{
print(responseResult ?? "unknown result")
//Parse your response
self.parseResult(result: responseResult!);
}
}
}
}
May I ask you one thing that I didn't understand in your question.
How exactly would you save the login credentials in a server? I mean, if you save the login credentials in the server, how would you authenticate user access to these saved credentials?

iOS | can not call URLSession function (swift 3)

I am working on a student system project. All the server side things are done but the iOS client application is making some trouble.
I am trying to create a login page for the user and than check in the database whether the info is correct
Here is the code:
import Foundation
import UIKit
class LoginPage: UIViewController {
/*A reference to the username/student ID text field*/
#IBOutlet weak var idField: UITextField!
//A reference to the password field
#IBOutlet weak var passwordField: UITextField!
//Send the information from the text fields to the server
#IBAction func loginButtonTapped(_ sender: UIButton) {
//declare parameter as a dictionary which contains string as key and value combination.
var parameters = ["name": idField.text!, "password": passwordField.text!] as Dictionary<String, String>
//create the url with NSURL
let url = NSURL(string: "https://api.sis.kemoke.net")
//create the session object
var session = URLSession.sharedSession()
//now create the NSMutableRequest object using the url object
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST" //set http method as POST
var err: NSError?
request.HTTPBody = JSONSerialization.dataWithJSONObject(parameters, options: nil, error: &err) // pass dictionary to nsdata object and set it as request body
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//create dataTask using the session object to send data to the server
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(err != nil) {
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr)'")
}
else {
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
// Okay, the parsedJSON is here, let's get the value for 'success' out of it
var success = parseJSON["success"] as? Int
println("Succes: \(success)")
}
else {
// Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: \(jsonStr)")
}
}
})
task.resume()
}
}
For me this all looks good, but I am getting an error
"can not call function URLSession"
and
"Extra argument" for the error handler.
How do I fix this?

Trouble unwrapping JSON Array to a String Value

I have been struggling with JSON for a few days. I am trying to create a POST request to my web server for a username, that will return information on said user. I have managed to get the JSON response two ways, but I cannot cast any of the elements of the Array to a string. I am using the SWIFTYJSON API too.
import UIKit
import Foundation
class ViewController: UIViewController {
var token = "barrymanilow"
var endPoint = "http://www.never.money/simple_app7.php"
#IBOutlet weak var firstLabel: UILabel!
override func viewDidLoad() {
submitAction(self)
}
func submitAction(sender: AnyObject) {
let myUrl = NSURL(string: "http://www.mindyour.money/simple_app7.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
// Compose a query string
let postString = "token=\(token)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil{
println("error=\(error)")
return
}
// Print out response body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString = \(responseString)")
//Let's convert response sent from a server side script to a NSDictionary object:
var err: NSError?
var myJSON = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error:&err) as? NSArray
var json : JSON = JSON(data: data)
let results: AnyObject? = myJSON?.valueForKey("player_username")
println("\(results)")
let result2 = json["player_username"].string
}
task.resume()
}
}
However, this doesn't seem to be working, can anybody help me?
I see that when using NSJSONSerialization you're casting your JSON response as an NSArray, so for example to get the first item's player_username with SwiftyJSON you would do:
let result2 = json[0]["player_username"].string
It should work without casting json[0] to a dictionary first because thanks to SwiftyJSON the compiler knows that json[0] is a dictionary.
If for some reason json[0] is not automatically subscriptable, you can do:
let playerOneDic = json[0].dictionary
let result2 = playerOneDic["player_username"].string
Otherwise, without SwiftyJSON, you would have to do something like this:
if let playerOne = myJSON?[0] as? [String:AnyObject] {
if let username = playerOne["player_username"] as? String {
println(username)
}
}

Resources