Firebase query hex codes contained within randomly generated keys - ios

I would like to query a particular child from the array of color hex codes.
Here's a snapshot of my database structure:
How do I query a particular hex code and obtain the entire array of its parent object?

You cannot query whether a specific value exists in a list. This is one of the many reasons why the Firebase documentation recommends against using arrays in the database.
But in this case (and most cases that I encounter), you may likely don't really need an array. Say that you just care about what colors your user picked. In that case, you can more efficiently store the colors as a set:
palettes
-KSmJZ....A5I
"0x474A39": true
"0xbA9A7C": true
"0xDEDEDF": true
"0x141414": true
"0x323E35": true

I did it in a different way,
made a function that does this:
let databaseRef = FIRDatabase.database().reference()
let HEX1 = hex1.text! as String
let HEX2 = hex2.text! as String
let HEX3 = hex3.text! as String
let HEX4 = hex4.text! as String
let HEX5 = hex5.text! as String
let URL = url.text! as String
// First set
let colorArray1 = [HEX2, HEX3, HEX4, HEX5, URL]
databaseRef.child("palette").child(HEX1).setValue(colorArray1)
// second set
let colorArray2 = [HEX1, HEX3, HEX4, HEX5, URL]
databaseRef.child("palette").child(HEX2).setValue(colorArray2)
// third set
let colorArray3 = [HEX1, HEX2, HEX4, HEX5, URL]
databaseRef.child("palette").child(HEX3).setValue(colorArray3)
// fourth set
let colorArray4 = [HEX1, HEX2, HEX3, HEX5, URL]
databaseRef.child("palette").child(HEX4).setValue(colorArray4)
// fifth set
let colorArray5 = [HEX1, HEX2, HEX3, HEX4, URL]
databaseRef.child("palette").child(HEX5).setValue(colorArray5)
so that when I target any of the 5 hexes, it will bring me back the whole array together with it.

Related

Using A Variable Inside Firestore Call - Swift

I would like to use a variable inside a Firestore reference. I have a sub-collections stored on the database per shop and they all have the format 'menu Shop1' or 'menu Shop2'. I have to store it this way otherwise if I use menu alone, the collectionGroup reference points to all the menus and returns them all at once - which is not what I want.
I'm struggling to pass the name of the shop to the collectionGroup reference.
This does not work:
let shopName = String("Shop1")
let collectionRef = String("menu \(shopName!)")
let ref = db.collectionGroup((collectionRef!))
But then this works:
let ref = db.collectionGroup("menu Shop1")
I have tried all the variations I know and it still wont pass the string. Does anybody know how to fix this? I'm guessing its a small tweek!
I would just concatenate the string like so:
let shopName: String = "Shop1"
let refString: String = "menu " + shopName
let ref = db.collectionGroup(refString)
Don't forget the 'space' after "menu"
You could also simplify further like so:
let shopName: String = "Shop1"
let ref = db.collectionGroup("menu " + shopName)
The argument does not need to be hard coded...
There's no need for specifying 'String'
let shopName = String("Shop1")
Just make it
let shopName = "Shop1"
Then this is not correct if the intention is to actually create a ref to a collection
let collectionRef = String("menu \(shopName!)")
it should be
let shopsCollection = db.collection("shops")
or like this
let shopName = "shop1"
let thisShop = "menu " + shopName
let shopsCollectionGroup = db.collectionGroup(thisShop)
But... I am not sure you're using the collectionGroups correctly to start with based on the names you're using.
A collection group consists of all collections with the same ID, so for example you could have a collection group of 'shops' whereas yours is called 'menu Shop1' which would indicate a single shop. Or from the guide a collectionGroup called 'landmarks' which would include landmarks from multiple cities.
Read though the Collection Group Queries guide again to ensure it's being used correctly.
As a side note, please protect your code by handling optionals properly.
shopName!
is bad news is shopName is nil as it will crash you code. See nil-coelescing operators, guard and if statements.

swifty Json parse data show me nil?

