Swift String omit/convert polish accents - swift

I have a following problem:
I am making an API request. With a city name f.e. 'Poznań" (containing some signs typical for some language), swift doesn't want to give me the result, but when I do the same request through Postman app it gives the result in a proper way. How can can I prevent swift from converting those 'strange' letters? 'city.name' is city name that I pass from the previous VC and googlePlaces API. Here is sample of a request and part of my code:
https://samples.openweathermap.org/data/2.5/weather?q=London&appid=b6907d289e10d714a6e88b30761fae22
private let kWeatherAPIURL = "https://api.openweathermap.org/data/2.5/weather?q=%#&appid=%#"
let urlString = String(format: kWeatherAPIURL, city.name, weatherAPIKey)
guard let url = URL(string: urlString) else {
print("address doesnt exist!")
return
}

I am force unwrapping here for brevity:
let kWeatherAPIURL = "https://api.openweathermap.org/data/2.5/weather?q=%#&appid=%#"
let weatherAPIKey = "YourWeatherAPIKey"
let cityName = "Poznań"
let cString = cityName.cString(using: .utf8)!
let utf8CityName = cityName.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
let urlString = String(format: kWeatherAPIURL, utf8CityName, weatherAPIKey)
let url = URL(string: urlString)!
//https://api.openweathermap.org/data/2.5/weather?q=Pozna%C5%84&appid=YourWeatherAPIKey
A safe approach would be to use URL components:
let weatherAPIKey = "YourWeatherAPIKey"
let cityName = "Poznań"
var components = URLComponents()
components.scheme = "https"
components.host = "api.openweathermap.org"
components.path = "/data/2.5/weather"
components.queryItems = [URLQueryItem(name: "q", value: cityName),
URLQueryItem(name: "appid", value: weatherAPIKey)
]
print(components.url!) //https://api.openweathermap.org/data/2.5/weather?q=Pozna%C5%84&appid=YourWeatherAPIKey

An example of using URLComponents.
Prepare a function like this:
func createWeatherAPIURL(cityName: String, apiKey: String) -> URL? {
let kWeatherAPIURL = "https://api.openweathermap.org/data/2.5/weather"
var urlCompo = URLComponents(string: kWeatherAPIURL)
urlCompo?.queryItems = [
URLQueryItem(name: "q", value: cityName),
URLQueryItem(name: "appid", value: apiKey)
]
return urlCompo?.url
}
And use it:
guard let url = createWeatherAPIURL(cityName: city.name, apiKey: weatherAPIKey) else {
print("address doesnt exist!")
return
}

Related

Signature Version 4 Issue on Swift

