Im building a swift package that will be a wrapper to an api. Since its going to be used by many people with many different needs, I have to fully support what the api offers.
There are 10 different query parameters all of which are optional to make the network request. I have a couple different ways of doing this, but I want to see if there is an optimal way im unaware of.
// only showing 3 params but I have 10 optional params
func getData(query: String, limit: Int, page: Int) {
let url: String = "https://baseurl.com"
if let query = query {
url += "?query=\(query)"
}
if let limit = limit {
url += "?limit=\(limit)"
}
// do this for all 10 query params
// make network request with url
}
next option was something like creating an object for my params but i think id be in the same position for unpacking the data
struct Params {
var query: String?
var limit: Int?
var page: Int
func createStringFromParams() -> String {
let queryParams: String = ""
if let query = query {
url += "?query=\(query)"
}
// do this for all params
}
}
var params: Params()
params.query = "my query"
func getData(params: Params) {
var url: String = "https://baseurl.com/\(params.createStringFromParams())"
}
I also will be doing this for many different functions as every api endpoint has 0-many query params.
I'd suggest to use URLQueryItem
& URLComponents
.
Basic code being:
let baseUrl: String = "https://baseurl.com/"
var components = URLComponents(string: baseUrl)
let queryParameters = [URLQueryItem(name: "query", value: "myQuery")]
//If empty and set, it will add a "?" at the end of the URL, start of the query with no parameters
if !queryParameters.isEmpty {
components?.queryItems = queryParameters
}
let finalURL = components?.url
Now, since you want to limit the values/calls, you could use an enum with associated values:
enum Params {
case query(String)
case limit(Int)
case page(Int)
func queryItem() -> URLQueryItem {
switch self {
case .limit(let limit):
return URLQueryItem(name: "limit", value: "\(limit)")
case .page(let page):
return URLQueryItem(name: "page", value: "\(page)")
case .query(let query):
return URLQueryItem(name: "page", value: query)
}
}
}
func createURL(with params: [Params]) {
let baseUrl: String = "https://baseurl.com/"
var components = URLComponents(string: baseUrl)
//If empty and set, it will add a "?" at the end of the URL, start of the query with no parameters
if !queryParameters.isEmpty {
components?.queryItems = queryParameters.map { $0.queryItem() }
}
let finalURL = components?.url
print(finalURL!.absoluteString)
}
If you still want to use your parameter struct:
struct Params {
let query: String?
let limit: Int?
let page: Int?
init(query: String? = nil, limit: Int? = nil, page: Int? = nil) {
self.query = query
self.limit = limit
self.page = page
}
func queryItems() -> [URLQueryItem] {
var items: [URLQueryItem] = []
if let query = query {
items.append(URLQueryItem(name: "query", value: query))
}
if let limit = limit {
items.append(URLQueryItem(name: "limit", value: "\(limit)"))
}
if let page = page {
items.append(URLQueryItem(name: "page", value: "\(page)"))
}
return items
}
}
func createURL(with params: Params) {
let baseUrl: String = "https://baseurl.com/"
var components = URLComponents(string: baseUrl)
//If empty and set, it will add a "?" at the end of the URL, start of the query with no parameters
let queryParameters = params.queryItems()
if !queryParameters.isEmpty {
components?.queryItems = queryParameters
}
let finalURL = components?.url
print(finalURL!.absoluteString)
}
In my opinion, both approach are valid. There are just less if let
in the enum
solution.