Permanenly saving a Int in SpriteKit - sprite-kit

I'm trying to create a game and need to be able to save a number and retrieve it. I've found another tutorial on this but it is outdated and kept giving errors:
//To save highest score
var highestScore:Int = 20
NSUserDefaults.standardUserDefaults().setObject(highestScore,
forKey:"HighestScore")
NSUserDefaults.standardUserDefaults().synchronize()
//To get the saved score
var savedScore: Int =
NSUserDefaults.standardUserDefaults().objectForKey("HighestScore") as Int
println(savedScore)
More Details: It's to save a highscore if that helps (which I don't think it does).
Any help is welcome, Thanks.

NSUserDefaults.standardUserDefaults().setInteger(20, forKey: "HighestScore")
let valueOrZeroIfNotSet = NSUserDefaults.standardUserDefaults().integerForKey("HighestScore")

NSUserdefaults are a really useful tool, you can permenantly create mutable variables that can be read and written anywhere and anytime. Heres a brief explanation of how to use them.
let saves = NSUserdefaults.standardUserDefaults()
You need to declare a NSUserdefaults to begin, now this is where you will be storing data. Imagine it as a Bookshelf, right now its empty but you can fill it with all forms of information.
Now that we have our 'Bookshelf', we can start filling it with Books, these books can be everything such as Integers, and Strings.
If we want to store my name (Peter) in a 'book' on the shelf, we would first have to create the book and give it a name. The name of the book is called the "key". The key has to be a unique String. Because Im storing my name the key will be "myName". Now we know our key to write to, now lets fill the book with information (Which is my name).
saves.setString("Peter", forKey: "myName")
Now we've made a book in our bookshelf that stores our name.
We will probably want to read this information just in case we need it! We can access books from our bookshelf easily with:
saves.stringForKey("myName")
Now we did need the key (the books name) so we could find the right book of information.
Thats pretty much it for NSUserdefaults for you.

Related

Delete specific value from firebase database using swift

Firebase Database
I tried using this bit of code but it doesn't seem to work. I take the name the user selects and store it in nameList.
Lets say I store Blake Wodruff in nameList[0].
How do I remove only that name?
var nameList = [String](repeating: "", count:100)
func remove() {
print(nameList[countAddNames])
let usernameRef = Database.database().reference().child("Candidate 1").child("alton").child(nameList[countAddNames]);
usernameRef.removeValue();
}
To write to or delete a node, you must specify its entire path. So to delete node 0 from your JSON, you'd do:
let usernameRef = Database.database().reference().child("Candidate 1").child("alton").child("0");
usernameRef.removeValue();
Or a bit shorter:
let usernameRef = Database.database().reference().child("Candidate 1/alton/0");
usernameRef.removeValue();
If you only know the name of the user you want to remove, you'll need to first look up its index/full path before you can remove it. If you have the data in your application already, you can do it in that code. Otherwise you may have to use a database query (specifically .queryOrderedByValue and .queryEqualToValue) to determine where the value exists in the database.
Also see: Delete a specific child node in Firebase swift
Once you remove a value from your JSON structure, Firebase may no longer recognize it as an array. For this reason it is highly recommended to not use arrays for the structure that you have. In fact, I'd model your data as a set, which in JSON would look like:
"alton": {
"Jake Jugg": true,
"Blake Wodruff": true,
"Alissa Sanchez": true
}
This would automatically:
Prevent duplicates, as each name can by definition only appear once.
Make removing a candidate by their name as easy as Database.database().reference().child("Candidate 1/alton/Jake Jugg").removeValue()
For more on this, also see my answer to Firebase query if child of child contains a value

How to store data, from Firebase, with multiple values for each key?

My structure on Firebase is like this:
picture of structure-layout
(Seems like I'm not allowed to embed pictures yet, so they made it a link instead)
As I'm new to Swift, I'm a bit confused here.
Been walking in circles for this, apparently, easy task.
I'd like to store the data on the users device.
This way, if new data is updated to Firebase, the device will retrieve the new data at logon, not having the user to update the app through App Store.
I've concluded an array of Dictionaries would be the way to go, but how?
My problem is the values..
They seem to be downloaded together, in one SNAP, and not in parts. (Like so: "SNAP1: yada yada", SNAP2: ... " and so on)
I need to be able to access, at any time, only one value belonging to whatever key i'd like.
Here's my code (I know it's not complete yet, but I'm stuck):
func updateData() {
var dictArray: [[String:String]] = []
// Setting the refference and getting the values within a snapshot..
ref.child(REF_CATEGORIES).observeSingleEvent(of: .value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
let categories = snap.key
let questions = snap.children.allObjects
dictArray.append([categories : String(describing: questions)])
}
}
})
}
Somehow I need to iterate through the values too, but then: How do I merge them (index) with the correct category?
I'm feeling it's right under my nose.. To bad my nose is too big, for me to see :)
Thanks in advance!
As the Firebase Database is key-value based, you can achieve roughly what you want by giving each value a key which is simply a number. For example:
"Key1": {
"0":"value1",
"1":"value2",
"2":"value3"
}
You can then easily parse this into an array when you retrieve the data.
Fun fact: if you 'number' (they're not actually numbers, they're strings) your keys in this fashion, the Firebase Database will actually store this as an array under-the-hood, they just always display it to us in key-value form.