I really try hard to solve this problem on my project. But I can't.
I try to sign URLRequest headers and body by Amazon AWS Signature Version 4. And send it to server with Alamofire SDK.
But only headers without body work correctly.
I dunno why I get the response "403 forbidden" from server when I put httpBody into URLRequest.
Here is my source.
...
var request = URLRequest(url: convUrl)
if let jsonString = String(data: jsonData, encoding: .utf8) {
AWS4Signer().aws4Sign(request: &request, method: httpMethodType, payload: jsonString, query: [:], path: url.replacingOccurrences(of: self.url.host, with: ""))
}
AF.request(request).responseData { response in
}
func aws4Sign(request: inout URLRequest, method: HttpMethodType, payload: String?, query: [String:String]?, path: String) {
var headers = [String:String]()
let requested_date_time = self.getAmzDate(date: Date())
headers["x-amz-date"] = requested_date_time
// x-amz-content-sha256
var payload_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // empty
if let payload {
let utf8Data = payload.data(using: .utf8) ?? Data()
payload_hash = SHA256.hash(data: utf8Data).hexDigest()
if !payload.isEmpty {
headers["x-amz-content-sha256"] = payload_hash
request.httpBody = utf8Data
}
}
let canonical_querystring = ""
headers["host"] = "hosthost.com"
let sortedHeaders = headers.filter { ["host", "x-amz-date", "x-amz-content-sha256"].contains($0.key) }
.sorted { $0.key < $1.key }
let canonical_headers = sortedHeaders.map { "\($0.key.lowercased()):\($0.value)" }.joined(separator: "\n") + "\n"
let signed_headers = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")
let canonical_uri = path
let canonical_request = "\(method.rawValue)\n\(canonical_uri)\n\(canonical_querystring)\n\(canonical_headers)\n\(signed_headers)\n\(payload_hash)"
let hashed_canonical_request = SHA256.hash(data: canonical_request.data(using: .utf8)!).hexDigest()
let algorithm = "AWS4-HMAC-SHA256"
let nowDate = Date().getString(format: "YYYYMMdd", timeZone: TimeZone(identifier: "UTC")!)
let credential_scope = "\(nowDate)/\(Const.key.awsRegion)/\(Const.key.awsServiceType)/aws4_request"
let string_to_sign = "\(algorithm)\n\(requested_date_time)\n\(credential_scope)\n\(hashed_canonical_request)"
let signing_key = self.getSignatureKey(key: Const.key.awsSecretAccessKey, dateStamp: nowDate, regionName: Const.key.awsRegion, serviceName: Const.key.awsServiceType)
let signature = self.sign(key: signing_key, msg: string_to_sign).hexDigest()
let authorization_header = "\(algorithm) Credential=\(Const.key.awsAccessKeyID)/\(credential_scope), SignedHeaders=\(signed_headers), Signature=\(signature)"
request.addValue(requested_date_time, forHTTPHeaderField: "X-amz-date")
request.addValue(authorization_header, forHTTPHeaderField: "Authorization")
}
func getSignatureKey(key: String, dateStamp: String, regionName: String, serviceName: String) -> Data {
let keyData = Data("AWS4\(key)".utf8)
let dateStampData = Data(dateStamp.utf8)
let regionNameData = Data(regionName.utf8)
let serviceNameData = Data(serviceName.utf8)
let signingData = Data("aws4_request".utf8)
var symmetricKey = SymmetricKey(data: keyData)
let dateSHA256 = HMAC<SHA256>.authenticationCode(for: dateStampData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(dateSHA256))
let regionSHA256 = HMAC<SHA256>.authenticationCode(for: regionNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(regionSHA256))
let serviceNameSHA256 = HMAC<SHA256>.authenticationCode(for: serviceNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(serviceNameSHA256))
let signingSHA256 = HMAC<SHA256>.authenticationCode(for: signingData, using: symmetricKey)
let skeyString = keyData.map { String(format: "%02hhx", $0) }.joined()
let kDateString = Data(dateSHA256).map { String(format: "%02hhx", $0) }.joined()
let kRegionString = Data(regionSHA256).map { String(format: "%02hhx", $0) }.joined()
let kServiceString = Data(serviceNameSHA256).map { String(format: "%02hhx", $0) }.joined()
let kSigningString = Data(signingSHA256).map { String(format: "%02hhx", $0) }.joined()
return Data(signingSHA256)
}
func sign(key: Data, msg: String) -> Data {
let hmac = HMAC<SHA256>.authenticationCode(for: msg.data(using: .utf8)!, using: SymmetricKey(data: key))
return Data(hmac)
}
func getAmzDate(date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
formatter.timeZone = TimeZone(identifier: "UTC")
return formatter.string(from: date)
}
func getCanonicalQuerystring(query: [String: String]) -> String {
let sortedParameters = query.sorted { $0.0 < $1.0 }
let encodedQueryParameters = sortedParameters.map { (key, value) in
return "\(key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")=\(value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")"
}
let canonicalQuerystring = encodedQueryParameters.joined(separator: "&")
return canonicalQuerystring
}
The "Authorization" data has it.
"AWS4-HMAC-SHA256 Credential=ACCESS_KEY_ID/20230213/ap-northeast-2/execute-api/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=cfa667aa18472d3d5a419f67aa2c321977428abac391a33f9f3e31839bcb4665"
ACCESS_KEY_ID is my access_key_id of AWS.
So far, only the POST method is tested.

Swift - Why is my JSON object element only adding the last array element?

I have a problem with my JSON object. Everything is working fine creating and printing out my JSON object, apart from the idQty part. It only prints the last key value result. I assume I have a problem with my for loop. If anybody can point out where I've went wrong, it would be of huge help.
Code below:
struct Order: Codable {
let idQty: [IdQty]
let collection: String
let name: String
let phone: Int
let doorNum: Int
let street: String
let postcode: String
}
struct IdQty: Codable {
let itemId: Int
let qty: Int
}
class CheckoutServer: NSObject, URLSessionDataDelegate {
var inputVals = [Int:Int]()
var idQty = [IdQty]()
var collection = String()
var name = String()
var phone = Int()
var doorNum = Int()
var street = String()
var postcode = String()
var request = URLRequest(url: NSURL(string: "http://192.168.1.100/api/AddOrder.php")! as URL)
func downloadItems() {
for(key,value) in inputVals {
idQty = [IdQty(itemId: key,qty: value)]
}
let order = Order(idQty: idQty,collection: collection,name: name,phone: phone,doorNum: doorNum,street: street,postcode: postcode)
let encodedOrder = try? JSONEncoder().encode(order)
var json: Any?
request.httpMethod = "POST"
if let data = encodedOrder {
json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let json = json {
}
}
let postParameters = "json="+String(describing: json!)
request.httpBody = postParameters.data(using: .utf8)
print(String(describing: json!))
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Failed to upload data at Menu Type Items")
} else {
print("Data uploaded")
}
}
task.resume()
}
}
Below is the output. the 'idQty' part only ever returns the last entry in the [Int:Int] dictionary:
{
collection = Delivery;
doorNum = 4;
idQty = (
{
itemId = 14;
qty = 2;
}
);
name = James;
phone = 4355345;
postcode = Test;
street = TestStreet;
}
You should append new value to your array instead of recreating it on each iteration
for(key,value) in inputVals
{
idQty.append(IdQty(itemId: key,qty: value))
}

