У меня есть вычисляемое свойство типа Parameters в моем APIRouter
// MARK: - Parameters
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters = ["main_category_id": doctorsFilter.0, "page": doctorsFilter.1, "specialty_id": doctorsFilter.2, "city_id": doctorsFilter.3, "region_id": doctorsFilter.4, "name": doctorsFilter.5, "company_id": doctorsFilter.6, "order_by": doctorsFilter.7]
return params
default:
return nil
}
}
Некоторые значения в Typealias, называемые doctorFilter, являются необязательными. в настоящее время у меня есть предупреждение с просьбой предоставить значение по умолчанию для необязательных значений, и я не хочу предоставлять значения по умолчанию, я хочу проверить, существует ли значение, чтобы добавить его, иначе я не добавлю ключ и значение
Как я могу безопасно развернуть необязательные значения и добавить их в словарь параметров, не сказав, разрешено ли для всех необязательных значений? пример:
if let specialtyID = doctorsFilter.2 {
params["specialty_id"] = specialtyID
}
Я не хочу разворачивать его таким образом, так как я проверю все необязательные значения, и это займет больше строк кода.
Обновлено:- тип DoctorsFilter задокументирован, когда я инициализирую экземпляр типа DoctorsFilter, автозаполнение сообщает мне, какой из них является чем, я думал о создании класса DoctorsFilter раньше, но я ищу другой способ, если таковой имеется, возможно, встроенный зарезервированное слово может справиться со всей ситуацией! , я хочу сделать это максимально простым. создание функции, которая обрабатывает словарь и возвращает его в классе DoctorsFilter, является опцией. Я думаю добавить эту функцию в APIRouter, нормально ли добавить ее туда? это правило APIRouter для обработки параметров? или APIRouter просто заинтересован в получении параметров и не будет их обрабатывать?
@КирилС. ну, тип DoctorsFilter задокументирован, когда я инициализирую экземпляр типа DoctorsFilter, автозаполнение говорит мне, какой из них что, я думал о вашем «нормальном типе» раньше, но я ищу другой способ, если таковой имеется, может быть встроенное зарезервированное слово может справиться со всей ситуацией! , я хочу сделать это максимально простым. поэтому я ценю ваш ответ, но ваше предложение будет моим последним вариантом
@КирилС. вопрос отредактирован
Есть три основных способа безопасно развернуть необязательный файл. Вы также можете указать значения по умолчанию, если хотите развернуть необязательный.
var firstString: String?
// In some cases you might performa a break, continue or a return.
guard let someString = firstString else { return }
print(someString)
var secondString: String?
var thirdString: String?
thirdString = "Hello, World!"
// Notice that we are able to use the same "IF LET" to unwrap
// multiple values. However, if one fails, they all fail.
// You can do the same thing with "Guard" statements.
if let someString = secondString,
let someOtherString = thirdString {
print(someString)
print(someOtherString)
} else {
// With this code snippet, we will ALWAYS hit this block.
// because secondString has no value.
print("We weren't able to unwrap.")
}
var fourthString: String?
// The ?? is telling the compiler that if it cannot be unwrapped,
// use this value instead.
print(fourthString ?? "Hello, World")
В Swift рекомендуется каждый раз, когда вы видите !, использовать какую-либо форму развертывания. Swift очень «безопасен для типов».
Вот ресурс, который вы можете использовать для опций. https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html
Ваше решение может выглядеть примерно так.
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
if let mainCatID = doctorsFilter.0,
let page = doctorsFilter.1,
let specialtyID = doctorsFilter.2,
let cityID = doctorsFilter.3,
let regionID = doctorsFilter.4,
let name = doctorsFilter.5,
let companyID = doctorsFilter.6,
let orderBy = doctorsFilter.7 {
params: Parameters = ["main_category_id": mainCatID,
"page": page,
"specialty_id": specialtyID,
"city_id": cityID,
"region_id": regionID,
"name": name,
"company_id": companyID,
"order_by": orderBy]
return params
} else {
//Unable to safely unwrap, return nothing.
return nil
}
default:
return nil
}
}
Я ценю ваш ответ, но вы не получили мой вопрос, я не спрашиваю, как безопасно развернуть необязательные значения, я спрашиваю, как обрабатывать необязательные значения, связанные с конкретной ситуацией
Мой ответ, ответ на ваш вопрос. Просто игнорируйте оператор Else или используйте guard let вместо if let. Если вы не можете развернуть, он не войдет в этот блок кода, что позволит вам не добавлять свои kvp.
Пожалуйста, внимательно прочитайте мой вопрос, я не хочу говорить, допустимо ли для каждого необязательного значения, также я не хочу указывать значение по умолчанию. Я ищу простой способ добавить ключи и значения в словарь без написания 10 строк кода для развертывания необязательных значений!
@ZiadKamel, тогда вы просите что-то под названием «Принудительное развертывание», которое приведет к сбою вашего приложения. Вы ДОЛЖНЫ развернуть их, что является соглашением. В противном случае в вашем классе doctorFilter вам нужно убедиться, что эти значения не являются необязательными.
Создание функции, которая обрабатывает словарь и возвращает его в классе DoctorsFilter, является опцией. Я думаю добавить эту функцию в APIRouter, нормально ли добавить ее туда? это правило APIRouter для обработки параметров? или APIRouter просто заинтересован в получении параметров и не будет их обрабатывать?
Я бы сказал, что где вы это поместите, зависит от вас. Не существует соглашения о том, где вы обрабатываете данные, за исключением некоторых стандартов кодирования, предоставляемых вашей компанией, или стандартов, которым вы можете следовать. Общее эмпирическое правило: если это работает, не связывайтесь с этим.
Не существует «однострочного» решения, но вы можете использовать KeyPaths, чтобы сократить серию операторов if let ... до цикла.
Начните с создания структуры для вашего фильтра, а не с кортежа.
Чтобы облегчить это, мы определяем протокол для Parameterable. Для этого протокола требуется словарь, который сопоставляет имена параметров (String) со свойством (KeyPath), которое содержит это имя параметра, а также функцию для возврата словаря параметров.
protocol Parameterable {
var paramNames: [String:KeyPath<Self,String?>] {get}
func parameters() -> [String:Any]
}
Используйте расширение, чтобы создать реализацию функции parameters() по умолчанию, так как код будет одинаковым для всех Parameterable. Он перебирает записи словаря и использует связанный KeyPath для доступа к соответствующему свойству и помещения его в выходной словарь. Если данное свойство равно nil, то оно просто не добавляется в выходной словарь, потому что так работают словари. Нет необходимости явно проверять.
(Если вы импортируете Alamofire, вы можете использовать typedef Parameters там, где я использовал [String:Any])
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
parameters[paramName]=self[keyPath:keypath]
}
return parameters
}
}
Используйте этот протокол для создания реализации DoctorsFilter:
struct DoctorsFilter: Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: String?
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:KeyPath<Self,String?>] = [
"main_category_id":\.mainCategoryId,
"page":\.page,
"specialty_id":\.specialtyId,
"city_id":\.cityID,
"region_id":\.regionId,
"name":\.name,
"company_id":\.companyId,
"order_by":\.orderBy]
}
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
return doctorsFilter.parameters()
case .someOtherThing(let someOtherThing):
return someOtherThing.parameters()
default:
return nil
}
}
}
Другой подход состоит в том, чтобы просто разделить создание словаря parameters на несколько строк; Если вы присвоите nil ключу словаря, то в словаре для этого ключа не будет храниться пара ключ/значение. В этом случае я оставил ваш подход с кортежами на месте, но вы можете использовать структуру (и я настоятельно рекомендую вам это сделать)
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters()
params["main_category_id"] = doctorsFilter.0
params["page"] = doctorsFilter.1
params["specialty_id"] = doctorsFilter.2
params["city_id"] = doctorsFilter.3
params["region_id"] = doctorsFilter.4
params["name"] = doctorsFilter.5
params["company_id"] = doctorsFilter.6
params["order_by"] = doctorsFilter.7
return params
default:
return nil
}
}
Если мы хотим обрабатывать смешанные свойства, а не только необязательные строки, нам нужно немного изменить код. Нам нужно использовать PartialKeyPath. Это делает код немного более сложным, так как оператор нижнего индекса для PartialKeyPath возвращает двойной необязательный параметр. С этим нужно справиться.
protocol Parameterable {
var paramNames: [String:PartialKeyPath<Self>] {get}
func parameters() -> [String:Any]
}
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
let value = self[keyPath:keypath] as? Any?
if let value = value {
parameters[paramName] = value
}
}
return parameters
}
}
struct DoctorsFilter:Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: Int
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:PartialKeyPath<Self>] =
["main_category_id":\Self.mainCategoryId,
"page":\Self.page,
"specialty_id":\Self.specialtyId,
"city_id":\Self.cityID,
"region_id":\Self.regionId,
"name":\Self.name,
"company_id":\Self.companyId,
"order_by":\Self.orderBy]
}
Идеальный! этот ответ работает со значениями String, но что, если мои параметры включают значения Int и значения String
Я действительно разработал это решение, но не опубликовал его, потому что оно не такое элегантное; вам нужно обрабатывать некоторые двойные опции, возникающие при использовании AnyKeyPath. Я обновлю свой ответ позже.
Я ценю ваши усилия, и мне нужно решение, надеюсь, вы скоро обновите свой ответ. Спасибо
Я сделал; это внизу, используя PartialKeyPath
Я думаю, вам нужно начать с превращения doctorsFilter в нормальный тип: иметь septuple (например, doctorsFilter.0, doctorsFilter.3) плохо, иметь septuple с опционами еще хуже. Ты хоть знаешь, кто из них что? И, может быть, вы это сделаете, но через год кто сможет поддерживать такой код? Как только ваш doctorsFilter станет обычным типом, этот нормальный тип также может иметь функцию (или свойство компьютера), которая возвращает Parameters только с доступными значениями.