fail parsing data
let tag:string = "\(sender.tag)"
let name:string = (self.json?["data"][tag]["name"].stringValue)!
success parse data
let tag:Int = sender.tag
let name:String = (self.json?["data"]["\(tag)"]["name"].stringValue)!
what different about tag?
Sender tags only take Int values and they also give Int values. You simply just can't take tag as String and set it as String.
You can even test it without your parsig. Simply test it with buttons.

`CountedSet` initialization issue

I'm comparing the characters contained within two words. In seeking to accomplish this, Set (aka NSSet) seemed like the way to go to accomplish this task. I've discovered it returns false positives on matches, so I am attempting to use CountedSet (aka NSCountedSet) instead.
I'm able to initialize a Set without issue, but I can't get the CountedSet initializer to work. Here's what I've done...
I start with a String:
// Let's say myTextField.text = "test"
let textFieldCharacters = myTextField.text?.characters
// word is a string from the ENABLE list of words
let wordCharacters = word.characters
Then I dump the characters into an Array:
var wordCharactersArray = [Character]()
for character in wordCharacters {
wordCharacterArray.append(character)
}
var textFieldCharactersArray = [Character]()
for character in textFieldCharacters {
wordCharacterArray.append(character)
}
Then I create a Set from the character arrays:
let textFieldSet = Set<Character>(textFieldCharactersArray)
let wordSet = Set<Character>(wordCharactersArray)
Finally, I test to see if the textFieldSet is a superSet of wordSet with the following:
textFieldSet.isSuperset(of: wordSet)
Going back to my example, if myTextField.text is "test", I'm returning values for word whose characters are a superset of the wordSet, but the counts of the individual elements don't match the character counts of myTextField.text
In researching my issue, I've found CountedSet (fka NSCountedSet), which I think would resolve my issue. It has two method signatures:
public convenience init(array: [AnyObject])
public convenience init(set: Set<NSObject>)
I've tried initializing the 2 sets of characters like so:
let textFieldSet = CountedSet(array: textFieldCharacterArray)
let wordSet = CountedSet(array: wordCharacterArray)
I get the following error for the sets
Cannot convert value of type '[Character]' to expected argument type
'[AnyObject]'.
So I tried initializing the set like this:
let textFieldSet = CountedSet(array: textFieldCharacterArray as! [AnyObject])
Which yields the following error:
'AnyObject' is not a subtype of 'Character'
I've also tried to initialize the CountedSet with a Set, per the method signature, but I get errors when I try to do that, too.
Any suggestions how to initialize a CountedSet would be greatly appreciated.
You are correct that if you need to compare not just the presents of elements but also their count, you should use CountedSet, which is a renaming of NSCountedSet for swift 3.0. The problem you are running into is CountedSet can only accept elements that are objects and Characters are not. As Eric D points out in their comment, the easies way to get around this is by mapping your [Character] to [String] which will bridge to [NSString].
You are not running into this problem using Set, because it is a native Swift collection type that initialize with elements of any type. This is why you can initialize a Set with [Character].
To see the difference:
let word = "helo"
let wordCharacters = Array(word.characters)
let wordSet = Set(wordCharacters)
let wordCharStrings = wordCharacters.map{String($0)}
let wordCountedSet = CountedSet(array: wordCharStrings)
let textField = "hello"
let textFieldCharacters = Array(textField.characters)
let textSet = Set(textFieldCharacters)
let textFieldCharStrings = textFieldCharacters.map{String($0)}
let textFieldCountedSet = CountedSet(array: textFieldCharStrings)
textFieldCountedSet.isSubset(of: wordCountedSet as! Set<NSObject>) // returns false, but if word had two or more l's it would return true
textSet.isSubset(of: wordSet) // returns true

I'm having trouble fetching entities from core data based on relationships with predicates

