SwiftUI Conditional List - Unable to infer complex closure return type - swift

I know questions involving this error have been asked before, but I have looked through them and none of them have helped me solve this particular issue.
I have a List, which depending on a certain condition, will have some items filtered out. Here it is:
List(tasks) { task in
if (!self.toggleIsOn) || (!task.completed.status) {
TaskRowView(task)
}
}
Everything works fine, but when I add the conditional, it gives me this error:
Unable to infer complex closure return type; add explicit type to disambiguate
On the List line. How can I fix this?

List expects a View returned for every item, using Group like that is a bit of a hack.
Better to move the logic out of the List, either
var filteredTasks: [Task] {
return tasks.filter { !self.toggleIsOn || !$0.completed.status }
}
...
List(filteredTasks) { task in
TaskRowView(task)
}
or
List(tasks.filter( { !self.toggleIsOn || !$0.completed.status })) { task in
TaskRowView(task)
}

I managed to fix this problem by surrounding the returned value in a Group - this meant that the return type was always Group, even if the Group didn't contain anything.
List(tasks) { task in
Group {
if (!self.toggleIsOn) || (!task.completed.status) {
TaskRowView(task)
}
}
}

Related

How to write to an Element in a Set?

With arrays you can use a subscript to access Array Elements directly. You can read or write to them. With Sets I am not sure of a way to write its Elements.
For example, if I access a set element matching a condition I'm only able to read the element. It is passed by copy and I can't therefore write to the original.
For example:
columns.first(
where: {
$0.header.last == Character(String(i))
}
)?.cells.append(value: addValue)
// ERROR: Cannot use mutating member on immutable value: function call returns immutable value
You can't just change things inside a set, because of how a (hash) set works. Changing them would possibly change their hash value, making the set into an invalid state.
Therefore, you would have to take the thing you want to change out of the set, change it, then put it back.
if var thing = columns.first(
where: {
$0.header.last == Character(String(i))
}) {
columns.remove(thing)
thing.cells.append(value: addValue)
columns.insert(thing)
}
If the == operator on Column doesn't care about cells (i.e. adding cells to a column doesn't suddenly make two originally equal columns unequal and vice versa), then you could use update instead:
if var thing = columns.first(
where: {
$0.header.last == Character(String(i))
}) {
thing.cells.append(value: addValue)
columns.update(thing)
}
As you can see, it's quite a lot of work, so maybe sets aren't a suitable data structure to use in this situation. Have you considered using an array instead? :)
private var _columns: [Column]
public var columns : [Column] {
get { _columns }
set { _columns = Array(Set(newValue)) }
// or any other way to remove duplicate as described here: https://stackoverflow.com/questions/25738817/removing-duplicate-elements-from-an-array-in-swift
}
You are getting the error because columns might be a set of struct. So columns.first will give you an immutable value. If you were to use a class, you will get a mutable result from columns.first and your code will work as expected.
Otherwise, you will have to do as explained by #Sweeper in his answer.

onErrorResumeNext type inference failed

If my single errors because of a networkexception return Single.just(false)
If my single errors because of another reason return Single.error
If my single succeeds return the original Single value.
this should be as easy as
getStudent(studentId)
.onErrorResumeNext { if (it is NetworkException) return #onErrorResumeNext Single.just(true)
return Single.error(it) }
Type inference failed. Expected type mismatch SingleSource found Single
Your Single needs to return the same type as your source (I'm assuming getStudent() isn't returning a Boolean). If you want to represent a "success" and "error" states, Kotlin has a Result class just for this.
E.g.
getStudent()
.map { student ->
// Your logic here may look different
Result.success(student)
}
.onErrorResumeNext { error ->
if (error is NetworkException){
Single.just(Result.failure(error))
} else {
Single.error(error)
}
}
This will catch network errors and wrap the exception in a Result, all other exceptions will be propagated downstream. You can then choose how to handle the error in your subscribe method.
Depending on your use case however, you may want to also look into using Maybe or the retry() operator.

How I can filter lens inside resolveCodeLens?

