String Comparison giving unexpected result with data obtained via Alamofire - swift

I am having issue with string comparison in Swift (I may wrong here).
Below is my method in which I am getting this issue.
Here is my input value.
self.callGrammerCheckAPI(ForText: """
ALSO BY STEPHEN HAWKING
A Brief History of Time
A Briefer History of Time
Black Holes and Baby Universes and Other Essays
The Illustrated A Brief History of Time
The Universe in a Nutshell
FOR CHILDREN
George's Secret Key to the Universe (with Lucy Hawking)
George's Cosmic Treasure Hunt (with Lucy Hawking)
ALSO BY LEONARD MLODINOW
A Briefer History of Time
The Drunkard's Walk: How Randomness Rules Our Lives
Euclid's Window: The Story of Geometry from Parallel Lines to Hyperspace
Feynman's Rainbow: A Search for Beauty in Physics and in Life
FOR CHILLDRAN
The Last Dinosaur (with Matt Costello)
Titanic Cat (with Matt Costello)
""")
Here is the method I am calling.
func callGrammerCheckAPI(ForText text:String) -> Void {
var valueToValidate = (text.components(separatedBy: .whitespacesAndNewlines).joined(separator: "+"))
let url = "https://montanaflynn-spellcheck.p.rapidapi.com/check/?text=\(valueToValidate)"
let headers: HTTPHeaders = [
"X-RapidAPI-Key": "3LlMwHr729mshN6RendH4kjvVp1pp1ogORZjsng5F2pBZdjsL3"
]
Alamofire.request(url, headers: headers).responseJSON { response in
print("Request: \(String(describing: response.request))") // original url request
print("Response: \(String(describing: response.response))") // http url response
print("Result: \(response.result)") // response serialization result
let attributedText = NSMutableAttributedString.init(string: text)
switch response.result{
case .success(let returnValue):
print(returnValue)
if let valueToParse = returnValue as? [String:Any]{
if let correctionsFound = valueToParse["corrections"] as? [String:Any]{
attributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.black, range: NSRange.init(location: 0, length: attributedText.string.utf16.count))
for key in correctionsFound.keys{
let unwantedCharRegex = try! NSRegularExpression.init(pattern: "([(/\')])", options: NSRegularExpression.Options.caseInsensitive)
let formatedKey = unwantedCharRegex.stringByReplacingMatches(in: key, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSRange.init(location: 0, length: key.count), withTemplate: "")
let expression = "(\(formatedKey))."
let regex = try! NSRegularExpression.init(pattern: expression , options: NSRegularExpression.Options.caseInsensitive)
let matches = regex.matches(in: attributedText.string, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSRange.init(location: 0, length: attributedText.string.utf16.count))
outerloop: for result in matches{
guard let availableOptions = correctionsFound[key] as? [String] else{
return
}
let valueToFound:NSString = NSString.init(string: attributedText.string.subString(from: result.range.location, to: result.range.location+result.range.length))
for suggestion in availableOptions{
let leftValue:NSString = NSString.init(string: suggestion)
let rightValue:NSString = valueToFound
print("\(leftValue) == \(rightValue) = \(leftValue == rightValue)")
if (leftValue == rightValue) == true {
print("Continue Done")
continue outerloop
}
}
attributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: result.range)
}
}
}
}
break
case .failure(let error):
print(error)
break
}
OperationQueue.main.addOperation {
self.tvReference.attributedText = attributedText
self.navigationItem.prompt = nil
}
if let json = response.result.value {
print("JSON: \(json)") // serialized json response
}
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)") // original server data as UTF8 string
}
}
}
Issue is when I compare leftValue and rightValue even though both the values are same it returns false. I do not know what went wrong there.

Related

Count number of objects in JSON array on a URL in swift5

