I am unable to hide the validation errors based on the value of each SegmentedRow (upon switching).
Edit: Eureka version 4.0.1
What I have tried: Switching over the val and comparing to each ImportSelectionType
I can hide them for an individual SegmentedRow value: code below.
$0.hidden = Condition.function(["segment"], { form in
if let val = form.rowBy(tag: "segment")?.baseValue as? String {
// TODO: make it work in all cases
return val != ImportSelectionType.keystore.title
}
return false
})
How could I make this generic so it will work in all cases?
Edit: ImportSelectionType is declared like so.
enum ImportSelectionType {
case keystore
case privateKey
case mnemonic
case watch
var title: String {
switch self {
case .keystore:
return "Keystore"
case .privateKey:
return "Private Key"
case .mnemonic:
return "Mnemonic"
case .watch:
return "Watch"
}
}
init(title: String?) {
switch title {
case ImportSelectionType.privateKey.title?:
self = .privateKey
case ImportSelectionType.watch.title?:
self = .watch
case ImportSelectionType.mnemonic.title?:
self = .mnemonic
default:
self = .keystore
}
}
}
It seems updating to Eureka 4.1.1 solved the issue.
Related
I have an enum describing various error codes that can be returned from a server:
public enum LoginError: Int, Error {
case missingUsername = 30001
case missingPassword = 30002
case incorrectPassword = 30003
case unknownUser = 30005
case couldNotBeLoaded = 31002
case sessionExpired = 31001
case unknownError = 30000
}
The server returns the error in the response body. I'm decoding the result and getting the error code, and then matching the error code with the enum:
let decoder = JSONDecoder()
let loginResult = try decoder.decode(LoginResult.self, from: data)
if loginResult.success {
return .success(loginResult)
} else {
switch loginResult.errorCode as! Error {
case LoginError.incorrectPassword:
return .failure(.incorrectPassword)
case LoginError.missingUsername:
return .failure(.missingUsername)
case LoginError.missingPassword:
return .failure(.missingPassword)
case LoginError.unknownUser:
return .failure(.unknownUser)
case LoginError.couldNotBeLoaded:
return .failure(.couldNotBeLoaded)
case LoginError.sessionExpired:
return .failure(.sessionExpired)
default:
return .failure(.unknownError)
}
}
For one, this crashes because I can't cast the LoginError Int type to Error, but also, there must be a more elegant way than this long switch statement. I feel like there's some basic bit of Swift syntax I've missed here. Is there a one-liner that could replace this?
I am looking for solution for a problem I am facing. I have TableView which has multiple cell and each cell has a UISwitch and state of that switch (either on/off) is being set like this:
viewModel.permissions
.drive(tblMessageTypes.rx.items(cellIdentifier: "PermissionCellIdentifier", cellType: PermissionTableViewCell.self)) { [switchSubject] _, item, cell in
cell.backgroundColor = .clear
cell.lblTitle.text = item.permissionTitle
cell.lblDetail.text = item.permissionDescirption
cell.selectionStyle = .none
cell.switchPermission.isOn = item.permissionValue
cell.switchPermission.isEnabled = !item.isPermissionReadOnly
cell.switchPermission.rx.controlEvent(.valueChanged)
.withLatestFrom(cell.switchPermission.rx.value)
.map { isOn in
var newPermission = item
newPermission.permissionValue = isOn
return newPermission
}
.bind(to: switchSubject)
.disposed(by: cell.disposeBag)
}
.disposed(by: disposeBag)
So when Switch is toggled, I am passing an current cell value with update Switch state and based on that I am calling api in my VM like this:
let serverReponse = inputs.switchToggle
.map { permission in
let dicto = permission.toDictionary()
let parameters = ["permissions": [dicto]]
return parameters
} .flatMapLatest { parameters in
userService.updateUserPermission(parameters: parameters)
.trackActivity(inputs.indicator)
.materialize()
}
.share()
Now the issue I have is, if api is failed due to any reason, How should that UISwitch should fallback to initial state, i.e if it was Off and user toggled it to On State and Api was failed it should fall back to Off State.
I would expect to see something like the below. The things to note about this code:
All "permission" objects have some way of uniquely identifying each cell, I'm using UUID here, but you might already have an ID of some sort.
The "permissions" Observable in the ViewModel is used to update each individual cell. All the cells subscribe to this, map out their own Permission object and display it.
As you already have in your view controller, all cells send an updated permission through the switchToggle Observable.
The below code compiles and resets the cells as required. I'm relying on the zip operator to ensure that I am combining the permission that was sent to the request with the response from the server. If the server errors, the permissionValue in the appropriate object in the dictionary will be set to the opposite of what was sent to the server.
The more astute observers (pun intended) of the code below, you will notice that when the server request fails, the permissions object will emit but it won't actually change state since it was the switch that was toggled, not the permissionValue in the dictionary. However, that emission will get picked up by the switch which will cause it to reset itself.
struct Inputs {
let initialPermissions: [Permission]
let switchToggle: Observable<Permission>
}
struct ViewModel {
let permissions: Observable<[UUID: Permission]>
init(_ inputs: Inputs, userService: UserService) {
let serverReponse = inputs.switchToggle
.map { permission in
let dicto = permission.toDictionary()
let parameters = ["permissions": [dicto]]
return parameters
}
.flatMapLatest { parameters in
userService.updateUserPermission(parameters: parameters)
.materialize()
}
.share()
let perms = Dictionary(grouping: inputs.initialPermissions, by: { $0.id })
.mapValues { $0.first! }
permissions = Observable.zip(inputs.switchToggle, serverReponse)
.filter { $0.1.error != nil }
.map { Permission(id: $0.0.id, permissionValue: !$0.0.permissionValue) }
.scan(into: perms, accumulator: { state, update in
state[update.id] = update
})
.startWith(perms)
}
}
Gmail client does not recognize line breaks within text from UIApplication.shared.openURL(url)
I have a function that returns a tuple of available email clients (validated by UIApplication.shared.canOpen) and the associated URL. It's for an error reporting feature, so the arguments array contains the text that will autopopulate email fields.
There are no issues with launching any of the three email clients, but gmail is the only one that doesn't process the line breaks. Does gmail use a different method?
enum EmailClient {
case gmail
case outlook
case mail
var title: String {
switch self {
case .gmail: return "Gmail"
case .outlook: return "Outlook"
case .mail: return "Mail"
}
}
//Creates url used by UIapplciation.shared to launch the client and autopopulate the email
func url(error: CustomError?) -> URL? {
guard let username = CredentialManager.username,
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
return nil
}
let arguments = [
username,
UIDevice().modelName,
UIDevice.current.systemVersion,
appVersion,
error?.description ?? "N/A" //When called from the settings page, no error is passed in
]
var urlFormat: String
switch self {
case .gmail: urlFormat = "googlegmail:///co?to=%#&subject=%#&body=%#"
case .outlook: urlFormat = "ms-outlook://compose?to=%#&subject=%#&body=%#"
case .mail: urlFormat = "mailto:%#?subject=%#&body=%#"
}
return URL(string: String(format: urlFormat, arguments: [
EMAIL_RECIPIENT,
EMAIL_SUBJECT.replacingOccurrences(of: " ", with: "%20"),
String(format: EMAIL_BODY_FORMAT, arguments: arguments).replacingOccurrences(of: " ", with: "%20").replacingOccurrences(of: "\n", with: "%0A")
]))
}
}
It seems that using "\r\n" instead of "\n" fixes the problem
1) Add the the scheme to your info.plist
We can do this through the beautiful thing that is the Info.plist file. Add a new key called LSApplicationQueriesSchemes as an array. Then you can enter your apps within the array. The Mail app doesn’t need to go in here, presumably because it is an Apple app. Your entry should look like the below
func openGmail(withFrom: String?, withSubject: String?) {
var gmailUrlString = "googlegmail:///"
if let from = withFrom {
gmailUrlString += "co?to=\(from)"
}
if let subject = withSubject {
gmailUrlString += "&subject=\(subject)"
}
}
One last thing we will need to do is URL encode the subject line before we pass it into the URL. We can do this by calling subjectString?.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed) on the string.
I have a service class that offers several methods that make calls to the backend, and a common theme for those methods is that I can pass callbacks for the success and error cases:
func makeCall(onSuccess: #escaping APIResponse, onError: #escaping APIError)
The type APIError is defined like so:
typealias APIError = (Error) -> Void
Furthermore I have an enum APICallError like this:
enum APICallError: Error {
case insufficientCredentials
case malformedResponse
case forbidden
}
So in my service methods I can call onError(response.result.error!) if the result contained an error object (the force unwrap is just for brevity, I'm not really doing that) and also pass my own enum value if the HTTP result is not specific enough, like onError(APICallError.insufficientCredentials).
This works fine.
The problem is, I can't figure out how to evaluate in my client code whether the error parameter that's coming in is of type APICallError and which one of those specifically. If I do:
if let callError = error as? APICallError {
if callError == .forbidden {
// ...
}
}
execution doesn't make it past the typecast. if error is APICallError also does not seem to work.
How can I cast the error parameter to my APICallError enum value that I know is in there, because when I print(error) it gives me myapp.APICallError.forbidden?
I tried to simulate what you have posted in your question in Playground, and what you are already doing should work fine for you.
if error is APICallError is also working. One possibility why the if let condition fails might be due to the error being nil. Check if that is the case by using breakpoints.
typealias APIError = (Error) -> Void
//The error type used in your question
enum APICallError: Error {
case insufficientCredentials
case malformedResponse
case forbidden
}
//A different error type for comparison
enum AnotherError: Error {
case error1
case error2
case error3
}
let apiCallError: Error = APICallError.insufficientCredentials
let anotherError: AnotherError = AnotherError.error1
//Closure definition
var onError: APIError? = { inputError in
if let apiError = inputError as? APICallError {
print("APICallError")
}
if let anotherError = inputError as? AnotherError {
print("AnotherError")
}
}
//Above defined closure is called here below...
onError?(apiCallError)
onError?(anotherError)
Console Output (Works as expected):
APICallError
AnotherError
You need to use the enum rawValue constructor.
if let callError = APICallError(rawValue: error) {
if callError == .forbidden {
...
}
} else {
// was not an APICallError value
}
Can someone please help me with this.
I have the following public enum
public enum OfferViewRow {
case Candidates
case Expiration
case Description
case Timing
case Money
case Payment
}
And the following mutableProperty:
private let rows = MutableProperty<[OfferViewRow]>([OfferViewRow]())
In my init file I use some reactiveCocoa to set my MutableProperty:
rows <~ application.producer
.map { response in
if response?.application.status == .Applied {
return [.Candidates, .Description, .Timing, .Money, .Payment]
} else {
return [.Candidates, .Expiration, .Description, .Timing, .Money, .Payment]
}
}
But now the problem, when I try to get the value of my enum inside my rows it throws errors. Please look at the code below.
func cellViewModelForRowAtIndexPath(indexPath: NSIndexPath) -> ViewModel {
guard
let row = rows.value[indexPath.row],
let response = self.application.value
else {
fatalError("")
}
switch row {
case .Candidates:
// Do something
case .Expiration:
// Do something
case .Description:
// Do something
case .Timing:
// Do something
case .Money:
// Do something
case .Payment:
// Do something
}
}
It throws an error: Enum case 'some' not found in type 'OfferViewRow on the line let row = rows.value[indexPath.row]
And on every switch statements it throws: Enum case 'Candidates' not found in type '<<Error type>>
Can someone help me with this?
The guard statement wants an optional, as hinted by "Enum case 'some'" in the error message.
But rows.value[indexPath.row] is not Optional<OfferViewRow>, it is a raw OfferViewRow. So it won't enter a guard statement.
Move let row = rows.value[indexPath.row] one line up: Swift takes care of bounds checking, and will crash if indexPath.row is out of bounds.