How to edit row without adding another row in tableview in swift - json

I am showing all address in firstVC Tableview from JSON.. the same addresses i am showing in secondVC tableview here i have an option edit.. so if i click editBtn i am able to send the address to nextVC map.. in nextVC if i continue i am going to finalVC then here i am getting address, here the edited address i need to show in the same row where i click editBtn.. how to do that?
like below i am sending address to nextVC: when i click editBtn in Tableview like below i am sending address to nextVC(which i want to edit)
func btnEditTapped(cell: EditAddressTableViewCell) {
if let indexPath = self.addeditTableview.indexPath(for: cell){
print("edit button address index \(indexPath.row)")
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "AddressViewController") as! AddressViewController
viewController.isEdit = true
var addrLo = addressArray[indexPath.row]
print("in edit total mode address \(addrLo.pincode)")
viewController.editPincode = addrLo.pincode
viewController.editStree = addrLo.streetName
viewController.editColony = addrLo.colony
viewController.editCity = addrLo.city
self.navigationController?.pushViewController(viewController, animated: true)
}
}
this is finalVC. code: here if i push to FirstViewController then the row should be edited with new address but i am here adding one more.. edit is not working for me
func editAddressService(){
let locations: [String: Any] = ["longitude": logitude as Any,"latitude": latitude as Any]
let parameters: [String: Any] = [
"pincode": zipName,
"city": localityName,
"streetName": sublocalityName,
"colony": "",
"location" : locations,
"addressName" : addressTextfield.text
]
let string = "http://jcksdjfksdljfklsdl"
var urlComponents = URLComponents(string: string)
let saveAddrsID: String = addAddrsID ?? ""
print("didload saved address userde edit service \(saveAddrsID)")
let requestedUserType = URLQueryItem(name: "requestedUserType", value: "personal")
let requestedItem = URLQueryItem(name: "addressType", value: "Other")
let requestedItemAddr = URLQueryItem(name: "addressId", value: saveAddrsID)
print("edit addressid \(requestedItemAddr)")
let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted)
let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String
print(jsonString)
urlComponents?.queryItems = [requestedItem, requestedUserType, requestedItemAddr]
let urlStr = urlComponents?.url
let request = NSMutableURLRequest(url: urlStr!, cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
let postData = String(format: "addressdetails=%#",jsonString) .data(using: .utf8)
request.httpBody = postData
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if error == nil {
let httpResponse = response as? HTTPURLResponse
if httpResponse!.statusCode == 200 {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! [String: Any]
self.editAddrsID = jsonObject["addressId"] as? String
DispatchQueue.main.async {
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "FirstViewController") as! FirstViewController
self.navigationController?.pushViewController(viewController, animated: true)
}
} catch { print(error.localizedDescription) }
} else {
}
}
})
dataTask.resume()
}
to know flow i have added only this post related code in github: https://github.com/SwiftSamples/AddressEdit
in my github project flow will be: (profileVC showing all address from JSON) add/edit btn - > (addeditVC showing same profileVC address), editBtn - > profileAddressVC Map continueBtn - > ZoomMapVC(picked address need to show in edit btn index row in profile&addeditVC)
please help me with code to edit address.
for edit and add: this is the difference in /saveaddress/ API

save indexPath from secondVC
pass it to finalVC
get the cell already in finalVC using the saved indexPath
sorting and number of cells must of course be the same in secondVC and finalVC

Related

How to make this POST request with different objects?