I have a JSON array at url = https://api.github.com/users/greenrobot/starred .
I want to count how many objects are there in that array using swift. I don't want data present, I just want count of it.
Assuming you downloaded the contents of that URL into a variable data of type Data:
if let object = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: AnyHashable]] {
let count = object[0].keys.count
}
I assume that you are using Alamofire for making a network request. In the code below we are just extracting the value object from Alamofire result. We convert the value to Array of dictionaries and then you can just get the count.
AF.request("https://api.github.com/users/greenrobot/starred",method: .get).responseJSON { apiResponse in
switch apiResponse.result{
case .success(_):
let dictionary = apiResponse.value as? [[String:Any]]
print("dictionaryCount \(dictionary?.count ?? -1)")
case .failure(_):
print("error \(apiResponse.error?.underlyingError?.localizedDescription ?? "")")
}
}
The GitHub starred API returns a maximum of 30 items by default, in the case of greenrobot with a total number of 372 it's not meaningful.
A smart way to get the actual number of starred items is to specify one item per page and to parse the Link header of the HTTP response which contains the number of the last page
Task {
do {
let url = URL(string: "https://api.github.com/users/greenrobot/starred?per_page=1")!
let (_, response) = try await URLSession.shared.data(from: url)
guard let link = (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "Link") else {
throw URLError(.badServerResponse)
}
let regex = try NSRegularExpression(pattern: "page=(\\d+)")
if let lastMatch = regex.matches(in: link).last {
let range = Range(lastMatch.range(at: 1), in: link)!
let numberOfStarredItems = String(link[range])
print(numberOfStarredItems)
} else {
print("No match found")
}
} catch {
print(error)
}
}

Persian Characters in URL (with Alamofire)

