How to send Dictionary as parameter to the Get Request Swift - ios

i have to call GET API like
required :
http://ctesterst.net/api/Customers/homeData?lat=12.456&lng=76.786&current_app_version=123&current_device=%7B%22Test%22%3A%22123%22%2C%22name%22%3A%22something%22%2C%22tested%22%3Atrue%7D&access_token=Km3R2AbU0yzAcVkp9BCxmCmaoC5k20fBiQxfLhIBIAolwJGgYw5w5E8X0NZzlDh8
like that i have to call.
input parameters how to add ??
i am doing like this :
my json string is : request model
struct RequestData: Codable {
struct Devices: Codable {
let test: String
let name: String
let tested:Bool
}
let current_device: Devices
}
and my url is :
urlAppend = val + "&lat=\(lati)" + "&lng=\(long)" + "&current_app_version=\(appVersion)" + "&current_device=\(jsonString)"
its getting crash .. please give me solution .
Thanks

You can use URLComponents for this, URLComponents is defined in Foundation.
var components = URLComponents()
components.scheme = "http"
components.host = "ctesterst.net"
components.path = "/api/Customers/homeData"
let queryItemLat = URLQueryItem(name: "lat", value: "12.456")
let queryItemLong = URLQueryItem(name: "lng", value: "76.786")
let queryItemAppVersion = URLQueryItem(name: "current_app_version", value: "123")
let queryItemDevice = URLQueryItem(name: "current_device", value: "something")
let queryItemToken = URLQueryItem(name: "access_token", value: "Km3R2AbU0yzAcVkp9BCxmCmaoC5k20fBiQxfLhIBIAolwJGgYw5w5E8X0NZzlDh8")
components.queryItems = [queryItemLat, queryItemLong,queryItemAppVersion,queryItemDevice,queryItemToken]
print(components.url)

Related

Swift prevent Optional("") or nil in string concatenate result | Create query string from object

I have struct like this:
struct Request {
var page: Int
var name: String?
var favoriteName: String?
var favoriteId: Int?
}
Then I convert it to Dictionary
func toDict() -> [String:Any] {
var dict = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value
}
}
return dict
}
If I loop it to modify and concatenate using those Dict key & value to create query string:
var queryString: String {
var output: String = ""
for (key,value) in toDict() {
output += "\(key)" + "=\(value)&"
}
output = String(output.dropLast())
return output
}
Does anyone know how to prevent nil, Optional(""), Optional(25) string added in the concatenation process ?
current result: page=20&name=nil&favoriteName=Optional("")&favoriteId=Optional(25)
expected result: page=20&name=&favoriteName=&favoriteId=25
Edit: Thank you everyone , wasn't really expecting so much answer tho. Let me edit the title to help future developers search.
The best practice is to use the URLComponents and URLQueryItem structs.
This is my approach to solving your problem.
First I added an enum to avoid having hardcoded strings.
struct Request {
var page: Int
var name: String?
var favoriteName: String?
var favoriteId: Int?
}
enum RequestValues: String {
case page
case name
case favoriteName
case favoriteId
}
Then I made this helper function to return the non nil vars from the Request instance as an array of URLQueryItem.
func createQuery(request: Request) -> [URLQueryItem] {
var queryItems: [URLQueryItem] = []
queryItems.append(URLQueryItem(name: RequestValues.page.rawValue, value: "\(request.page)"))
if let name = request.name {
queryItems.append(URLQueryItem(name: RequestValues.name.rawValue, value: name))
}
if let favoriteName = request.favoriteName {
queryItems.append(URLQueryItem(name: RequestValues.favoriteName.rawValue, value: favoriteName))
}
if let favoriteId = request.favoriteId {
queryItems.append(URLQueryItem(name: RequestValues.favoriteId.rawValue, value: "\(favoriteId)"))
}
return queryItems
}
Then you can get the query string like this:
let queryString = queryItems.compactMap({ element -> String in
guard let value = element.value else {
return ""
}
let queryElement = "\(element.name)=\(value)"
return queryElement
})
this will give you the expected result in your question.
page=20&name=&favoriteName=&favoriteId=25
But you should use the URLComponents struct to build your url as such.
func buildURL() -> URL? {
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = "google.com"
urlComponents.queryItems = queryItems
urlComponents.path = "/api/example"
urlComponents.url
guard let url = urlComponents.url else {
print("Could not build url")
return nil
}
return url
}
This would will give you the url with the query.
It would look like this :
https://google.com/api/example?page=5&name=nil&favoriteName=Hello&favoriteId=9
In model have optioanl var.
First, make your dictionary value optional
func toDict() -> [String: Any?] {
and then use the default value
var queryString: String {
var output: String = ""
for (key,value) in toDict() {
output += "\(key)" + "=\(value ?? "")&" // <== Here
}
output = String(output.dropLast())
return output
}
You can also prevent nil value in string
var queryString: String {
var output: String = ""
for (key,value) in toDict() where value != nil { //< Here
output += "\(key)" + "=\(value ?? "")&" //< Here
}
output = String(output.dropLast())
return output
}
Check if value is nil before insert it in the output, if the value is nil you can provide a default value:
var queryString: String {
var output: String = ""
for (key,value) in toDict() {
output += "\(key)"
if let value = value{
output += "=\(value)&"
}else{
output += "=DefaultValue&" // Replace DefaultValue with what is good for you
}
}
output = String(output.dropLast())
return output
}
This is more concise:
var queryString: String {
var output: String = ""
for (key,value) in toDict() {
output += "\(key)" + "=\(value ?? "DefaultValue")&"
}
output = String(output.dropLast())
return output
}
Instead of manually creating your string you should use URLComponents to generate your URL and use URLQueryItems for your parameters. I would make your structure conform to Encodable and encode NSNull in case the value is nil. Then you just need to pass an empty string to your query item when its value is NSNull:
This will force encoding nil values:
extension Request: Codable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(page, forKey: .page)
try container.encode(name, forKey: .name)
try container.encode(favoriteName, forKey: .favoriteName)
try container.encode(favoriteId, forKey: .favoriteId)
}
}
let request = Request(page: 20, name: nil, favoriteName: nil, favoriteId: 25)
do {
let dictiony = (try JSONSerialization.jsonObject(with: JSONEncoder().encode(request))) as? [String: Any] ?? [:]
let queryItems: [URLQueryItem] = dictiony.reduce(into: []) {
$0.append(.init(name: $1.key, value: $1.value is NSNull ? "" : String(describing: $1.value)))
}
print(queryItems)
var components = URLComponents()
components.scheme = "https"
components.host = "www.google.com"
components.path = "/search"
components.queryItems = queryItems
if let url = components.url {
print(url)
}
} catch {
print(error)
}
This will print
[name=, favoriteName=, page=20, favoriteId=25]
https://www.google.com/search?favoriteName=&name=&page=20&favoriteId=25

