I was accessing my webservices fine till xcode 7.3 i updated to xcode 8.2 and swift 3, now my app is crashing, here is my code
func jsonParsingFromURL () throws {
let WSLink = "http://XXXXX.co/itXXXXMS/webservice.asmx/GXXXXXX"
let url = URL(string: WSLink)
let bodyData = "lastlogindate=\(todayDate)&CompanyId=\(compID)" //problem is here
let request:NSMutableURLRequest = NSMutableURLRequest(url:url!)
request.httpMethod = "POST"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main) {(response, data, error) in
guard let _ = data else { return }
self.startParsing(data!)
}
}
func startParsing(_ data :Data) {
//Am getting error here in this line
let dict: NSDictionary!=(try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary
arrDict.removeAllObjects()
for i in 0 ..< (dict.value(forKey: "results") as! NSArray).count {
arrDict.add((dict.value(forKey: "results") as! NSArray) .object(at: i))
}
}
json text did not start with array or object and option to allow
fragments not set
Actually i found the reason of error but don't know how to fix it.
when i write as, let bodyData = "lastlogindate=26/11/2016&CompanyId=3" //it is working.
and when i wrap parameters in variables which are dynamic coming from previous viewController like
let date = "26/11/2016", let id = "3" and
let bodyData = "lastlogindate=(date)&CompanyId=(id)" then am getting error.
So basically my question is how to wrap dynamic variables to request.httpBody in swift 3 ?
My problem was in this line while passing parameters to bodyData:
let bodyData = "lastlogindate=\(todayDate)&CompanyId=\(compID)"
//example lastlogindate = "12/24/2016" and CompanyId = 134
When I do print(bodyData) I'm getting output as
"lastlogindate=optional(12/24/2016)&CompanyId=optional(134)"
Why optional was getting added in Swift 3, I dont know.
In Swift 2.2 it was not adding optional .
So, what i did is
let bodyData = "lastlogindate=" + todayDate + "&CompanyId=" + compID
Now, when I do print(bodyData) it gives me correct output as
lastlogindate=12/24/2016&CompanyId=134.
Related
This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I have tried to convert this code to use ‘sendSynchronousRequest()‘ but have had no luck since most resources on SO are outdated.
I am trying to collect data from an API. It then creates an Array of Marvel Characters with the information I need. The whole code works fine, but the Array is empty because it returns the data before the request finishes. How can I wait for the data?
static func getCharacters() -> Array<MarvelCharModel>{
var CharactersFromApi = [MarvelCharModel]();
let baseUrl = "https://gateway.marvel.com";
let charUri = "/v1/public/characters";
let apiKey = "apikey=xxxxxxxxxxxxxxxxxxxx";
let url = URL(string: baseUrl + charUri + "?" + apiKey)!
var response: URLResponse?
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("developer.marvel.com", forHTTPHeaderField: "Referer")
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main) {(response, data, error) in
guard let data = data else { return }
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
if let dataInfo = responseJSON["data"] as? [String: Any] {
let results = dataInfo["results"] as! NSArray;
for dataCharacter in results {
let character = dataCharacter as? [String: Any];
let charId = character?["id"] as! Int;
let charName = character?["name"] as! String;
let charDesc = character?["description"] as! String;
let charPic = character?["thumbnail"] as! [String: Any];
let charPath = charPic["path"] as! String;
let charExt = charPic["extension"] as! String;
CharactersFromApi.append(MarvelCharModel(name: charName, desc: charDesc, pic: charPath + "." + charExt, id: charId));
// print(CharactersFromApi) <-- This shows correctly but clearly the return at the bottom is not waiting
}
}
}
}
return getFavourite(characters: CharactersFromApi); // CharactersFromAPI is empty
}
You have to wait until all of your received data aren't appended to local array and then you have to call completion.
So, instead of having return type, have completion handler in parameter
static func getCharacters(_ completion: #escaping ([MarvelCharModel]) -> () )
then after foreach loop call completion
for dataCharacter in results {
...
}
completion(getFavourite(characters: CharactersFromApi))
then you can call this method and you can access received MarvelCharModel inside its closure
Foo.getCharacters { characters in
... // do what you need when completion is called
}
I am new to iOS please consider.I want to post some data to server, but I am not able to send Mobile number +91 and bloodGroup A+..it was sending to firebase + is replace with space " " like this ( 91) and (A )
func addEmployees(){
let photoUrl = "https://firebasestorage.googleapis.com/v0/b/pickcel-1241.appspot.com/o/task.careGallery%2FGroup%2018aasa.png?alt=media&token=4e0ac8f6-134a-4807-9fef-f44eabe9f6a8";
let userID = Auth.auth().currentUser!.uid
let mobilenumber = Auth.auth().currentUser?.phoneNumber
var employeeDetails = [String : AnyObject]()
employeeDetails["OID"] = getOID() as AnyObject
employeeDetails["MID"] = userID as AnyObject
employeeDetails["email"] = "ssinth#gmail.com" as AnyObject
employeeDetails["firstName"] = "First Name" as AnyObject
employeeDetails["lastName"] = "last name" as AnyObject
employeeDetails["isManager"] = "true" as AnyObject
employeeDetails["regMedia"] = "mobile" as AnyObject
employeeDetails["shortDestination"] = "btm" as AnyObject
employeeDetails["address"] = "+btm" as AnyObject
employeeDetails["createdDate"] = getdateformat() as AnyObject
employeeDetails["orgName"] = "Test Org" as AnyObject
employeeDetails["photoUrl"] = photoUrl as AnyObject
employeeDetails["officeOpenTime"] = "09:00" as AnyObject
employeeDetails["officeCloseTime"] = "18:00" as AnyObject
employeeDetails["phoneNumber"] = labelmobile.text as AnyObject
employeeDetails["bloodGroup"] = "A+" as AnyObject
employeeDetails["empId"] = "abcd" as AnyObject
let convertedvalue : String = convertToParameters(employeeDetails)
print("convertedvalues : \(convertedvalue)")
let myUrl = URL(string: "https://us-central1-pickceltest.cloudfunctions.net/rebliss/createNewUser");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"// Compose a query string
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let postString = convertedvalue;
print("start create employee")
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(String(describing: error))")
return
}
print("start create employee =successfull")
print("response = \(String(describing: response))")
do {
print("start create employee =parsing problems ")
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
// print("resultjson=one : ",self.json)
print("resultjson=two : ",parseJSON)
}
} catch {
print(error)
}
}
task.resume()
}
function convertToParameters
func convertToParameters(_ params: [String: AnyObject?]) -> String {
var paramList: [String] = []
for (key, value) in params {
guard let value = value else {
continue
}
let scapedKey = key
let scapedValue = value
print("employee add status objects = ","\(scapedKey)=\(scapedValue as AnyObject)")
paramList.append("\(scapedKey)=\(scapedValue as AnyObject)")
}
return paramList.joined(separator: "&")
}
Json Error:
resultjson=two : {
error = {
code = "auth/invalid-phone-number";
message = "The phone number must be a non-empty E.164 standard compliant identifier string.";
};
success = 0;
}
Error in firebase console:
Actual issue with you is your parameters not getting properly url encoded.
Temporary Solution for your code:
In your convertToParams method, make following changes:
let scapedKeyEncoded = scapedKey.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed);
let scapedValueEncoded = scapedValue.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed);
paramList.append("\(scapedKeyEncoded)=\(scapedValueEncoded)"
Note: Your scapedValue must be String, so, make employeeDetails as [String:String].
Permanent Solution(Perfect way for encoding query parameters):
For this, you can change your code to something like this:
Remove convertToParameters method
Add following method in place of it
class func getURLRequestWith(urlStr:String, paramsDict:[String:String]?, isParamsAsQuery:Bool, isParamsAsBody:Bool) -> URLRequest? {
guard var components = URLComponents(string: urlStr) else { return nil }
if paramsDict != nil{
components.queryItems = paramsDict!.map({ (key, value) -> URLQueryItem in
return URLQueryItem(name: key, value: value)
})
}
if isParamsAsQuery{
let request = URLRequest(url: components.url!);
return request;
}
if isParamsAsBody{
let url = URL(string: urlStr);
var request = URLRequest(url: url!);
let bodyStr = components.percentEncodedQuery;
if bodyStr != nil{
request.httpBody = bodyStr!.data(using: String.Encoding.utf8);
}
return request;
}
let url = URL(string: urlStr);
let request = URLRequest(url: url!);
return request;
}
Remove below lines from your code:
let convertedvalue : String = convertToParameters(employeeDetails)
print("convertedvalues : \(convertedvalue)")
let myUrl = URL(string: "https://us-central1-pickceltest.cloudfunctions.net/rebliss/createNewUser");
var request = URLRequest(url:myUrl!)
request.httpBody = postString.data(using: String.Encoding.utf8);
Add following code instead of above removed code
let urlStr = "https://us-central1-pickceltest.cloudfunctions.net/rebliss/createNewUser"
var request = getURLRequestWith(urlStr: urlStr, paramsDict: employeeDetails, isParamsAsQuery: false, isParamsAsBody: true)
convert employeeDetails dictionary to type [String:String] instead of [String:AnyObject]
Now, try your code and it will surely work.
Please check if below solution works. Add percent encoding to you url.
// Create NSURL Ibject
let url : NSString = urlWithParams as NSString
let urlStr = url.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
let searchURL : NSURL = NSURL(string: urlStr! as String)!
// Creaste URL Request
let request = NSMutableURLRequest(url: searchURL as URL, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 120)
// Set request HTTP method to GET. It could be POST as well
request.httpMethod = "POST"
This question already has answers here:
How can I get the Data from NSURLSession.sharedSession().dataTaskWithRequest
(2 answers)
JSON parsing swift, array has no value outside NSURLSession
(1 answer)
Closed 4 years ago.
at my form load, i need to get a data from json string and push it into an arraylist. When i check my code with breakpoints, my cursor enters my method, going through, and it get task2, pass "if" then stopped at var sec="" then exit my method, after 2-3 seconds, it enters do for.
Whats wrong with my code?
override func viewDidLoad() {
super.viewDidLoad()
let urlJsonToken2 = "https://services.domain.com/"
let myURL2 = NSURL(string: urlJsonToken2)
let request2 = NSMutableURLRequest(url: myURL2! as URL)
request2.httpMethod = "GET"
request2.addValue("Bearer "+tokenNewId, forHTTPHeaderField: "Authorization")
request2.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request2.setValue("application/json", forHTTPHeaderField: "Accept")
let task2 = URLSession.shared.dataTask(with: request2 as URLRequest) {(data2, response2, error2) -> Void in
if let unwrappedData2 = data2 {
do {
guard let records = try? JSONSerialization.jsonObject(with: unwrappedData2, options: .mutableContainers) as? [[String: Any]] else {
return
}
for item in records! {
let id = item["id"] as? intmax_t
let name = item["name"] as? String
self?.ArrayList.append(ClassCat(id:id!, id: name!, name));
}
} catch {}
}
} task2.resume()
var sec = ""
}
Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.
I'm trying to make a post to an HTTP server
By the way this is my code:
func sendPostToUrl(url:String, withParams params: [String: String?] ) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
var bodyData = ""
for (key,value) in params{
if (value==nil){ continue }
let scapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(
.URLHostAllowedCharacterSet())!
let scapedValue = value!.stringByAddingPercentEncodingWithAllowedCharacters(
.URLHostAllowedCharacterSet())!
bodyData += "\(scapedKey)=\(scapedValue)&"
}
request.HTTPBody = bodyData.dataUsingEncoding
(NSUTF8StringEncoding, allowLossyConversion: true)
var task = session.dataTaskWithRequest(request,
completionHandler: {data, response, error -> Void in
println("Response: \(response)")
let dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Data: \(dataString)")
})
task.resume()
}
It works but is not perfect. If I call the function this way:
client.sendPostToUrl("http://novagecko.com/tests/test.php",
withParams: ["hello":"world","inject":"param1=value1¶m2=value2"]);
The server detects 3 post fields (with keys hello,inject and param2) instead of 2.
How can I escape the key and values?
Is there something more I could do for improving the method?
If you can target iOS 8 (thanks #Rob), use NSURLComponents to escape your parameters instead:
import Foundation
func encodeParameters(#params: [String: String]) -> String {
var queryItems = map(params) { NSURLQueryItem(name:$0, value:$1)}
var components = NSURLComponents()
components.queryItems = queryItems
return components.percentEncodedQuery ?? ""
}
Now encodeParameters(params:["hello":"world","inject":"param1=value1¶m2=value2"]) returns hello=world&inject=param1%3Dvalue1%26param2%3Dvalue2 as you would expect.
Otherwise, the best way to create the character set that will let you escape your values properly is this:
var safeCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy()
safeCharacterSet.removeCharactersInString("&=")
and see #rintaro's answer to use filter/map properly to perform the encoding in a nice way.
It seems, NSCharacterSet doesn't have relevant set for that.
So, add this
extension NSCharacterSet {
class func URLUnreservedCharacterSet() -> NSCharacterSet {
return self(charactersInString: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~")
}
}
Then
var bodyData = ""
var safeCharacterSet = NSCharacterSet.URLUnreservedCharacterSet()
for (key,value) in params{
if (value==nil){ continue }
let scapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
let scapedValue = value!.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
bodyData += "\(scapedKey)=\(scapedValue)&"
}
As following #Rob's advice in comment, here is a map and join example:
let params:[String:String?] = ["fubar":nil, "hello":"world", "inject":"param1=value1¶m2=value2"]
let safeCharacterSet = NSCharacterSet.URLUnreservedCharacterSet()
let pairs = filter(params, {$1 != nil}).map { (key, value) -> String in
let _key = key.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
let _val = value!.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
return _key + "=" + _val
}
let bodyData = "&".join(pairs)
This is better because there is no trailing & in the result.