i have variables and i want put them in url to post
some variables can be persian or english characters
everything is well when characters are english
but when i use persian characters alamofire respond invalid url
the code is .
let headers : Dictionary = [
"Content-Type":"application/json"
];
let request = Product_Rename() .
request.newName = string .
request.productId = UInt(_Data[deletIndex].ProductId!)
Alamofire.request("http://example.com/api/product/rename?productId=\(_Data[deletIndex].ProductId!)&newName=\(string)&AUTHID=\(authID!)",
method: .put,
headers: headers)
.validate(statusCode: 200..<300)
.responseString(completionHandler: {response in
})
.responseJSON(completionHandler: {response in
switch response.result{
case .success:
if let data = response.data, let utf8Text = String(data:data,encoding:.utf8){
let x = Product_Rename_Response(json:utf8Text)
if x.success == true {
self._Data[self.deletIndex].Name = string
self._ProductTable.reloadData()
}else{
self.dismiss(animated: true, completion: nil)
}
}
case .failure(let error):
print(error)
break
}
})
You have to escape the special characters from the URL.
request.productId = UInt(_Data[deletIndex].ProductId!)
let url = "http://example.com/api/product/rename?productId=\(_Data[deletIndex].ProductId!)&newName=\(string)&AUTHID=\(authID!)"
if let encodedUrl = original.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) {
Alamofire.request(encodedUrl,
method: .put,
headers: headers)
.validate(statusCode: 200..<300)
.responseString(completionHandler: {response in
})
// use the data
}
The method addingPercentEncoding(withAllowedCharacters:) will encode the characters into percent encoding (e.g. https://example.com/foo.php?text=bar%20baz, where %20 represents a space).

Swift :Initializer for conditional binding must have Optional type, not 'String.SubSequence' (aka 'Substring')

I'm working on a little project and I came across this error which I can't find on the Internet.
This is my code:
let bufferRecieved = String(bytesNoCopy: buffer, length: length, encoding: .utf8, freeWhenDone: true)
guard let delimiterFirstIndex = bufferRecieved?.index(of: ":")!,
let name = bufferRecieved![..<delimiterFirstIndex],
let message = bufferRecieved![delimiterFirstIndex...] else {
return nil
}
I get:
Initializer for conditional binding must have Optional type, not 'String.SubSequence' (aka 'Substring') for the two instructions
let name = bufferRecieved![..<delimiterFirstIndex],
let message = bufferRecieved![delimiterFirstIndex...]
What does this error mean and how can I fix it?
name and message aren't Optionals, so there's no need for them to be part of the conditional binding. Just put them after the guard statement:
guard let bufferRecieved = String(bytesNoCopy: buffer, length: length, encoding: .utf8, freeWhenDone: true),
let delimiterFirstIndex = bufferRecieved.index(of: ":") else {
return nil
}
let name = bufferRecieved[..<delimiterFirstIndex]
let message = bufferRecieved[delimiterFirstIndex...]
use(name, message)

swift euc-kr korean encoding not working. But works in python

I am writing some code to parse korean text from server encoded with euc-kr korean encoder.
When I just do the same encoding in Python, it works as expected.
But when I do it as following, encoding doesn't work. The result is unreadable.
In Python :
string = u'안녕하세요.'.encode('eucKR')
In Swift :
let encoding:UInt = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(
CFStringEncodings.EUC_KR.rawValue))
let encodedData = "안녕하세요.".data(using: String.Encoding(rawValue: encoding))!
What the difference between those 2 encodings ?
Following are full source codes for both python and swift. I still stuck on the encoding part. Is the problem related to alamofire post request?
Python:
import requests
from pattern import web
string = u'저는 내일 바빠서 학교에 못갑니다.'.encode('eucKR')
r = requests.post("http://nlp.korea.ac.kr/~demo/dglee/komatag.php", data={'formradio1': '', 'formradio2': 'ems', 'textarea': string})
dom = web.Element(r.text)
main = dom('tr')
for item in main:
result = web.plaintext(item.source)
a = result.encode('ISO-8859-1')
t=a.decode('eucKR')
print(t)
Swift:
override func viewDidLoad() {
let string: NSString = NSString(string: "안녕하세요")
let encodedEucKr = stringToEuckrString(stringValue: string as String)
print(encodedEucKr)
Alamofire.request("http://nlp.korea.ac.kr/~demo/dglee/komatag.php", method: .post, parameters: ["formradio1":"", "formradio2":"ems", "textarea": encodedEucKr], headers: nil).responseString { response in
switch(response.result) {
case .success(_):
if let data = response.result.value{
print(response.result.value)
}
break
case .failure(_):
print(response.result.error)
break
}
}
}
func stringToEuckrString(stringValue: String) -> String {
let encoding:UInt = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(
CFStringEncodings.EUC_KR.rawValue))
let encodedData = stringValue.data(using: String.Encoding(rawValue: encoding))!
let attributedString = try? NSAttributedString(data: encodedData, options:[:], documentAttributes: nil)
if let _ = attributedString {
return attributedString!.string
} else {
return ""
}
}
It was not easy for two reasons...
Sending form data in EUC-KR is not considered to be standard-compliant in modern web technologies and standards.
The response sent from your server is sort of broken, in that Swift cannot decode the result as a valid EUC-KR text.
(This seems to be a bug of your server side code.)
Anyway, when you need to send a web form based request to your server in EUC-KR:
Create a EUC-KR byte sequence from the original
Percent-escape it. You may need to do it by yourself
Put entire request in an HTTP request body
Add proper MIME type header
Some details depend on the server. I have never used Alamofire, so I do not know if Alamofire supports such things.
Here I show you an example using a normal URLSession:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
sendRequest(string: "안녕하세요")
}
func sendRequest(string: String) {
let rawEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.EUC_KR.rawValue))
let encoding = String.Encoding(rawValue: rawEncoding)
let url = URL(string: "http://nlp.korea.ac.kr/~demo/dglee/komatag.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
//Create an EUC-KR byte sequece
let eucKRStringData = string.data(using: encoding) ?? Data()
//Percent-escape, you need to do it by yourself
//(Though, most servers accept non-escaped binary data with its own rules...)
let eucKRStringPercentEscaped = eucKRStringData.map {byte->String in
if byte >= UInt8(ascii: "A") && byte <= UInt8(ascii: "Z")
|| byte >= UInt8(ascii: "a") && byte <= UInt8(ascii: "z")
|| byte >= UInt8(ascii: "0") && byte <= UInt8(ascii: "9")
|| byte == UInt8(ascii: "_") || byte == UInt8(ascii: ".") || byte == UInt8(ascii: "-")
{
return String(Character(UnicodeScalar(UInt32(byte))!))
} else if byte == UInt8(ascii: " ") {
return "+"
} else {
return String(format: "%%%02X", byte)
}
}.joined()
//In application/x-www-form-urlencoded format, you send data in a URL-query like format.
let paramString = "formradio1=&formradio2=ems&textarea=\(eucKRStringPercentEscaped)"
//As all non-ASCII characters are percent-escaped, .isoLatin1 works well here.
let bodyData = paramString.data(using: .isoLatin1)!
//Form data needs to be sent as a body of HTTP protocol.
request.httpBody = bodyData
//MIME type for usual form data is "application/x-www-form-urlencoded".
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
//URLRequest is ready and you can start dataTask here.
let task = URLSession.shared.dataTask(with: request) {data, response, error in
if let error = error {
print("Error:", error)
}
if let response = response {
print("Response:", response)
}
//The response may not be valid EUC-KR; you need to decode it while accepting invalid bytes.
if let data = data {
var result = ""
var i = 0
while i < data.count{
let ch = data[i]
if ch < 0x80 {
result += String(Character(UnicodeScalar(UInt32(ch))!))
} else if
i + 2 <= data.count,
let ch2 = String(data: data.subdata(in: i..<i+2), encoding: encoding)
{
result += ch2
i += 1
} else {
result += "?"
}
i += 1
}
print("Result:", result)
}
}
//Do not forget to resume the created task.
task.resume()
//And remember you should not do anything after you invoke an async task.
}
If your server side can handle UTF-8 requests and responses properly, the code above can be far more simple. Using EUC-KR in web services is sort of outdated. You'd better adopt UTF-8 soon.