Detect current input language

I am building an app which allows users to check the local time in any place around the world. The information is being fetched using Google APIs and all works well, but for the best results I need to specify the language which I would like to use.
Obviously, in my case, it makes sense to set the desired language to the one which user used when typing a city name. On iPhone this is super easy to do, but I cannot figure out how I could get this language code on macOS. Could anyone help me please?
Update
Following an advice from Leo I am now at the stage when I am getting the current input with the help of carbon framework.
let source = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
let lang = TISGetInputSourceProperty(source, kTISPropertyInputSourceLanguages).assumingMemoryBound(to: NSArray.self).pointee
The problem is that TISGetInputSourceProperty() returns an UnsafeMutableRawPointer. And I cannot find a way to convert it to something readable. I assume it is an NSArray, but I have no luck trying to convert it. Can someone help please?
let source = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
let value = TISGetInputSourceProperty(source, kTISPropertyInputSourceLanguages)
if (value != nil) {
var name = Unmanaged<AnyObject>.fromOpaque(value!).takeUnretainedValue() as? String
}
I learn it form here https://github.com/noraesae/kawa/blob/master/kawa/InputSourceManager.swift
Carbon has TISCopyCurrentKeyboardInputSource() which gives you the current language.
TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
TISGetInputSourceProperty(source, kTISPropertyInputSourceLanguages));
There is also the kTISNotifySelectedKeyboardInputSourceChanged distributed notification to determine when language changes.
For Swift ease of use, consider making an Objective C wrapper which returns a string value of the above API.

Store quotes in parse

