Swift : affect struct by reference or "alias" to a variable - swift

I have to update a struct, but this struct needs a long expression to be found from my viewController :
let theMessage:Message? = self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) })?
.msgs.first(where: { $0 == msg })
I want to mutate one property of this struct, but not a copy of it, I want to update it "where it is", that is in messageSections[].msgs[]
The problem here is that if I try this code after the one above :
theMessage.status = .NOT_SENT
Only the local copy will be updated, and not the real one in messageSections[].msgs[]. So when I reload my collectionView, nothing changes.
I could do
self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) })?
.msgs.first(where: { $0 == msg }).status = .NOT_SENT
But if I have to manipulate this property several times in my function, I dont want to re-write the whole expression each time. So what I'm looking for is something like in C :
let theMessage:Message?* = &self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) })?
.msgs.first(where: { $0 == msg })
if(theMessage != NULL)
theMessage->status = .NOT_SENT
I saw in other questions that I could use & before argument and inout before parameter name, but its only for functions calling. I'm looking for a way to create an alias for a big expression when affecting a variable, just to avoid re-writting it each time.

You can use firstIndex to get the index of the element in the array, then access the element directly using the retrieved index.
if let messageSectionIndex = self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) }), let messageIndex = self.messageSections[messageSectionIndex].msgs.firstIndex(where: { $0 == msg }) {
self.messageSections[messageSectionIndex].msgs[messageIndex].status = .NOT_SENT
}

Related

Coredata/swift : hasChanges not as expected

This function is not acting as expected. I'm trying to nil a set of fields.The earlier section gets the correct field names, and is used in other functions. I've got about ten tables, and they all share the same context, in case that matters.
The first unexpected thing is that "yes, changes" never runs, so I presume that the settings object is detached from its context. Or perhaps CoreData treats nil as some kind of exception to triggering the .hasChanges flag?
When it runs, the save throws no errors, and the object displays as expected, displayed with the values set to nil. But there are no changes in the db.
I can save data into these fields without problem, and confirm that in the db; this problem only happens with setting the value to nil.
static func clearSettings(_ s : Set<PaletteElementType>? = nil) {
guard var setting = activeSetting() else {return}
print(setting.id)
let cats = s ?? PaletteView.compCatButtons
let tgts = setting.getAssociatedFieldNames(tgts: cats, clearing : true, values: false)
for (key, val) in tgts {
var src = Set((val as? Dictionary<FieldNameSuffixes, String>)!.values)
if key == .catBgndButton {
src = src.union(["opacity", "opacityStart", "opacityStartDelta","opacityEnd", "opacityEndDelta", "opacityTimer"])
}
for s in src {
print(s)
setting.setNilValueForKey(s)
if Blocks.context!.hasChanges {
print("yes, changes")
}
do {
try Blocks.context!.save()
print("deleted \(setting.value(forKey: s))")
} catch { print("deadly dogs")}
}
print("val is \(setting)")
}
}
OK, working when I do it this way:
static func clearSettings(_ s : Set<PaletteElementType>? = nil) {
guard var setting = activeSetting() else {return}
print(setting.id)
let cats = s ?? PaletteView.compCatButtons
let tgts = setting.getAssociatedFieldNames(tgts: cats, clearing : true, values: false)
for (key, val) in tgts {
var src = Set((val as? Dictionary<FieldNameSuffixes, String>)!.values)
if key == .catBgndButton {
src = src.union(["opacity", "opacityStart", "opacityStartDelta","opacityEnd", "opacityEndDelta", "opacityTimer"])
}
for n in setting.entity.attributesByName.enumerated() {
if src.contains( n.element.key as String) {
print("found one")
setting.setNilValueForKey(n.element.key)
}
}
do {
try Blocks.context!.save()
} catch {print("bumpy beasts")}
print("val is \(setting)")
}
}
Happy it's working, but I don't really understand the distinction here. What is the better way to handle this? I'm not chasing some super performant code, so I don't mind a few extra loops... but what's the deal?

Setting variable equal to if statement condition

I have an if statement that checks to see if an array element matches a local variable.
if pinArray.contains(where: {$0.title == restaurantName})
How would I create a variable of this element?
I attempted
let thePin = pinArray.contains(where: {$0.title == restaurantName})
but this comes with "could not cast boolean to MKAnnotation".
I also tried variations of
let pins = [pinArray.indexPath.row]
let pinn = pins(where: pin.title == restaurantName) (or close to it)
mapp.selectAnnotation(thePin as! MKAnnotation, animated: true)
to no avail. What basic step am I missing?
contains(where:) returns a Bool indicating whether a match was found or not. It does not return the matched value.
So thePin is a Bool which you then attempt to force-cast to a MKAnnotation which of course crashes.
If you want the matching value, change your code to:
if let thePin = pinArray.first(where: { $0.title == restaurantName }) {
do {
mapp.selectionAnnotation(thePin, animated: true)
} catch {
}
} else {
// no match in the array
}
No need for contains at all. No need to cast (assuming pinArray is an array of MKAnnotation).

Dictionary in Dictionary value search