Xcode returns nil on string that isn't optional

I have a function that takes an image and a string. When I try to put the string into a longer string using the () ability, it tells me it finds nil while unwrapping an optional. Exception it's not an optional at all, it's a string. I can print the value out and it comes out correctly.
func UpdateBusiness(logo: UIImage, category: String) {
guard let bizID = UserDefaults.standard.string(forKey: defaultKeys.businessID) else {return}
let thisURL = "http://mywebsite.com/api/v0.1/Business/EditBusinessLogoAndCategory?businessID=\(bizID)&category=\(category)"
let combinedURL = URL(string: thisURL)!
}
creating the URL crashes the system. I can see the value of category in the debugger, and I have no optionals in this string. How can it find nil?
This code is crashing due to force unwrapping. In this case can recommend to use URLComponents.This is more readable than the string joining and for large number of parameter string joining is not a good option.
var components = URLComponents()
components.scheme = "http"
components.host = "mywebsite.com"
components.path = "/api/v0.1/Business/EditBusinessLogoAndCategory"
components.queryItems = [
URLQueryItem(name: "businessID", value: bizID),
URLQueryItem(name: "category", value: category)
]
let url = components.url
enter code here

how to pass value to struct dynamically

I am trying to form an URL dynamically.
where I am using Struct to construct it form resultant url
Constants.Swift
import Foundation
private struct Domains {
static let Local = "localhost IP"
static let QA = "QA.Environment"
static let UAT = "http://test-UAT.com"
}
// HardCoded URLRoutes
private struct URLRoutes{
static let query = "query=bitcoin&"
static let date = "date=2019-04-04&"
static let sortBy = "sortBy=Date&"
}
private static let constructionURL = Domain.local+URLRoutes.query + URLRoutes.date + URLRoutes.sortBy + API.Key
static var ResultantURL: String {
return constructionURL
}
I am trying to make this dynamic to pass value from other class to form the dynamic url.
private struct URLRoutes{
var query : String
var date : String?
var sortBy : String?
}
From another Class trying to access the resultant URL
url = URL(string: URLRoutes.ResultantURL)!
but how can I construct the formate of url from the another class?
static let query = "query=bitcoin&"
static let date = "date=2019-04-04&"
static let sortBy = "sortBy=Date&"
Your inputs will guide me.
Here's playground code that does what you want:
struct API {
static let Key = "ABC123"
}
struct URLRoutes{
var query : String
var date : String?
var sortBy : String?
var constructionURL: String {
return query + (date ?? "") + (sortBy ?? "") + API.Key
}
}
let query = "query=bitcoin&"
let date = "date=2019-04-04&"
let sortBy = "sortBy=Date&"
let myRoute = URLRoutes(query: query, date: date, sortBy: sortBy)
print(myRoute.constructionURL)
However, this isn't really ideal and doesn't use the constructs that Apple provides. Here's another approach:
struct URLRoute {
var queryItems:[URLQueryItem]
init(query: String, date:String?, sortBy:String?) {
queryItems = [
URLQueryItem(name: "query", value: query),
URLQueryItem(name: "date", value: date),
URLQueryItem(name: "sortBy", value: sortBy),
URLQueryItem(name: "api_key", value: API.Key)
]
}
var constructionURL:String {
get {
var component = URLComponents(string: "")
component?.queryItems = queryItems
return component?.string ?? ""
}
}
}
let betterRoute = URLRoute(query: "bitcoin", date: "2019-04-04", sortBy: "Date")
print(betterRoute.constructionURL)
You can use URLComponents to do lots of heavy lifting for you in creating valid URLs.