Quick background: I have en ExamEntity, which is linked via a relationship to sections covered in the exam (SectionEntity), which is linked via a relationship to the subsections under each section (SubsectionEntity).
What I'm try to do is fetch the sections and related subsections which will be covered be a selected Exam, and store them in a dictionary which can hold multiple string values per key.
My code starts like this:
var sectionsDictionary = [String:[String]]()
//---Fetch Section Entity
let sectionPredicate = NSPredicate(format: "(exam = %#)", selectedExam)
let sectionsFetchRequest:NSFetchRequest = SectionEntity.MR_requestAllWithPredicate(sectionPredicate)
let fetchedSections:NSArray = SectionEntity.MR_executeFetchRequest(sectionsFetchRequest)
All good here. But then I try and fetch the subsections related to the selected sections and things go wrong.
following Code:
for section in fetchedSections {
let sectionName = section.valueForKey("name") as! String
// //error occurs on the line below
let subsectionPredicate = NSPredicate(format: "(section = %#)", section as! SectionEntity)
let subsectionsFetchRequest:NSFetchRequest = SubsectionEntity.MR_requestAllWithPredicate(subsectionPredicate)
let fetchedSubsections:NSArray = SubsectionEntity.MR_executeFetchRequest(subsectionsFetchRequest)
The error I get is:
Could not cast value of type 'NSManagedObject_SectionEntity_' (0x7fb363e18580) to 'MyApp.SectionEntity' (0x10c9213e0).
The next few lines are supposed to iterate through the fetched results, adding their names as values to the dictionary with section names as keys.
for subsection in fetchedSubsections{
let subsectionName = subsection.valueForKey("name") as! String
sectionsDictionary[sectionName] = [subsectionName]
}
}
thanks in advance.
You're not defining the type of the class for your record
Go to the Project.xcdatamodeld file, and select your SectionEntity
Check if the field Class is like this
If it is then write in the Class field "SectionEntity" and press Enter, it should be like this
Then try again
You need to make sure that the elements of the collection you enumerate are of the correct type.
Presumably, Magical Record will return an NSArray of NSManagedObject items. You need to make sure Swift knows what type section is.
for object in fetchedSections {
let section = object as! SectionEntity
// ...
}
Also, see my answer about the unsuccessful casts to NSManagedObject subclasses.

insert a String inside another String

How can I make this:
var originalString = "http://name.domain.com/image.jpg"
becomes this:
originalString = "http://name.domain.com/image_new.jpg"
I could not find any document about the new Range<String.Index> in Swift.
This is not a problem in Obj-C, but without any reference about Range, it suddenly becomes so confusing.
Thanks.
Edit:
Well, thanks for these solutions. However, let me give you more details about this question.
After uploading an image to server, it responds back with a String link, like above, and the image name is a random string.
The server also generates different versions of uploaded image (like Flickr). In order to get these images, I have to append a suffix into image name, it looks like this:
originalString = "http://image.domain.com/randomName_large.jpg" or "http://image.domain.com/randomName_medium.jpg"
So that's why I need to insert a String into another String. My solution is find the first . by scan the link backwardly and append a suffix before it, but the new Range<String.Index> makes it confusing.
There are some nice and useful methods on NSString that you should be able to use:
let originalString: NSString = "http://name.domain.com/image.jpg"
let extension = originalString.pathExtension // "jpg"
let withoutExt = originalString.stringByDeletingPathExtension() // "http://name.domain.com/image"
let imageName = withoutExt.lastPathComponent // "image"
let withoutFilename = withoutExt.stringByDeletingLastPathComponent() // "http://name.domain.com/"
let newString = withoutFilename
.stringByAppendingPathComponent("\(imageName)_new")
.stringByAppendingPathExtension(extension)
I only typed this into the browser (it's untested) but it should give you an idea...
This can be done with String manipulation functions. But what if the string
is
var originalString = "http://images.domain.com/image.jpg"
? You probably do not want to replace the first or all occurrences of the string
"image" here.
A better tool for this purpose might be NSURLComponents, which lets you
modify all components of a URL separately:
var originalString = "http://name.domain.com/image.jpg"
let urlComps = NSURLComponents(string: originalString)!
urlComps.path = "/image_new.jpg"
originalString = urlComps.URL!.absoluteString!
println(originalString) // http://name.domain.com/image_new.jpg
Why not using string interpolation?
var imageName = "image_new"
originalString = "http://images.domain.com/\(imageName).jpg"

Resources