Overview:
I am trying to make a POST request, which I have done before with only strings. This time, I have a few variables, being: String, Int, and Bool.
Error:
Cannot assign value of type [String : Any] to type Data
Line causing the error:
request.httpBody = paramToSend
Question:
How to convert a Dictionary into Data ?
Complete Code:
func sendComplimentAPI (message: String, recipient: Int, isPublic: Bool) {
let url = URL(string: "https://complimentsapi.herokuapp.com/compliments/send/")
let session = URLSession.shared
let preferences = UserDefaults.standard
let request = NSMutableURLRequest(url: url!)
request.addValue("\(preferences.object(forKey: "token") as! String)", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
let paramToSend = ["message":message,"recipient":recipient,"is_public":isPublic] as [String : Any]
request.httpBody = paramToSend
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data 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 session_data = server_response["id"] as? String {
print("worked")
//do something
/*DispatchQueue.main.async (
execute:
)*/
} else {
print("error")
}
})
task.resume()
}
EDIT:
I have tried this new code and it is still not posting to the server. I am attaching what I changed and also writing what the console shows for the two prints I have it do.
let paramToSend = ["message":writeTextField.text!,"recipient":1,"is_public":isPrivate] as [String : Any] //messageString + recipientString + isPublicString
do {
var serialized = try JSONSerialization.data(withJSONObject: paramToSend, options: .prettyPrinted)
print(serialized)
request.httpBody = serialized
print(request.httpBody)
} catch {
print("found a problem")
}
The console returns (for serialized and then the HTTP body):
113 bytes
Optional(113 bytes)
Is that optional causing the problem? How do I fix it?
To convert Dictionary to Data, use JSONSerialization.data:
Solution:
JSONSerialization.data(withJSONObject: paramToSend, options: .prettyPrinted)
Check the request:
Print the request and see if it matches your expectation
Reading the response:
//Check if there is any error (check if error != nil)
//Examine the response
let statusCode = (response as? HTTPURLResponse)?.statusCode
let statusCodeDescription = (response as? HTTPURLResponse)?.localizedString(forStatusCode: httpResponse.statusCode)
//Check Data
if let data = data {
let dataString = String(data: data, encoding: String.Encoding.utf8)
}
It turns out I needed to add a simple additional header to get the whole thing to work.
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
This is probably why it was not understanding the dictionary I was sending it

calling a function from a function doesn't return value unless the main function is completed swift

Guys I need assistance on swift 4.0
i have a simple function that calls a json and fills those vars with values.
self.getDataFromServer()
after 'self.getDataFromServer()' this will fill in the arrays with retrieved data. This was working great on the previous version of swift (swift 3).
This is the code after the self.getDataFromServer() and getting index out of range, because data isn't populated (PS: This was working on swift 3.0)
var totalAmount = [String]()
var remainingAmount = [String]()
self.getDataFromServer()
let newTotal = Int(totalAmount[0])! + Int(remainingAmount[0])!
let newRemaining = String(newTotal)
updateDailyData(newRemainingAmount: newRemaining, id: userID[0])
I'm getting error on 'newTotal' saying index out of range. Please Help.
I noticed that on swift 4, I'm facing this issue whenever i'm calling JSON.
The JSON Function is as below:
func getDataFromServer() {
let dateOfToday = Date()
let strDateOfToday = convertToString(myDate: dateOfToday)
let postString = "ANYTHING HERE";
let myUrl = URL(string: "ANYTHING HERE")
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"// Compose a query string
request.httpBody = postString.data(using: String.Encoding.utf8);
//Start the task
let task = URLSession.shared.dataTask(with: request) {
(data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(String(describing: error))")
return
}
let values = try! JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSArray
let success = ((values.value(forKey: "success") as? [String])! as NSArray) as! [String]
if (success[0] == "1")
{
self.totalAmount = ((values.value(forKey: "totalAmount") as? [String])! as NSArray) as! [String]
self.remainingAmount = ((values.value(forKey: "remainingAmount") as? [String])! as NSArray) as! [String]
}
}
//Let's convert response sent from a server side script to a NSDictionary object:
task.resume()
}
Here is your perfect working solution using Closure
Swift 4
self.getDataFromServer { (arrTotalAmount, arrRemainingAmount) in
if arrTotalAmount.count > 0, arrRemainingAmount.count > 0 {
// Your code here
let newTotal = Int(totalAmount[0])! + Int(remainingAmount[0])!
let newRemaining = String(newTotal)
updateDailyData(newRemainingAmount: newRemaining, id: userID[0])
}
}
func getDataFromServer(completion: #escaping (_ arrTotalAmount: [String], _ arrRemainingAmount: [String]) -> Void) {
let dateOfToday = Date()
let strDateOfToday = convertToString(myDate: dateOfToday)
let postString = "ANYTHING HERE";
let myUrl = URL(string: "ANYTHING HERE")
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"// Compose a query string
request.httpBody = postString.data(using: String.Encoding.utf8);
//Start the task
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(String(describing: error))")
return
}
let values = try! JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSArray
let success = ((values.value(forKey: "success") as? [String])! as NSArray) as! [String]
if (success[0] == "1")
{
let totalAmount = ((values.value(forKey: "totalAmount") as? [String])! as NSArray) as! [String]
let remaininAmount = ((values.value(forKey: "remainingAmount") as? [String])! as NSArray) as! [String]
completion(totalAmount, remaininAmount)
}
}
//Let's convert response sent from a server side script to a NSDictionary object:
task.resume()
}
I can't really imagine this working in Swift 3, since the effects of your getDataFromServer function are asynchronous. More precisely, the function returns before the code inside the dataTask is executed.
Try something like this to execute code on the caller side after the dataTask has succeeded:
func getDataFromServer(onDone: () -> Void) {
...
let task = URLSession.shared.dataTask(with: request) { ... in
...
onDone()
}
task.resume()
}
And to call it:
var totalAmount = [String]()
var remainingAmount = [String]()
self.getDataFromServer {
let newTotal = Int(totalAmount[0])! + Int(remainingAmount[0])!
let newRemaining = String(newTotal)
updateDailyData(newRemainingAmount: newRemaining, id: userID[0])
}