So I am really new to parse and I have no idea how to store a famous quote and it's own author, so then I can use that information in my iOS app made with swift.
I have imported all the frameworks in order to make parse work but I don't know to to store that quotes with the authors and then retrieve the information to display the quote in a label and the author in another label. Please don't be rude, I don't get who to make this work.
So if you want to save a text and retrieve to Parse
let's say you want to save some text into a class called Data
Save
var data = "Swift is nice"
var object = PFObject(className:"Data")
object["message"] = data
object.saveInBackground()
So I use the saveInBackground method just for simplicity however if you should other saveInBackground method where you could check if there is no error while you are saving into Parse.
Retrieve
#IBOutlet weak var textlabel:UILabel!
var query = PFQuery(className:"Data")
query.getFirstObjectInBackgroundWithBlock({ (objects:PFObject?, error:NSError?) -> Void in
if error == nil {
let retrieveData = objects?.objectForKey("message") as! String
//so lets say you had a UILAbel
self.textlabel.text = retrieveData
}
})
I used the getFirstObjectInBackgroundWithBlock method because I assume that I have at least one object save in Parse. So if you are retrieve a lot of data you could findObjects method .
Hope that helps :)
To answer your comment on Lamars's fine answer:
Create a class ("Add class") "Quotes" in Parse, if you haven't already. Create a column in that class named "Quote", set it's type to String. Click "Add Row" and you will get a new row in your table. Double Click it's field and you can write your quote there.
This is me editing the usernameField in my Parse Class:
You could have another column named "Author" of type String, and just do the same thing, but if your app gets more advanced and you would like to display all the quotes from a specific author, you should add another class, named "Author". Add a column named "Name", double click and submit your name.
In your "Quotes" class, add a column named "Author", type Pointer, and make it point to your Author Class. Then copy the correct objectId from Pointer (let's sat Steve Jobs has objectId "12345678") and paste it to the "Author" column in Quotes. Now, if there's another quote by Steve Jobs, you can re-use that objectId, not having to store the name "Steve Jobs" more than once.
I understand you're new to Parse.com and maybe databases as well, but this way of creating relations is very good knowledge, if you want to design stuff in the future.
Parse has a great documentation, in Obj-C and Swift, check it out:
https://www.parse.com/docs/ios/guide

Autocomplete with Firebase

How does one use Firebase to do basic auto-completion/text preview?
For example, imagine a blog backed by Firebase where the blogger can tag posts with tags. As the blogger is tagging a new post, it would be helpful if they could see all currently-existing tags that matched the first few keystrokes they've entered. So if "blog," "black," "blazing saddles," and "bulldogs" were tags, if the user types "bl" they get the first three but not "bulldogs."
My initial thought was that we could set the tag with the priority of the tag, and use startAt, such that our query would look something like:
fb.child('tags').startAt('bl').limitToFirst(5).once('value', function(snap) {
console.log(snap.val())
});
But this would also return "bulldog" as one of the results (not the end of the world, but not the best either). Using startAt('bl').endAt('bl') returns no results. Is there another way to accomplish this?
(I know that one option is that this is something we could use a search server, like ElasticSearch, for -- see https://www.firebase.com/blog/2014-01-02-queries-part-two.html -- but I'd love to keep as much in Firebase as possible.)
Edit
As Kato suggested, here's a concrete example. We have 20,000 users, with their names stored as such:
/users/$userId/name
Oftentimes, users will be looking up another user by name. As a user is looking up their buddy, we'd like a drop-down to populate a list of users whose names start with the letters that the searcher has inputted. So if I typed in "Ja" I would expect to see "Jake Heller," "jake gyllenhaal," "Jack Donaghy," etc. in the drop-down.
I know this is an old topic, but it's still relevant. Based on Neil's answer above, you more easily search doing the following:
fb.child('tags').startAt(queryString).endAt(queryString + '\uf8ff').limit(5)
See Firebase Retrieving Data.
The \uf8ff character used in the query above is a very high code point
in the Unicode range. Because it is after most regular characters in
Unicode, the query matches all values that start with queryString.
As inspired by Kato's comments -- one way to approach this problem is to set the priority to the field you want to search on for your autocomplete and use startAt(), limit(), and client-side filtering to return only the results that you want. You'll want to make sure that the priority and the search term is lower-cased, since Firebase is case-sensitive.
This is a crude example to demonstrate this using the Users example I laid out in the question:
For a search for "ja", assuming all users have their priority set to the lowercased version of the user's name:
fb.child('users').
startAt('ja'). // The user-inputted search
limitToFirst(20).
once('value', function(snap) {
for(key in snap.val()){
if(snap.val()[key].indexOf('ja') === 0) {
console.log(snap.val()[key];
}
}
});
This should only return the names that actually begin with "ja" (even if Firebase actually returns names alphabetically after "ja").
I choose to use limitToFirst(20) to keep the response size small and because, realistically, you'll never need more than 20 for the autocomplete drop-down. There are probably better ways to do the filtering, but this should at least demonstrate the concept.
Hope this helps someone! And it's quite possible the Firebase guys have a better answer.
(Note that this is very limited -- if someone searches for the last name, it won't return what they're looking for. Hence the "best" answer is probably to use a search backend with something like Kato's Flashlight.)
It strikes me that there's a much simpler and more elegant way of achieving this than client side filtering or hacking Elastic.
By converting the search key into its' Unicode value and storing that as the priority, you can search by startAt() and endAt() by incrementing the value by one.
var start = "ABA";
var pad = "AAAAAAAAAA";
start += pad.substring(0, pad.length - start.length);
var blob = new Blob([start]);
var reader = new FileReader();
reader.onload = function(e) {
var typedArray = new Uint8Array(e.target.result);
var array = Array.prototype.slice.call(typedArray);
var priority = parseInt(array.join(""));
console.log("Priority of", start, "is:", priority);
}
reader.readAsArrayBuffer(blob);
You can then limit your search priority to the key "ABB" by incrementing the last charCode by one and doing the same conversion:
var limit = String.fromCharCode(start.charCodeAt(start.length -1) +1);
limit = start.substring(0, start.length -1) +limit;
"ABA..." to "ABB..." ends up with priorities of:
Start: 65666565656565650000
End: 65666665656565650000
Simples!
Based on Jake and Matt's answer, updated version for sdk 3.1. '.limit' no longer works:
firebaseDb.ref('users')
.orderByChild('name')
.startAt(query)
.endAt(`${query}\uf8ff`)
.limitToFirst(5)
.on('child_added', (child) => {
console.log(
{
id: child.key,
name: child.val().name
}
)
})