Adding query parameter to the GET url in iOS in Swift3

I have an URL with
https://api.asiancar.com/api/applicationsettings
It is basically a GET url so I need to pass a boolean "isMobile" and timestamp as query parameters . How to achieve this as the ultimate URL after passing the query will look like this:
https://api.asiancar.com/api/applicationsettings?timestamp=111122244556789879&isMobile=true
let queryItems = [
NSURLQueryItem(timestamp: "1234568878788998989", isMobile: true),
NSURLQueryItem(timestamp: "1234568878788998989", isMobile: true)
]
let urlComps = NSURLComponents(string: "www.api.asiancar.com/api/applicationsettings")!
urlComps.queryItems = queryItems
let URL = urlComps.URL!
Am I doing right or any other modification ? please tell.
Unless you have subclassed NSURLQueryItem, then your init method is not correct. Per Apple's documentation for NSURLQueryItem, the init method signature is:
init(name: String, value: String?)
This means your query items should be created like this:
let queryItems = [NSURLQueryItem(name: "timestamp" value: "1234568878788998989"), NSURLQueryItem(name: "isMobile", value: "true")]
This will properly add them to the url in the format you are expecting.
You can try an alternative way by using String
let baseUrlString = "https://api.asiancar.com/api/"
let timeStamp = 1234568878788998989
let isMobile = true
let settingsUrlString = "\(baseUrlString)applicationsettings?timestamp=\(timeStamp)&isMobile=\(isMobile)"
print(settingsUrlString)
let url = URL(string: settingsUrlString)
output : https://api.asiancar.com/api/applicationsettings?timestamp=1234568878788998989&isMobile=true
Try this :
let API_PREFIX = "www.api.asiancar.com/api/applicationsettings"
var url : URL? = URL.init(string: API_PREFIX + queryItems(dictionary: [name: "isMobile", value: "true"] as [String : Any]))
func queryItems(dictionary: [String:Any]) -> String {
var components = URLComponents()
print(components.url!)
components.queryItems = dictionary.map {
URLQueryItem(name: $0, value: $1 as String)
}
return (components.url?.absoluteString)!
}

How to build a URL by using Dictionary in Swift

So I am messing around with the iMessages Framework, and in order to send data to another user the data must be sent as a URLComponents URL. However I can't figure out how to send a dictionary to use as the messages.url value.
func createMessage(data: dictionary) {
/*
The Dictionary has 3 values, 1 string and two arrays.
let dictionary = ["title" : "Title Of Dictionary", "Array1" : [String](), "Array2" : [String]() ] as [String : Any]
*/
let components = URLComponents()
let layout = MSMessageTemplateLayout()
layout.image = UIImage(named: "messages-layout.png")!
layout.imageTitle = "\(dictionary["title"])"
let message = MSMessage()
message.url = components.url!
message.layout = layout
self.activeConversation?.insert(message, completionHandler: { (error: Error?) in
print("Insert Message")
})
}
Does anybody know how I can send the dictionary values as URLQueryItems in the URLComponents to save as the message.url ?
PS: I was wondering wether it would be possible to create an extension for the URL to store the dictionary in, that is what I am unsuccessfully trying atm.
Here is a code snippet to convert dictionary to URLQueryItems:
let dictionary = [
"name": "Alice",
"age": "13"
]
func queryItems(dictionary: [String:String]) -> [URLQueryItem] {
return dictionary.map {
// Swift 3
// URLQueryItem(name: $0, value: $1)
// Swift 4
URLQueryItem(name: $0.0, value: $0.1)
}
}
var components = URLComponents()
components.queryItems = queryItems(dictionary: dictionary)
print(components.url!)
You can use the following URLComponents extension:
extension URLComponents{
var queryItemsDictionary : [String:Any]{
set (queryItemsDictionary){
self.queryItems = queryItemsDictionary.map {
URLQueryItem(name: $0, value: "\($1)")
}
}
get{
var params = [String: Any]()
return queryItems?.reduce([:], { (_, item) -> [String: Any] in
params[item.name] = item.value
return params
}) ?? [:]
}
}
}
And set the url components:
var url = URLComponents()
url.queryItemsDictionary = ["firstName" : "John",
"lastName" : "Appleseed"]
You can use iMessageDataKit library. It's a tiny, useful MSMessage extension for setting/getting Int, Bool, Float, Double, String and Array values for keys.
It makes setting and getting data really easy like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "user_id")
message.md.set(value: "john", forKey: "username")
message.md.set(values: ["joy", "smile"], forKey: "tags")
print(message.md.integer(forKey: "user_id")!)
print(message.md.string(forKey: "username")!)
print(message.md.values(forKey: "tags")!)
(Disclaimer: I'm the author of iMessageDataKit)

Resources