Spaces between words are getting lost after I send post method [duplicate]

I am learning Swift and I don't know how to send parameters to server using Swift.
In Objective-C we can do this by using "%#" as the placeholder.
But what should be done in case of Swift, suppose I have a login webservice which requires email and password.
Now I want to know is that how will i send the logintextfield and passwordtextfield text to the server, such as,
var bodyData = "email=logintextfield.text&password=passwordtextfield.text"
When creating a HTTP request that includes user input, one should generally percent escape it in case there are any reserved characters in the user's input, thus:
let login = logintextfield.text?.addingPercentEncodingForURLQueryValue() ?? ""
let password = passwordtextfield.text?.addingPercentEncodingForURLQueryValue() ?? ""
let bodyData = "email=\(login)&password=\(password)"
Note, you'd really want to check to see if login and password were nil or not. Anyway, the percent-escaping is done as follows:
extension String {
/// Percent escapes values to be added to a URL query as specified in RFC 3986
///
/// This percent-escapes all characters besides the alphanumeric character set and "-", ".", "_", and "~".
///
/// http://www.ietf.org/rfc/rfc3986.txt
///
/// :returns: Returns percent-escaped string.
func addingPercentEncodingForURLQueryValue() -> String? {
let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
return self.addingPercentEncoding(withAllowedCharacters: allowedCharacters)
}
}
See this answer for another rendition of this extension.
If you wanted to see a demonstration of the use of the above, imagine the following request:
let keyData = "AIzaSyCRLa4LQZWNQBcjCYcIVYA45i9i8zfClqc"
let sensorInformation = false
let types = "building"
let radius = 1000000
let locationCoordinate = CLLocationCoordinate2D(latitude:40.748716, longitude: -73.985643)
let name = "Empire State Building, New York, NY"
let floors = 102
let now = Date()
let params:[String: Any] = [
"key" : keyData,
"sensor" : sensorInformation,
"typesData" : types,
"radius" : radius,
"location" : locationCoordinate,
"name" : name,
"floors" : floors,
"when" : now,
"pi" : M_PI]
let url = URL(string: "http://some.web.site.com/inquiry")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = params.dataFromHttpParameters()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard data != nil && error == nil else {
print("error submitting request: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse where httpResponse.statusCode != 200 {
print("response was not 200: \(response)")
return
}
// handle the data of the successful response here
}
task.resume()
I'm including lots of parameters that were not included in your example, but simply as a way of illustrating the routine's handling of a diverse array of parameter types.
Incidentally, the above uses my datafromHttpParameters function:
extension Dictionary {
/// This creates a String representation of the supplied value.
///
/// This converts NSDate objects to a RFC3339 formatted string, booleans to "true" or "false",
/// and otherwise returns the default string representation.
///
/// - parameter value: The value to be converted to a string
///
/// - returns: String representation
private func httpStringRepresentation(_ value: Any) -> String {
switch value {
case let date as Date:
return date.rfc3339String()
case let coordinate as CLLocationCoordinate2D:
return "\(coordinate.latitude),\(coordinate.longitude)"
case let boolean as Bool:
return boolean ? "true" : "false"
default:
return "\(value)"
}
}
/// Build `Data` representation of HTTP parameter dictionary of keys and objects
///
/// This percent escapes in compliance with RFC 3986
///
/// http://www.ietf.org/rfc/rfc3986.txt
///
/// :returns: String representation in the form of key1=value1&key2=value2 where the keys and values are percent escaped
func dataFromHttpParameters() -> Data {
let parameterArray = self.map { (key, value) -> String in
let percentEscapedKey = (key as! String).addingPercentEncodingForURLQueryValue()!
let percentEscapedValue = httpStringRepresentation(value).addingPercentEncodingForURLQueryValue()!
return "\(percentEscapedKey)=\(percentEscapedValue)"
}
return parameterArray.joined(separator: "&").data(using: .utf8)!
}
}
Here, because I'm dealing with an array of parameter strings, I use the join function to concatenate them separated by &, but the idea the same.
Feel free to customize that function to handle whatever data types you may be passing into it (e.g. I don't generally have CLLocationCoordinate2D in there, but your example included one, so I wanted to show what it might look like). But the key is that if you're supplying any fields that include user input, make sure to percent-escape it.
FYI, this is my rfc3339String function which is used above. (Clearly, if you don't need to transmit dates, you don't need this, but I'm including it for the sake of completeness for a more generalized solution.)
extension Date {
/// Get RFC 3339/ISO 8601 string representation of the date.
///
/// For more information, see:
///
/// https://developer.apple.com/library/ios/qa/qa1480/_index.html
///
/// - returns: Return RFC 3339 representation of date string
func rfc3339String() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter.string(from: self)
}
}
To see Swift 2 rendition, see previous rendition of this answer.
It can be done by passing the required parameter in the service like this,
var urlPath = NSString(format: "https://maps.googleapis.com/maps/api/place/search/json?key=AIzaSyCRLa4LQZWNQBcjCYcIVYA45i9i8zfClqc&sensor=false&types=restaurant&radius=100000&location=\(locationCoord)")
Here urlPath is the url containing web service and locationCoord (as last parameter) is the run time value for the location parameter for the web service. The parameter key, sensor, radius and types are fixed.
I am calling the json on login button click
#IBAction func loginClicked(sender : AnyObject){
var request = NSMutableURLRequest(URL: NSURL(string: kLoginURL)) // Here, kLogin contains the Login API.
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(self.criteriaDic(), options: nil, error: &err) // This Line fills the web service with required parameters.
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
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 err1: NSError?
var json2 = NSJSONSerialization.JSONObjectWithData(strData.dataUsingEncoding(NSUTF8StringEncoding), options: .MutableLeaves, error:&err1 ) as NSDictionary
println("json2 :\(json2)")
if(err) {
println(err!.localizedDescription)
}
else {
var success = json2["success"] as? Int
println("Succes: \(success)")
}
})
task.resume()
}
Here, I have made a seperate dictionary for the parameters.
var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
return params
}
Based on the above I ended up with this, to get a token within a Set-Cookie element.
Where the URLResponse was
<NSHTTPURLResponse: 0x17403ef20> { URL: http://bla.co.uk//auth/authenticate?email=bob#isp.eu&password=xcode } { status code: 200, headers {
"Cache-Control" = "private, must-revalidate";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Fri, 17 Feb 2017 10:51:41 GMT";
Expires = "-1";
Pragma = "no-cache";
Server = nginx;
"Set-Cookie" = "token=Cu4CmOaverylongstring0mCu4CmOpBGg; expires=Fri, 17-Feb-2017 20:51:41 GMT; Max-Age=36000; path=auth; httponly";
"Transfer-Encoding" = Identity;
"X-Powered-By" = "PHP/5.5.9-1ubuntu4.19, PleskLin";
} }
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
let httpResponse = response as! HTTPURLResponse
let statusCode = httpResponse.statusCode
if statusCode == 200 {
let keyValues = httpResponse.allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }
// Now filter the array, searching for your header-key, also lowercased
if let myHeaderValue = keyValues.filter({ $0.0 == "Set-Cookie".lowercased() }).first {
print(myHeaderValue.1)
let cookies = myHeaderValue.1
let cookieDict = cookies.components(separatedBy: ";")
print("\(cookieDict)")
let tokenEntryParameter = cookieDict.filter({$0 .contains("token")})
let tokenEntry = tokenEntryParameter.first
token = (tokenEntry?.components(separatedBy: "=").last)!
}
}
}