Passing JSON data from HTTP request to another view controller in Swift 3

How to pass the JSON data from HTTP request to another view controller in swift 3? This function is when selected a cell, It will get the JSON data from server then I have to pass the JSON data to another view controller.
func retrieveTime(jobDateValue: String) -> Void {
if let crew = user!["crew"] as? [String:Any], let crewID = crew["crew_id"] as? String{
let param = ["action": "retrieve time", "job": ["crew_id": crewID, "jobDate": jobDateValue]] as [String : Any]
let headers = [
"content-type": "application/json",
"cache-control": "no-cache"
]
if let postData = (try? JSONSerialization.data(withJSONObject: param, options: [])) {
let request = NSMutableURLRequest(url: URL(string: "http://52.221.231.3/gv/app_api.php")!,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData
_ = URLSession.shared
let task = URLSession.shared.dataTask(with: request as URLRequest) {
(data, response, error) -> Void in
DispatchQueue.main.async(execute: {
if let json = (try? JSONSerialization.jsonObject(with: data!, options: [.mutableContainers])) as? NSDictionary
{
let result = json["result"] as? String
if (result == "success") {
let passValue = json
}else{
}
}
})
}
task.resume()
}
}
}
passing json data to second view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SecondVCSegue"{
if let destination = segue.destination as? SecondVC {
destination.passedData = json
}
}
}
JSON Output:
{
jobs = (
{
jobDate = "2017-09-01";
jobEndTime = 1504231200;
jobID = 88;
jobTime = 1504224000;
}
);
message = "Retrieve Sucessfully";
result = success;
}
You should set up a manual segue in Storyboard, that you only call inside the completion handler of your network request. If the segue is connected to a single tableview cell, it will be called by the system before your async function would finish execution. Once you changed your segue to be manual and not connected to a static table view cell, you can call it using the function performSegue.
func retrieveTime(jobDateValue: String) -> Void {
if let crew = user!["crew"] as? [String:Any], let crewID = crew["crew_id"] as? String{
let param = ["action": "retrieve time", "job": ["crew_id": crewID, "jobDate": jobDateValue]] as [String : Any]
let headers = [ "content-type": "application/json", "cache-control": "no-cache" ]
if let postData = (try? JSONSerialization.data(withJSONObject: param, options: [])) {
var request = URLRequest(url: URL(string: "http://52.221.231.3/gv/app_api.php")!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
guard error == nil, let data = data else {return}
DispatchQueue.main.async{
if let json = (try? JSONSerialization.jsonObject(with: data)) as? [String:Any]{
let result = json["result"] as? String
if (result == "success") {
let passValue = json
self.performSegue(withIdentifier: "YourSegue", sender: json)
} else{
}
}
}
}
task.resume()
}
}
}
Also use native Swift objects when available instead of their Foundation counterparts (such as NSDictionary, NSMutableURLRequest).
You should also use the sender option of performSegue to send the json data to your other view controller instead of storing it in another variable unless you use the json object in your first view controller as well.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SecondVCSegue"{
if let destination = segue.destination as? SecondVC, let json = sender as? [String:Any] {
destination.passedData = json
}
}
}

I'm having troubles with my Login authentication

