In my code I need to retrieve a saved array of data to populate student history data. I am using the following line of code - which works great.
returnedArray = UserDefaults.standard().object(forKey: "studentHistoryArray")! as! NSArray as! [[String]]
The problem I have is on the initial (first) run of the program. The array hasn't been created/saved yet so I need to skip this step, but only on the initial run of the program. Is there a way to run this line of code only on the first run of a program?
var defaults = UserDefaults.standard()
let studentHistoryArrayKey = "studentHistoryArray"
var returnedArray = defaults.object(forKey: studentHistoryArrayKey) as? [[String]]
// I don't think that you need to use the intermediary cast to NSArray, but
// I could be wrong.
if returnedArray == nil {
returnedArray = [[String]]()
defaults.setObject(returnedArray, forKey: studentHistoryArrayKey)
}
// Now it's guaranteed to be non-nil, so do whatever you need to do with the array.
As a rule of thumb, if you're using ! as liberally as in your example, something's going to break.
Related
This is my first question and I'm still learning Swift/Xcode/Firebase, so I appreciate your patience. I've been stalking StackOverflow and have found a lot of answers to help with various things, but nothing that makes sense for the problem I've been struggling with for 2 days.
I am writing a program that will save a date picked on a previous viewcontroller and a set of user-entered floats from text fields to a Firebase database, and append each data set as a separate entry instead of overwriting the previous data. Using the first block of code below, I've got this problem solved except I can't find a way to do it without using AutoID. This leaves me with a setup like this in Firebase, but with multiple categories and "optionSelected" sections in each category:
program-name
Category 1
optionSelected
L1cggMnqFqaJf1a7UOv
Date: "21-12-2017"
Variable 1 Float: "12345"
Variable 2 Float: "26.51"
L1ciVpLq1yXm5khimQC
Date: "30-12-2017"
Variable 1 Float: "23456"
Variable 2 Float: "35.88"
Code used to save:
func newWithNewVars() {
let myDatabase = Database.database().reference().child("Category 1").child(optionSelected)
let variable1 = textField1.text
let variable2 = textField2.text
let variable1Float = (textField1.text! as NSString).floatValue
let variable2Float = (textField2.text! as NSString).floatValue
let writeArray = ["Date": textPassedOverDate, "Variable 1 Float": variable1Float, "Variable 2 Float": variable2Float]
myDatabase.childByAutoId().setValue(gasArray) {
(error, reference) in
if error != nil {
print(error!)
}
else {
print("Message saved successfully!")
}
}
}
The problem comes with recalling data. Since the AutoID is unique, I can't figure out how to access the data deeper inside for calculations. Specifically, I want to be able to make a new entry, press the save data button, and have it find the most recent entry in the "optionSelected" section so it can do calculations like subtract the older variable 1 from the new variable 1 and such.
Given the above description, layout, and code used above, what code structure would allow me to find the most recent date and access the data inside the AutoID sections for a specific category and "optionSelected"?
Thank you for your help.
The issue you're having is that you're trying to dig deeper but can't as you don't have a hold of that id. You'll want to use the .childAdded in your reference observation when you want to get inside of a list in your JSON tree when you don't have a hold of that id to get inside - this will be called as many times as there are values inside of Category 1 tree:
let reference = Database.database().reference()
reference.child("Category 1").child("optionSelected").observe(.childAdded, with: { (snapshot) in
let uniqueKey = snapshot.key // IF YOU WANT ACCESS TO THAT UNIQUE ID
print(uniqueKey)
guard let dictionary = snapshot.value as? [String: AnyObject] else { return }
let date = dictionary["date"] as? String
let variableOne = dictionary["Variable 1 Float"] as? Float
let variableOne = dictionary["Variable 2 Float"] as? Float
}, withCancel: nil)
You may also want to avoid using spaces in your database keys to avoid any problems in the near future. I'd stick with the common lowercased underscore practice e.g. "category_1" or "variable_2_float"
Ok, I am working in an iMessage app and am trying to parse more than 1 url query item from the selected message here- I have been successful getting/sending just 1 value in a query:
override func willBecomeActive(with conversation: MSConversation) {
// Called when the extension is about to move from the inactive to active state.
// This will happen when the extension is about to present UI.
if(conversation.selectedMessage?.url != nil) //trying to catch error
{
let components = URLComponents(string: (conversation.selectedMessage?.url?.query?.description)!)
//let val = conversation.selectedMessage?.url?.query?.description
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = queryItems.filter({$0.name == "theirScore"}).first
print("***************=> GOT IT ",param1?.value)
}
}
When I just have 1 value, just by printing conversation.selectedMessage?.url?.query?.description I get an optional with that 1 value, which is good. But with multiple I cant find a clean way to get specific values by key.
What is the correct way to parse a URLQueryItem for given keys for iMessage?
When you do conversation.selectedMessage?.url?.query?.description it simply prints out the contents of the query. If you have multiple items then it would appear something like:
item=Item1&part=Part1&story=Story1
You can parse that one manually by splitting the string on "&" and then splitting the contents of the resulting array on "=" to get the individual key value pairs in to a dictionary. Then, you can directly refer to each value by key to get the specific values, something like this:
var dic = [String:String]()
if let txt = url?.query {
let arr = txt.components(separatedBy:"&")
for item in arr {
let arr2 = item.components(separatedBy:"=")
let key = arr2[0]
let val = arr2[1]
dic[key] = val
}
}
print(dic)
The above gives you an easy way to access the values by key. However, that is a bit more verbose. The way you provided in your code, using a filter on the queryItems array, is the more compact solution :) So you already have the easier/compact solution, but if this approach makes better sense to you personally, you can always go this route ...
Also, if the issue is that you have to write the same filtering code multiple times to get a value from the queryItems array, then you can always have a helper method which takes two parameters, the queryItems array and a String parameter (the key) and returns an optional String value (the value matching the key) along the following lines:
func valueFrom(queryItems:[URLQueryItem], key:String) -> String? {
return queryItems.filter({$0.name == key}).first?.value
}
Then your above code would look like:
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = valueFrom(queryItems:queryItems, key:"item")
print("***************=> GOT IT ", param1)
}
You can use iMessageDataKit library. It makes setting and getting data really easy and straightforward like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "user_id")
message.md.set(value: "john", forKey: "username")
message.md.set(values: ["joy", "smile"], forKey: "tags")
print(message.md.integer(forKey: "user_id")!)
print(message.md.string(forKey: "username")!)
print(message.md.values(forKey: "tags")!)
(Disclaimer: I'm the author of iMessageDataKit)
It's not entirely that I can't solve this, but there are proper and improper ways and I don't want to use a flawed method. I have this snapshot from firebase, and I declare it as NSDictionary as so:
let commentDict = commentsDictionary.value as? NSDictionary
When I print commentDict it looks like this:
commentText = Fififgkffy;
postedBy = USXhV0QYkpbSzAmXgfeteXHuoqg2;
commentText = Fhzifigh;
postedBy = USXhV0QYkpbSzAmXgfeteXHuoqg2;
CommentDict will always look like this. It will always have 2 items. I want to know the proper way to grab the items and assign them to variables. One way I have used before, but just on one item is by putting this in a for loop:
if name as! String == "postedBy" {
Then I get the value for the "postedBy" dictionary item. When having 2 items though, should I make a little array for commentText and postedBY, and have 2 arrays with 2 items each? Another important point is that These must stay in their own groups. I have to keep track of which "commentText" was the first group and which was the 2nd group, same with "postedBy".
In the long-run I have to end up with commentText1, postedBy1, commentText2, postedBy2 all as separate String variables.
This is what I ended up doing but I just want to know if there is a better or more "proper" way to do it. I might have to put some safety if statements into this method to make sure the array gets filled properly and all that.
if let commentDict = commentsDictionary.value as? NSDictionary {
for(name, value) in commentDict {
commentInfoArray.append(value as! String)
}
let comment1Text = commentInfoArray[0]
let comment1User = commentInfoArray[1]
let comment2Text = commentInfoArray[2]
let comment2User = commentInfoArray[3]
}
If it's only postedBy and commentText relation that you are looking for try using this as your structure:-
USXhV0QYkpbSzAmXgfeteXHuoqg2 : comment1 : true,
comment2 : true,
//postedById : [postText]
Then at your retrieval use a dictionary to append your data to:-
db_Ref.child(userID).observeSingleEvent(of: .value, with: {(snap) in
if let commentDict = snap.value as? [String:AnyObject]{
for each in commentDict{
print(each.key) // Append your key here
}
}
})
I just wrote the following code:
var results:[[String:[Int:String]]] = [Dictionary<String, Dictionary<Int,String>>()]
results.removeAll(keepCapacity: false)
results.append(["Swift":[1:"I love you"]])
print(results)
var dict = results[0]
var key = dict[dict.startIndex].0
var updateValue = [2:"I want to marry you"]
var value = dict.updateValue(updateValue, forKey: key)
print(results)
But I found that it doesn't work as expected. The final print still outputs
[[Swift: [1: I love you]]]. and dict.updateValue did succeed!
Welcome to the world of copy semantics. When you take the first item out of your array with results[0], you aren't making a reference to it, you're taking a copy of the value instead. This means that the Dictionary that you're updating is not the one inside the array.
After you make your updates, just replace the first value in the array with your modified one.
var value = dict.updateValue(updateValue, forKey: key)
results[0] = dict
print(results) // "[[Swift: [2: I want to marry you]]]"
I've been trying for weeks now to save some values in one view controller then access them in another using the NSUserDefaults method. I am using this code:
(to save):
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Coding Explorer", forKey: "userNameKey")
the last line produces an "expected declaration" error
(to get values):
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey("userNameKey")
{
println(name)
}
The first of these lines produces a "incorrect redeclarion of 'defaults'"
second line produces "expected declaration"
third line produces "Consecutive declarions on a line must be seperated by a ;" and "variables used within its own initial value"
Please help me fix this I've tried different lots of code to use NSUserDefaults, and I've tried using the code on separate and the same view controllers.
You may try to use this way..
to save first define a string called for example "firstName":
var firstName : String = "Im Swift"
in viewDidLoad in the first ViewController :
if (NSUserDefaults.standardUserDefaults().stringForKey("firstName") == nil){
NSUserDefaults.standardUserDefaults().setObject(firstName, forKey: "firstName")
}
To retrieve value :
in the viewDidLoad of second ViewController :
if (NSUserDefaults.standardUserDefaults().stringForKey("firstName") != nil){
var x = NSUserDefaults.standardUserDefaults().objectForKey("firstName") as! String
println("\(x)");
}