How can I use a # symbol instead of ? in URLQueryItem?

If I create a URL like this:
guard var urlComponents = URLComponents(url: "https://example.com/something", resolvingAgainstBaseURL: false) else {
return
}
let queryItems = URLQueryItem(name: "token", value: token)
urlComponents.queryItems = [queryItems]
guard let urlWithQueryItem = urlComponents.url else {
return
}
I want the end result to be something like https://example.com/something#token=7as78f6asd678768asd768asd678
Instead of the default https://example.com/something?token=7as78f6asd678768asd768asd678
(I'm looking for something smarter than a search and replace of the ? character)
Thanks
As others have noted, URLQueryItem object is more intended for use with querystrings vs anchors/fragments. With that said, below is a function that helps you accomplish what your questions is asking.
func constructURL(withURL urlBase: String, andFragmentFromQueryItem queryItem: URLQueryItem) -> URL?{
guard let url = URL(string:urlBase) else {
return nil
}
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return nil
}
let fragmentKey = queryItem.name
let fragmentValue = queryItem.value ?? ""
urlComponents.fragment = "\(fragmentKey)=\(fragmentValue)"
guard let urlWithFragment = urlComponents.url else {
return nil
}
return urlWithFragment
}
let url = constructURL(withURL:"https://example.com/something",
andFragmentFromQueryItem: URLQueryItem(name: "token", value: "tokenValue"))
let urlString = url?.absoluteString
print("\(urlString!)")
Here is a link to a working Swift fiddle of the code.
http://play.swiftengine.io/code/8JKI3/2
As #rmaddy points out, there is a definite difference between the meaning of ? and #. '?' introduces query parameters, which are part of the URL. '#' introduces the "fragment" which is not considered part of the URL.
You can, however use URLComponents to add a fragment to a URL. Just use the fragment property instead:
urlComponents.fragment = "token=\(token)"

how to use random string to let or var to url link

how to use random string to let or var to url link
i want to make random string for url
let url = URL(string:"https://www.pallive.net/random.json")
or see the code when i change values in the site linke in the app do not changed,but if i chnage name of url it change
the code not reload if i change the value in json and keep same file
if i want to reload i have to change the name of file how to do some thange
auotmatic change the url and keep the orginal in the ftp server
import Foundation
class Episode
{
var title: String?
var description: String?
var thumbnailURL: URL?
var url: URL?
var episodes = [Episode]()
init(title: String, description: String, thumbnailURL: URL, createdAt: String, author: String)
{
self.title = title
self.description = description
self.thumbnailURL = thumbnailURL
}
init(espDictionary: [String : AnyObject])
{
self.title = espDictionary["title"] as? String
// description = espDictionary["description"] as? String
thumbnailURL = URL(string: espDictionary["thumbnailURL"] as! String)
self.url = URL(string: espDictionary["link"] as! String)
}
static func downloadAllEpisodes(completion: #escaping ([Episode]) -> ()) {
var episodes = [Episode]()
let url = URL(string:"https://www.pallive.net/random.json")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
completion(episodes)
}
else {
if let jsonData = data ,let jsonDictionary = NetworkService.parseJSONFromData(jsonData) {
let espDictionaries = jsonDictionary["episodes"] as! [[String : AnyObject]]
for espDictionary in espDictionaries {
let newEpisode = Episode(espDictionary: espDictionary)
episodes.append(newEpisode)
}
}
completion(episodes)
DispatchQueue.main.async(execute: {
completion(episodes)
})
}
}.resume()
}
func randomString(_ length: Int) -> String {
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
}

convert string unicode 8 swift

i was a nsdata from json
when i put in string i get "#1495;#1493;#1507; #1492;#1510;#1493;#1511; +& Before each tag
i need it in Hebrew
the code is
let json_url = NSURL(string: "http://itaypincas7.ipage.com/WigiTech-DB/createJSON.php")
let jsonData = NSData(contentsOfURL: json_url!)
do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
for (_, subJson) in json {
let id = Int(subJson["id"] as! String)
let name: String = subJson["name"] as! String
let area: String = subJson["area"] as! String
let latitude = CLLocationDegrees(CGFloat(Float(subJson["latitude"] as! String)!))
let longitude = CLLocationDegrees(CGFloat(Float(subJson["longitude"] as! String)!))
let beachNew = BeachNew(id: id!, name: name, area: area, latitude: latitude, longitude: longitude)
beachesList.append(beachNew)
}
You can use NSAttributedString to replace the HTML entities. Here's an example for decoding the name variable.
let name: String = subJson["name"] as! String
let nameData = name.dataUsingEncoding(NSUTF8StringEncoding)!
let options: [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedName = try NSAttributedString(data: nameData, options: options, documentAttributes: nil)
let decodedName = attributedName.string
The value of decodedName will have all entities replaced with the Hebrew characters you're looking for.