I am downloading information from a Firebase database and it is being inputted via a for loop into:
static var Reports = [String:[String:String]]()
I need to figure out a way to search the inside values for a certain string
I have messed around with this but can't seem to get it inside the inside dictionary (If that makes sense)
for values in Reports.count {
if let item = Reports["favorite drink"] {
print(item)
}
}
I need to have a search string then a number of times the value appears like so:
func findString(dict Dictionary) -> Int {
var ReportsLevel1 = 0
(for loop I'm guessing)
search here for string
return ReportsLevel1
}
Tip: the outside dictionary keys are not set in stone, they depend on what time and date the report was submitted
To find out the numberOfTimes in which "yourSearchString" appears you can do as follows
var numberOfTimes = 0
for internalDictionary in reports.values
{
for value in internalDictionary.values
{
if (value == "yourSearchString") { numberOfTimes += 1 }
}
}
or
let numberOfTimes = reports.flatMap { internalDictsArray in internalDictsArray.value.filter { $0.value == "yourSearchString" } }.count

Form validation issue in macOS app

I am trying to run through a settings form and make sure that the user hasn't left any of the required fields empty.
Some of the forms fields are secure ( eg password).
Whats the easiest way to loop through all these fields and check they are not empty?
I have tried below - but I get a weird error:
if textfield1.stringValue == "",
textfield2.stringValue == "",
passwordfield.stringValue == "" {
//Shows error: Braced block of statements is an unused closure
}
Additionally I am unable to group all these NSTextfields into an array as the password textfields are NSSecureTextField which despite being inherited from NSTextfield, the are not groupable with NSTextfield.
You can have NSTextField and NSSecureTextField in the same array. This is indeed an easy way to find the empty ones.
let tf = NSTextField()
let stf = NSSecureTextField()
let tf2 = NSTextField()
tf2.stringValue = "some text"
let all = [tf, stf, tf2]
let emptyTextFields = all.filter { $0.stringValue.isEmpty }
Also in your example you can't use commas to group conditions in if, you have to use &&:
if tf.stringValue.isEmpty && stf.stringValue.isEmpty && tf2.stringValue.isEmpty {
// do something
}
but this is not a good solution, better use the array and filter.
Under Swift 2, here's what Eric Aya correctly identified:
if textfield1.stringValue == "" && textfield2.stringValue == "" && == "" {
}
It also compiles under Swift 3.
On the other hand, the code you put in your question actually works in Swift 3.
Other way to check empty string with isEmpty variable of String object.
let userName = ""
let email = ""
if(userName.isEmpty && email.isEmpty) {
print("empty strings")
}
else {
print("good strings")
}

Check if dictionary contains value in Swift

Just simple task. I've got a dictionary var types = [Int : String]() which inits like an empty and after some user actions it fills with data. According to emptiness or some specific data in this dictionary I enable/disable a button in UI.
Check for emptiness is easy, but how to check if dictionary contains certain value?
Compiler suggested me a placeholder with predicate:
types.contains(predicate: ((Int, String)) throws -> Bool>)
Since you only want to check for existance of a given value, you can apply the contains method for the values properties of your dictionary (given native Swift dictionary), e.g.
var types: [Int : String] = [1: "foo", 2: "bar"]
print(types.values.contains("foo")) // true
As mentioned in #njuri: answer, making use of the values property of the dictionary can seemingly yield an overhead (I have not verified this myself) w.r.t. just checking the contains predicate directly against the value entry in the key-value tuple of each Dictionary element. Since Swift is fast, this shouldn't be an issue, however, unless you're working with a huge dictionary. Anyway, if you'd like to avoid using the values property, you could have a look at the alternatives given in the forementioned answer, or, use another alternative (Dictionary extension) as follows:
extension Dictionary where Value: Equatable {
func containsValue(value : Value) -> Bool {
return self.contains { $0.1 == value }
}
}
types.containsValue("foo") // true
types.containsValue("baz") // false
I wrote a function which is using contains method on dictionary.
Your specific case:
let dic : [Int : String] = [1 : "a", 2 : "b"]
func dictionary(dict : [Int : String], containsValue value : String)->Bool{
let contains = dict.contains { (_,v) -> Bool in
return v == value
}
return contains
}
let c = dictionary(dic, containsValue: "c") // false
let a = dictionary(dic, containsValue: "a") // true
Generic:
extension Dictionary{
func containsValue<T : Equatable>(value : T)->Bool{
let contains = self.contains { (k, v) -> Bool in
if let v = v as? T where v == value{
return true
}
return false
}
return contains
}
}
I've tested this function against dictionary.values.contains() and it is roughly two times faster.
If you want to check if already contains a value this would be the way:
if !yourDictionary.values.contains("Zero") {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("this value already exists");
}
And this one if you want to check if the key exists:
You get the item to add to your dictionary.
Check if the item's key already exists
If it doesn't, append the item or enable the button.
//1
let newItemKey = 0
let newItemValue = "Zero"
//2
let keyExists = yourDictionary[newItemKey] != nil
//3
if !keyExists {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("This key already exists");
}
The dictionary getter returns an optional value.
let dictionary = ["ben": "says hi"]
let containsAlpha = dictionary["alpha"] != nil
let containsBen = dictionary["ben"] != nil