I have a JSON Post authentication to make a login request, of course I need to send the user & password parameters to receive a response for filling my HomeViewController, so I need first to validate the two parameters, and if the user doesn't exist of course I'm going to send an Alert and forbid the access to the Home. The thing is that I don't know in which method do I have to put this validation, because the way I'm doing it throws me an alert before the validation. This is my code:
class LoginViewController: UIViewController, LoginProtocol {
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var user : String = ""
var psw : String = ""
var idResponse : String = ""
var message : String = ""
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func validateLogin(_ sender: Any) {
DoLogin(self.username.text!, self.password.text!)
}
#IBAction func login(_ sender: Any) {
if self.idResponse != "0" {
validation(message: self.message)
} else {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.present(vc, animated: false, completion: nil)
vc.getUser = self.username.text!
vc.getPassword = self.password.text!
}
}
func validation(message: String) {
let myAlert = UIAlertController(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction (title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
return
}
func DoLogin(_ user:String, _ psw:String)
{
let url = URL(string: "http://162.209.99.39:8080/MiClaroBackend/auth")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url!)
request.httpMethod = "POST"
self.username.text = user
self.password.text = psw
let paramToSend = ["username":user,"password":psw]
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try! JSONSerialization.data(withJSONObject: paramToSend)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data else
{
return
}
let json:AnyObject?
do {
json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json{
let loginModel = LoginModel()
let defaultServiceResponse = parseJSON["defaultServiceResponse"] as! NSDictionary
loginModel.message = defaultServiceResponse["message"] as! String
loginModel.idResponse = defaultServiceResponse["idResponse"] as! Int
self.idResponse = String(loginModel.idResponse)
self.message = loginModel.message
print(json!)
}
} catch {
let error = ErrorModel()
error.phrase = "PARSER_ERROR"
error.code = -1
error.desc = "Parser error in login action"
}
})
task.resume()
}
The validateLogin method is attached to a TextView with the action: "EditingDidEnd". So what I need is that when I finish writing the password, the DoLogin function validates that exists and let me continue to the HomeViewController, but the issue is that when I click the button 'Ingresar'(Login), it shows a blank Alert first and only the second time I click the button it let me through immediately. I don't know why is sending the Alert that I declare at the "func validation" if I'm not calling it before. I'm going to attach a picture to show you the Alert that is appearing:
Chances are your EditingDidEnd method calls DoLogin which is processing in background when you press the Login button. hence your if condition below becomes true as your idResponse is initialized to "" empty string.
if self.idResponse != "0"
Rectify this if condition to check if its empty then dont call the validation message.
The best way to do this is to call your DoLogin only after click of the Login button and if in case you have success redirect to Home page. Otherwise show the alert. Please note do the UI action on the main thread.
First Update the Code And User Like this
func DoLogin(_ user:String, _ psw:String)
{
let url = URL(string: "http://162.209.99.39:8080/MiClaroBackend/auth")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url!)
request.httpMethod = "POST"
let paramToSend = ["username":user,"password":psw]
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try! JSONSerialization.data(withJSONObject: paramToSend)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data else
{
return
}
let json:AnyObject?
do {
json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json{
let loginModel = LoginModel()
let defaultServiceResponse = parseJSON["defaultServiceResponse"] as! NSDictionary
loginModel.message = defaultServiceResponse["message"] as! String
loginModel.idResponse = defaultServiceResponse["idResponse"] as! Int
self.idResponse = String(loginModel.idResponse)
self.message = loginModel.message
if self.idResponse != "0" {
validation(message: self.message)
} else {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.present(vc, animated: false, completion: nil)
vc.getUser = user!
vc.getPassword = psw!
}
}
} catch {
let error = ErrorModel()
error.phrase = "PARSER_ERROR"
error.code = -1
error.desc = "Parser error in login action"
}
})
task.resume()
}
If You want to add alert in imageview use this code
var imageView = UIImageView(frame: CGRectMake(220, 10, 40, 40))
imageView.image = yourImage
alert.view.addSubview(imageView)

swift - unexpectedly found nil while unwrapping an Optional value when searching in Google Images

let placeName = "New York"
func getImage(place: String) {
let url = NSURL(string: "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=\(place)")
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){ (response, go, error) -> Void in
do {
let go = try NSJSONSerialization.JSONObjectWithData(go!, options: NSJSONReadingOptions.AllowFragments) as! [String:AnyObject]
let responseData = go["responseData"] as! [String:AnyObject]
let results = responseData["results"] as! [[String:String]]
let firstObject = results[0]
let firstURL = firstObject["unescapedUrl"]
print(responseData)
} catch{
print(error)
}
}
}
getImage(placeName)
It looks like when I want to grab an image from Google Images, it won't work. Can someone please figure out what I am doing wrong?
Your URL is badly formatted. Delete the space between New and York.