I am trying to split CodeLensProvider implementation in two parts. First part is implementation of provideCodeLenses which returns array of unresolved CodeLens. And the second part is implementation of resolveCodeLens.
So I want to ignore some kind of CodeLens don't match some conditions inside resolveCodeLens because provideCodeLenses should return as fast as possible. Is it possible to do?
Right now I just got <<MISSING COMMAND>> for unresolved CodeLens.
An Example
class Provider implements CodeLensProvider {
provideCodeLenses() {
return [lensA, lensB, lensC];
}
resolveCodeLens(lens) {
return executeCommand('vscode.someCommand')
.then((result) => {
if (result.isTrue) {
return lens.resolve();
} else {
// ignore `lens`
}
});
}
}
Well. According to the answer on the VSCode repository, this is not possible to do. :(

Are there any side effects of exiting a loop with return rather than break in Swift?

I need to match items in two different arrays (one with imported items and another with local items that share some properties with the imported items) to sync two databases that are quite different. I need to use several criteria to do the matching to increase the robustness of finding the right local item and match it with the imported item. I could check each criterium in the same loop, but that is too expensive, because the criteria are checked by the likelihood of success in descending order. Thus, in my first implementation I used a boolean flag called found to flag that the checking of other criteria should be ignored.
Using pseudo code:
// calling code for the matching
for item in importedItems {
item.match() }
In the imported item class:
match()
{
var found = false
for localItem in localItems
{
if (self.property == localItem.property)
{
// update the local item here
found = true
break
}
}
// match with less likely 2nd property
if (!found)
{
for localItem in localItems
{
if (self.property2 == localItem.property2)
{
// update the local item here
found = true
break
}
}
}
The if !found {...} pattern is repeated two additional times with even less likely criteria.
After reviewing this code, it is clear that this can be optimized by returning instead of breaking when there is a match.
So, my question is "are there any known side-effects of leaving a loop early by using return instead of break in Swift?" I could not find any definitive answer here in SO or in the Swift documentation or in blogs that discuss Swift flow control.
No, there are no side effects, quite the opposite it's more efficient.
It's like Short-circuit evaluation in a boolean expression.
But your code is a bad example because found cannot be used outside the function.
This is a more practical example returning a boolean value
func match() -> Bool
{
for localItem in localItems
{
if (self.property == localItem.property)
{
// update the local item here
return true
}
}
....
return false
}
If you know for sure that you can return because nothing else have to be done after the loop then there are no side effects of using return

Zend Framework: is there a way to access the element name from within a custom validator?

I'm writing a custom validator that will validate against multiple other form element values. In my form, I call my custom validator like this:
$textFieldOne = new Zend_Form_Element_Text('textFieldOne');
$textFieldOne->setAllowEmpty(false)
->addValidator('OnlyOneHasValue', false, array(array('textFieldTwo', 'textFieldThree')));
My validator will check that only one of those three fields (textFieldOne, textFieldTwo, textFieldThree) has a value. I want to prevent a future developer from accidentally passing the same field twice.
$textFieldOne->addValidator('OnlyOneHasValue', false, array(array('textFieldOne', 'textFieldTwo', 'textFieldThree')));
So far, my validator works perfectly, except when I pass the same field name as the field that has the valiator set on it.
In my validator, you can see that I am checking that the value (of the element with the validator set on it). I'm also checking the values of the other fields that were passed to the validator.
public function isValid($value, $context = null) {
$this->_setValue($value);
$this->_context = $context;
if ($this->valueIsNotEmpty()) {
if ($this->numberOfFieldsWithAValue() == 0) {
return true;
}
$this->_error(self::MULTIPLE_VALUES);
return false;
}
if ($this->numberOfFieldsWithAValue() == 0) {
$this->_error(self::ALL_EMPTY);
return false;
}
if ($this->numberOfFieldsWithAValue() == 1) {
return true;
}
if ($this->numberOfFieldsWithAValue() > 1) {
$this->_error(self::MULTIPLE_VALUES);
return false;
}
}
private function valueIsNotEmpty() {
return Zend_Validate::is($this->_value, 'NotEmpty');
}
private function numberOfFieldsWithAValue() {
$fieldsWithValue = 0;
foreach ($this->_fieldsToMatch as $fieldName) {
if (isset($this->_context[$fieldName]) && Zend_Validate::is($this->_context[$fieldName], 'NotEmpty')) {
$fieldsWithValue++;
}
}
return $fieldsWithValue;
}
My solution is to either...
A. Let the developer figure out there is a certain way to do it.
B. Ignore $value, forcing you to pass all the elements (which isn't much different than the first option).
or C. (if possible) Find the name of the element that called my validator in the first place and ignore it from the list of $fieldsWithValue.
I don't think there is a way to apply a validator on a form without attaching it to an element, but that would be even better, if it were an option.
How can I solve this problem?
Normaly i'd advise against such things, but, in this case I believe a static member in your class would actually provide a good solution to this problem.
With a static member, you can set it to the value in the first time the isValid is called, and check against it in subsequent calls, thus giving you a mechanism for this.
You may want to set this up to use some array in the configuration options, so that you can namespace and allow multiple instances of the validator to exist happily alongside each other for different sets.
The only problem that you really have to decide how to overcome, is where you wish to display the error, as yes the form itself does not take validators. if you want all the duplicates after the first to display an error, it is not so much of a problem.